Documentation
¶
Overview ¶
Package filepath provides path helpers: symlink resolution and containment checks.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Expand ¶
Expand expands a leading ~ to the user's home directory and resolves environment variables via os.ExpandEnv. It is purely lexical: the result is not checked for existence or resolved against the filesystem (use Resolve or ResolveLenient for that).
Example ¶
Expand also expands a leading ~ to the user's home directory.
package main
import (
"fmt"
"os"
xfilepath "github.com/gechr/x/filepath"
)
func main() {
_ = os.Setenv("PROJECT_ROOT", "/srv/app")
fmt.Println(xfilepath.Expand("$PROJECT_ROOT/config.toml"))
}
Output: /srv/app/config.toml
func IsWithin ¶
IsWithin reports whether all target paths are equal to or contained within `base`. Returns false when no `targets` are provided.
Example:
IsWithin("src", "src/foo.go") // true
IsWithin(".", "src/foo.go", "lib/bar.go") // true
IsWithin("src", "lib/foo.go") // false
Example ¶
package main
import (
"fmt"
xfilepath "github.com/gechr/x/filepath"
)
func main() {
fmt.Println(xfilepath.IsWithin("src", "src/foo.go"))
fmt.Println(xfilepath.IsWithin("src", "src"))
fmt.Println(xfilepath.IsWithin("src", "lib/foo.go"))
fmt.Println(xfilepath.IsWithin("src"))
}
Output: true true false false
Example (MultipleTargets) ¶
IsWithin only reports `true` when every target is contained within the base.
package main
import (
"fmt"
xfilepath "github.com/gechr/x/filepath"
)
func main() {
fmt.Println(xfilepath.IsWithin(".", "src/foo.go", "lib/bar.go"))
fmt.Println(xfilepath.IsWithin("src", "src/foo.go", "lib/bar.go"))
}
Output: true false
func Merge ¶
func Merge(paths []string, opts ...MergeOption) []string
Merge reduces `paths` to the minimal set covering the same locations: comparing them as cleaned absolute paths, it drops any that duplicate or are nested within another, so a later walk visits each file once. Survivors keep their original form and first-seen order; a path whose absolute form cannot be computed is compared by its cleaned form.
The comparison is lexical by default; pass WithResolveSymlinks to compare resolved physical locations instead.
Example:
Merge([]string{"a", "a"}) // ["a"]
Merge([]string{".", "./sub"}) // ["."]
Merge([]string{"a/b", "a"}) // ["a"]
Merge([]string{"a", "b"}) // ["a", "b"]
Example ¶
package main
import (
"fmt"
xfilepath "github.com/gechr/x/filepath"
)
func main() {
fmt.Println(xfilepath.Merge([]string{".", "./sub"}))
fmt.Println(xfilepath.Merge([]string{"a/b", "a"}))
fmt.Println(xfilepath.Merge([]string{"a", "b"}))
}
Output: [.] [a] [a b]
Example (Duplicates) ¶
Exact duplicates are merged; the first occurrence survives in its original spelling.
package main
import (
"fmt"
xfilepath "github.com/gechr/x/filepath"
)
func main() {
fmt.Println(xfilepath.Merge([]string{"a", "./a", "a/"}))
}
Output: [a]
func Resolve ¶
Resolve recursively follows every symlink along `path` and returns the fully resolved absolute path. On any error (missing component, cycle, permission) the input path is returned alongside the error so callers can choose whether to handle it or fall back.
Example ¶
Resolve follows symlinks, so a link and its target resolve to the same path.
package main
import (
"fmt"
"os"
"path/filepath"
xfilepath "github.com/gechr/x/filepath"
)
func main() {
dir, _ := os.MkdirTemp("", "example")
defer func() { _ = os.RemoveAll(dir) }()
target := filepath.Join(dir, "target.txt")
_ = os.WriteFile(target, []byte("hello"), 0o600)
link := filepath.Join(dir, "link.txt")
_ = os.Symlink(target, link)
resolvedLink, _ := xfilepath.Resolve(link)
resolvedTarget, _ := xfilepath.Resolve(target)
fmt.Println(resolvedLink == resolvedTarget)
}
Output: true
func ResolveLenient ¶
ResolveLenient returns an absolute path with symlinks resolved where possible. If `path` itself cannot be resolved, it resolves the parent directory and rejoins the original base name. If neither can be resolved, it returns the absolute path.
Example ¶
ResolveLenient succeeds where Resolve fails: a missing file resolves via its parent directory, keeping the original base name.
package main
import (
"fmt"
"os"
"path/filepath"
xfilepath "github.com/gechr/x/filepath"
)
func main() {
dir, _ := os.MkdirTemp("", "example")
defer func() { _ = os.RemoveAll(dir) }()
missing := filepath.Join(dir, "missing.txt")
_, err := xfilepath.Resolve(missing)
fmt.Println(err != nil)
resolved, _ := xfilepath.ResolveLenient(missing)
fmt.Println(filepath.Base(resolved))
}
Output: true missing.txt
Types ¶
type MergeOption ¶
type MergeOption func(*mergeConfig)
MergeOption configures Merge.
func WithResolveSymlinks ¶
func WithResolveSymlinks() MergeOption
WithResolveSymlinks makes Merge compare paths by their resolved physical location (via ResolveLenient) rather than lexically, so two spellings that reach the same target through a symlink are merged. It touches the filesystem; without it Merge is pure and lexical.