package utils import ( "os" "path/filepath" "runtime" "strings" logger "git.site.quack-lab.dev/dave/cylogger" ) // pathLogger is a scoped logger for the utils/path package. var pathLogger = logger.Default.WithPrefix("utils/path") // ResolvePath resolves a file path by: // 1. Expanding ~ to the user's home directory // 2. Making the path absolute if it's relative // 3. Normalizing path separators to forward slashes // 4. Cleaning the path func ResolvePath(path string) string { resolvePathLogger := pathLogger.WithPrefix("ResolvePath").WithField("inputPath", path) resolvePathLogger.Debug("Resolving path") if path == "" { resolvePathLogger.Warning("Empty path provided") return "" } // Step 1: Expand ~ to home directory originalPath := path if strings.HasPrefix(path, "~") { home := os.Getenv("HOME") if home == "" { // Fallback for Windows if runtime.GOOS == "windows" { home = os.Getenv("USERPROFILE") } } if home != "" { if path == "~" { path = home } else if strings.HasPrefix(path, "~/") { path = filepath.Join(home, path[2:]) } else { // Handle cases like ~username // For now, just replace ~ with home directory path = strings.Replace(path, "~", home, 1) } resolvePathLogger.Debug("Expanded tilde to home directory: home=%s, result=%s", home, path) } else { resolvePathLogger.Warning("Could not determine home directory for tilde expansion") } } // Step 2: Make path absolute if it's not already if !filepath.IsAbs(path) { cwd, err := os.Getwd() if err != nil { resolvePathLogger.Error("Failed to get current working directory: %v", err) return path // Return as-is if we can't get CWD } path = filepath.Join(cwd, path) resolvePathLogger.Debug("Made relative path absolute: cwd=%s, result=%s", cwd, path) } // Step 3: Clean the path path = filepath.Clean(path) resolvePathLogger.Debug("Cleaned path: result=%s", path) // Step 4: Normalize path separators to forward slashes for consistency path = strings.ReplaceAll(path, "\\", "/") resolvePathLogger.Debug("Final resolved path: original=%s, final=%s", originalPath, path) return path } // ResolvePathForLogging is the same as ResolvePath but includes more detailed logging // for debugging purposes func ResolvePathForLogging(path string) string { return ResolvePath(path) } // IsAbsolutePath checks if a path is absolute (including tilde expansion) func IsAbsolutePath(path string) bool { // Check for tilde expansion first if strings.HasPrefix(path, "~") { return true // Tilde paths become absolute after expansion } return filepath.IsAbs(path) } // GetRelativePath returns the relative path from base to target func GetRelativePath(base, target string) (string, error) { resolvedBase := ResolvePath(base) resolvedTarget := ResolvePath(target) relPath, err := filepath.Rel(resolvedBase, resolvedTarget) if err != nil { return "", err } // Normalize to forward slashes return strings.ReplaceAll(relPath, "\\", "/"), nil }