mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-06-26 00:11:43 +08:00
158 lines
3.3 KiB
Go
158 lines
3.3 KiB
Go
package appdata
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
const bootstrapFileName = "storage_root.json"
|
|
|
|
var (
|
|
ErrSetActiveRootCreateDataDirectory = errors.New("create data directory failed")
|
|
ErrSetActiveRootCreateBootstrapDirectory = errors.New("create bootstrap directory failed")
|
|
)
|
|
|
|
type setActiveRootError struct {
|
|
kind error
|
|
detail error
|
|
}
|
|
|
|
func (e *setActiveRootError) Error() string {
|
|
if e == nil || e.kind == nil {
|
|
return ""
|
|
}
|
|
if e.detail == nil {
|
|
return e.kind.Error()
|
|
}
|
|
return e.kind.Error() + ": " + e.detail.Error()
|
|
}
|
|
|
|
func (e *setActiveRootError) Unwrap() error {
|
|
if e == nil {
|
|
return nil
|
|
}
|
|
return e.kind
|
|
}
|
|
|
|
func newSetActiveRootError(kind error, detail error) error {
|
|
if kind == nil {
|
|
return detail
|
|
}
|
|
if detail == nil {
|
|
return kind
|
|
}
|
|
return &setActiveRootError{kind: kind, detail: detail}
|
|
}
|
|
|
|
func SetActiveRootErrorDetail(err error) error {
|
|
var target *setActiveRootError
|
|
if errors.As(err, &target) {
|
|
return target.detail
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type bootstrapConfig struct {
|
|
DataRoot string `json:"dataRoot"`
|
|
}
|
|
|
|
func DefaultRoot() string {
|
|
homeDir, err := os.UserHomeDir()
|
|
if err != nil || strings.TrimSpace(homeDir) == "" {
|
|
return "."
|
|
}
|
|
return filepath.Join(homeDir, ".gonavi")
|
|
}
|
|
|
|
func BootstrapPath() string {
|
|
return filepath.Join(DefaultRoot(), bootstrapFileName)
|
|
}
|
|
|
|
func normalizeRoot(root string) (string, error) {
|
|
trimmed := strings.TrimSpace(root)
|
|
if trimmed == "" {
|
|
trimmed = DefaultRoot()
|
|
}
|
|
abs, err := filepath.Abs(trimmed)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return abs, nil
|
|
}
|
|
|
|
func ResolveRoot(root string) (string, error) {
|
|
return normalizeRoot(root)
|
|
}
|
|
|
|
func ResolveActiveRoot() (string, error) {
|
|
defaultRoot, err := normalizeRoot(DefaultRoot())
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
data, err := os.ReadFile(BootstrapPath())
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return defaultRoot, nil
|
|
}
|
|
return "", err
|
|
}
|
|
var cfg bootstrapConfig
|
|
if err := json.Unmarshal(data, &cfg); err != nil {
|
|
return "", err
|
|
}
|
|
if strings.TrimSpace(cfg.DataRoot) == "" {
|
|
return defaultRoot, nil
|
|
}
|
|
return normalizeRoot(cfg.DataRoot)
|
|
}
|
|
|
|
func MustResolveActiveRoot() string {
|
|
root, err := ResolveActiveRoot()
|
|
if err != nil {
|
|
return DefaultRoot()
|
|
}
|
|
return root
|
|
}
|
|
|
|
func DriverRoot(activeRoot string) string {
|
|
root := strings.TrimSpace(activeRoot)
|
|
if root == "" {
|
|
root = MustResolveActiveRoot()
|
|
}
|
|
return filepath.Join(root, "drivers")
|
|
}
|
|
|
|
func SetActiveRoot(root string) (string, error) {
|
|
targetRoot, err := normalizeRoot(root)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defaultRoot, err := normalizeRoot(DefaultRoot())
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if err := os.MkdirAll(targetRoot, 0o755); err != nil {
|
|
return "", newSetActiveRootError(ErrSetActiveRootCreateDataDirectory, err)
|
|
}
|
|
if targetRoot == defaultRoot {
|
|
if err := os.Remove(BootstrapPath()); err != nil && !os.IsNotExist(err) {
|
|
return "", err
|
|
}
|
|
return defaultRoot, nil
|
|
}
|
|
if err := os.MkdirAll(defaultRoot, 0o755); err != nil {
|
|
return "", newSetActiveRootError(ErrSetActiveRootCreateBootstrapDirectory, err)
|
|
}
|
|
payload, err := json.MarshalIndent(bootstrapConfig{DataRoot: targetRoot}, "", " ")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if err := os.WriteFile(BootstrapPath(), payload, 0o644); err != nil {
|
|
return "", err
|
|
}
|
|
return targetRoot, nil
|
|
}
|