package infra
import (
"fmt"
"os"
"path/filepath"
)
// ResolveRegistryRoot returns the absolute path of the fn_registry root
// (the directory that contains registry.db), or an error if not found.
//
// Resolution order:
// 1. FN_REGISTRY_ROOT env var — if set and
/registry.db exists, return it.
// 2. Walk up from the executable's directory — up to 6 levels; first dir
// that contains registry.db wins. Covers binaries at /fn and at
// /apps//.
// 3. $HOME/fn_registry — if <$HOME>/fn_registry/registry.db exists.
// 4. Error — no method found a valid registry root.
func ResolveRegistryRoot() (string, error) {
// 1. Env var
if envDir := os.Getenv("FN_REGISTRY_ROOT"); envDir != "" {
if nonEmptyFile(filepath.Join(envDir, "registry.db")) {
return filepath.Clean(envDir), nil
}
}
// 2. Walk up from executable
if exe, err := os.Executable(); err == nil {
dir := filepath.Dir(exe)
for i := 0; i < 6; i++ {
if nonEmptyFile(filepath.Join(dir, "registry.db")) {
return dir, nil
}
parent := filepath.Dir(dir)
if parent == dir {
// Reached filesystem root
break
}
dir = parent
}
}
// 3. $HOME/fn_registry
if home, err := os.UserHomeDir(); err == nil {
candidate := filepath.Join(home, "fn_registry")
if nonEmptyFile(filepath.Join(candidate, "registry.db")) {
return candidate, nil
}
}
return "", fmt.Errorf("resolve_registry_root: no se encontro registry.db (set FN_REGISTRY_ROOT)")
}
// nonEmptyFile reports whether path is an existing, non-empty regular file.
// A zero-byte registry.db — which sql.Open silently creates when handed a
// mis-resolved path — must NOT count as a valid registry root, otherwise such
// a shadow file would hijack the walk-up and mask the real database.
func nonEmptyFile(path string) bool {
fi, err := os.Stat(path)
return err == nil && fi.Mode().IsRegular() && fi.Size() > 0
}