pathlib

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 27, 2025 License: MIT Imports: 9 Imported by: 0

README

pathlib.go

String types for path manipulation.

Benefits of using string types to represent paths

  1. you can treat paths exactly like strings
  2. String subtypes prevent common mix-ups: the go compiler will warn you about passing a string that could represent a file to a function that accepts a pathlib.Dir argument.
  3. Each string subtype can have associated methods, consolidating functionality from "path/filepath", "os", and "io/fs"

Prior art

Inspired by Python's pathlib and Go's filesystem methods scattered throughout the stdlib.

There are plenty of other ports of pathlib to go, but only one other package one other uses string types to represent paths.

Trade-offs

Using this library adds some overhead compared to using the stdlib functions directly. Rough measurements on my x86_64 machine indicated that linking pathlib adds around a kilobyte to the size of a binary that does nothing except links path/filepath.

Documentation

Overview

Types that expose filesystem/path functionality as methods.

Example
package main

import (
	"fmt"

	"github.com/skalt/pathlib.go"
)

// Syntactic sugar.
func enforce(err error) {
	if err != nil {
		panic(err)
	}
}

// More syntactic sugar.
func expect[T any](val T, err error) T {
	enforce(err)
	return val
}

func main() {
	dir := expect(pathlib.TempDir().Join("pathlib-example").AsDir().Make(0o777))
	defer func() { expect(dir.RemoveAll()) }()

	onDisk := expect(dir.Stat())
	fmt.Printf("Created %s with mode %s\n", dir, onDisk.Mode())

	for i, subPath := range []string{"a.txt", "b.txt", "c/d.txt"} {
		file := dir.Join(subPath).AsFile()
		handle := expect(file.MakeAll(0o666, 0o777))
		expect(fmt.Fprintf(handle, "%d", i))
		enforce(handle.Close())
		fmt.Printf("contents of %s: %q\n", file, string(expect(file.Read())))
	}

	fmt.Printf("contents of %s:\n", dir)
	for _, entry := range expect(dir.Read()) {
		fmt.Println("  - " + entry.Name())
	}
}
Output:

Created /tmp/pathlib-example with mode drwxr-xr-x
contents of /tmp/pathlib-example/a.txt: "0"
contents of /tmp/pathlib-example/b.txt: "1"
contents of /tmp/pathlib-example/c/d.txt: "2"
contents of /tmp/pathlib-example:
  - a.txt
  - b.txt
  - c

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Beholder

type Beholder[P Kind] interface {
	// See [os.Stat].
	Stat() (Info[P], error)
	// See [os.Lstat].
	Lstat() (Info[P], error)
	// Returns true if the path exists on-disk.
	Exists() bool
}

Behaviors for inspecting a path on-disk.

type Changer

type Changer interface {
	// see [os.Chmod].
	Chmod(fs.FileMode) error
	// see [os.Chown].
	Chown(uid, gid int) error
}

Methods that alter a filesystem object without changing its path or performing I/O.

type Dir

type Dir PathStr

A string that represents a directory. The directory may or may not exist on-disk, and the string may or may not end in an os.PathSeparator.

Example (PurePath)
d := pathlib.Dir("~/.config/git/..")

fmt.Println("On Unix")
fmt.Printf("%T(%q)\n", d, d)
method := func(name string, val any) {
	fmt.Printf("\t.%s() => %T(%#v)\n", name, val, val)
}
method("Parts", d.Parts())
method("Clean", d.Clean())
method("Parent", d.Parent())
method("BaseName", d.BaseName())
method("IsAbsolute", d.IsAbsolute())
method("IsLocal", d.IsLocal())
method("Ext", d.Ext())
// method("Abs().Unwrap", d.Abs()))
// method("ExpandUser().Unwrap", d.ExpandUser()))
Output:

On Unix
pathlib.Dir("~/.config/git/..")
	.Parts() => []string([]string{"~", ".config", "git", ".."})
	.Clean() => pathlib.Dir("~/.config")
	.Parent() => pathlib.Dir("~")
	.BaseName() => string("..")
	.IsAbsolute() => bool(false)
	.IsLocal() => bool(true)
	.Ext() => string(".")

func Cwd

func Cwd() (Dir, error)

Gets the Current Working Directory. See os.Getwd.

func TempDir

func TempDir() Dir

returns the process/os-wide temporary directory. See os.TempDir.

func UserCacheDir

func UserCacheDir() (Dir, error)

See os.UserCacheDir.

Example
stash := os.Getenv("XDG_CACHE_HOME")
defer func() {
	_ = os.Setenv("XDG_CACHE_HOME", stash)
}()
fmt.Println("On Unix:")

{ // UserCacheDir() returns $XDG_CACHE_HOME if it's set
	expected := "/example/.cache"
	if err := os.Setenv("XDG_CACHE_HOME", expected); err != nil {
		panic(err)
	}
	actual := expect(pathlib.UserCacheDir())
	fmt.Printf("$XDG_CACHE_HOME: %q\n", expected)
	fmt.Printf("UserCacheDir()): %q\n", actual)
}

{ // if $XDG_CACHE_HOME is unset, return the OS-specific default.
	if err := os.Unsetenv("XDG_CACHE_HOME"); err != nil {
		panic(err)
	}
	home := expect(pathlib.UserHomeDir())
	actual := strings.Replace(
		expect(pathlib.UserCacheDir()).String(),
		home.String(),
		"$HOME",
		1,
	)
	fmt.Printf("$XDG_CACHE_HOME: %q\n", os.Getenv("XDG_CACHE_HOME"))
	fmt.Printf("UserCacheDir()): %q\n", actual)

}
Output:

On Unix:
$XDG_CACHE_HOME: "/example/.cache"
UserCacheDir()): "/example/.cache"
$XDG_CACHE_HOME: ""
UserCacheDir()): "$HOME/.cache"

func UserConfigDir

func UserConfigDir() (Dir, error)

See os.UserConfigDir.

Example
stash := os.Getenv("XDG_CONFIG_HOME")
home := expect(pathlib.UserHomeDir())
defer func() {
	_ = os.Setenv("XDG_CONFIG_HOME", stash)
}()

checkOutput := func(expected string) {
	_ = os.Setenv("XDG_CONFIG_HOME", expected)
	xdg_config_home := os.Getenv("XDG_CONFIG_HOME")
	val, err := pathlib.UserConfigDir()
	if err == nil {
		fmt.Printf(
			"%q => %q\n",
			xdg_config_home,
			strings.Replace(val.String(), home.String(), "$HOME", 1),
		)
	} else {
		fmt.Printf("%q => Err(%q)\n", xdg_config_home, err.Error())
	}
}
fmt.Println("On Unix")
checkOutput("/foo/bar")
checkOutput("")
checkOutput("./my_config")
Output:

On Unix
"/foo/bar" => "/foo/bar"
"" => "$HOME/.config"
"./my_config" => Err("path in $XDG_CONFIG_HOME is relative")

func UserHomeDir

func UserHomeDir() (Dir, error)

See os.UserHomeDir.

Example
homeDir := expect(pathlib.UserHomeDir())
fmt.Println(os.Getenv("HOME") == string(homeDir))
Output:

true

func (Dir) Abs

func (d Dir) Abs() (Dir, error)

Returns an absolute path, or an error if the path cannot be made absolute. Note that there may be more than one absolute path for a given input path.

See path/filepath.Abs.

Abs implements Transformer.

Example
cwd := expect(pathlib.Cwd())
roundTrip := func(d pathlib.Dir) {
	abs := expect(d.Abs())
	rel := expect(abs.Rel(cwd))
	if rel.Clean() != d.Clean() {
		fmt.Printf("dir=%q\nabs=%q\nrel=%q\n\n", d, abs, rel)
	} else {
		fmt.Println("true")
	}
}
roundTrip("foo/bar")
roundTrip("./bar")
Output:

true
true

func (Dir) BaseName

func (d Dir) BaseName() string

See filepath.Base.

BaseName implements PurePath.

func (Dir) Chdir

func (d Dir) Chdir() (Dir, error)

CHange DIRectory. See os.Chdir.

func (Dir) Chmod

func (d Dir) Chmod(mode os.FileMode) error

See os.Chmod.

Chmod implements Changer.

func (Dir) Chown

func (d Dir) Chown(uid int, gid int) error

See os.Chown.

Chown implements Changer.

func (Dir) Clean

func (d Dir) Clean() Dir

Remove ".", "..", and repeated slashes from a path.

See path/filepath.Clean.

Clean implements Transformer.

func (Dir) Eq

func (d Dir) Eq(other Dir) (equivalent bool)

Returns true if the two paths represent the same path.

Eq implements Transformer.

Example
cwd := expect(pathlib.Cwd())
fmt.Println(cwd.Eq("."))
fmt.Println(pathlib.Dir("/foo").Eq("/foo"))
fmt.Println(cwd.Join("./relative").Eq("relative"))
fmt.Println(pathlib.Dir("/a/b").Eq("b"))
Output:

true
true
true
false

func (Dir) Exists

func (d Dir) Exists() bool

Returns true if the path exists on-disk after following symlinks.

See os.Stat, fs.ErrNotExist.

Exists implements Beholder.

func (Dir) ExpandUser

func (d Dir) ExpandUser() (Dir, error)

ExpandUser implements Transformer.

func (Dir) Ext

func (d Dir) Ext() string

Ext implements PurePath.

func (Dir) Glob

func (d Dir) Glob(pattern string) ([]PathStr, error)

See path/filepath.Glob.

Example
demoDir := pathlib.
	TempDir().
	Join("glob-example").
	AsDir()
demoDir = expect(demoDir.MakeAll(0o777, 0o777))

defer func() { _, _ = demoDir.RemoveAll() }()

for _, name := range []string{"x", "y", "z", "a", "b", "c"} {
	expect(demoDir.Join(name + ".txt").AsFile().Make(0o777))
}

for _, match := range expect(demoDir.Glob("*.txt")) {
	fmt.Println(match)
}
Output:

/tmp/glob-example/a.txt
/tmp/glob-example/b.txt
/tmp/glob-example/c.txt
/tmp/glob-example/x.txt
/tmp/glob-example/y.txt
/tmp/glob-example/z.txt

func (Dir) IsAbsolute

func (d Dir) IsAbsolute() bool

Returns true if the path is absolute, false otherwise. See filepath.IsAbs for more details.

IsAbsolute implements PurePath.

func (Dir) IsLocal

func (d Dir) IsLocal() bool

IsLocal implements PurePath.

func (Dir) Join

func (d Dir) Join(parts ...string) PathStr

Join implements PurePath.

Example
result := pathlib.Dir("/tmp").Join("a/b")
fmt.Printf("%s is a %T", result, result)
Output:

/tmp/a/b is a pathlib.PathStr

func (Dir) Localize

func (d Dir) Localize() (Dir, error)

See path/filepath.Localize

Localize implements Transformer.

Example
fmt.Println("On Unix")
fmt.Println("Localized", expect(pathlib.Dir("foo/bar/baz").Localize()))
Output:

On Unix
Localized foo/bar/baz

func (Dir) Lstat

func (d Dir) Lstat() (result Info[Dir], err error)

See os.Lstat.

Lstat implements Beholder.

Example
tmpDir := expect(pathlib.TempDir().
	Join("dir-lstat-example").
	AsDir().
	MakeAll(0o777, 0o777))
defer func() { _, _ = tmpDir.RemoveAll() }()

nonExistent := tmpDir.Join("dir").AsDir()
_, err := nonExistent.Lstat()
if errors.Is(err, fs.ErrNotExist) {
	fmt.Printf("%T(%q).Lstat() => fs.ErrNotExist\n", nonExistent, nonExistent)
}

dir := expect(nonExistent.Make(0o777))

file := tmpDir.Join("file.txt").AsFile()
expect(file.Make(0o755))

_, err = pathlib.Dir(file.String()).Lstat()
if e, ok := err.(pathlib.WrongTypeOnDisk[pathlib.Dir]); ok {
	fmt.Println(e.Error())
}

link := expect(tmpDir.Join("link").AsSymlink().LinkTo(pathlib.PathStr(dir)))
disguised := pathlib.Dir(link)
unmasked := expect(disguised.Stat()) // works
for _, name := range []string{"a", "b", "c"} {
	expect(dir.Join(name).AsFile().Make(0666))
}
fmt.Println(unmasked.Path())
for _, entry := range expect(unmasked.Path().Read()) {
	fmt.Println(entry)
}
Output:

pathlib.Dir("/tmp/dir-lstat-example/dir").Lstat() => fs.ErrNotExist
pathlib.Dir("/tmp/dir-lstat-example/file.txt") unexpectedly has mode -rwxr-xr-x on-disk
/tmp/dir-lstat-example/link
- a
- b
- c

func (Dir) Make

func (d Dir) Make(perm fs.FileMode) (result Dir, err error)

See os.Mkdir.

Make implements Maker.

func (Dir) MakeAll

func (d Dir) MakeAll(perm, parentPerm fs.FileMode) (result Dir, err error)

MakeAll implements Maker

func (Dir) Parent

func (d Dir) Parent() Dir

Parent implements PurePath.

func (Dir) Parts

func (d Dir) Parts() []string

func (Dir) Read

func (d Dir) Read() ([]fs.DirEntry, error)

See os.ReadDir.

Read implements [Reader].

func (Dir) Rel

func (d Dir) Rel(base Dir) (Dir, error)

Returns a relative path to the target directory, or an error if the path cannot be made relative.

See path/filepath.Rel.

Rel implements Transformer.

func (Dir) Remove

func (d Dir) Remove() error

See os.Remove.

Remove implements Remover.

func (Dir) RemoveAll

func (d Dir) RemoveAll() (Dir, error)

See os.RemoveAll.

RemoveAll implements [Destroyer].

func (Dir) Rename

func (d Dir) Rename(newPath PathStr) (Dir, error)

See os.Rename.

Rename implements Remover.

func (Dir) Stat

func (d Dir) Stat() (result Info[Dir], err error)

Observe the directory's filesystem Info on-disk. If the info is not a directory or a symlink, Stat will return a WrongTypeOnDisk error.

See os.Stat.

Stat implements Beholder.

func (Dir) String

func (d Dir) String() string

Convenience method to cast get the untyped string representation of the path.

String implements Transformer.

func (Dir) Walk

func (d Dir) Walk(
	callback func(path PathStr, d fs.DirEntry, err error) error,
) error

See path/filepath.WalkDir.

Example
dir := expect(pathlib.TempDir().Join("dir-walk-example").AsDir().Make(0755))
defer func() { _, _ = dir.RemoveAll() }()

_ = expect(dir.Join("foo/bar/baz").AsDir().MakeAll(0755, 0755))
_ = expect(dir.Join("a/b/c").AsDir().MakeAll(0755, 0755))

err := dir.Walk(func(path pathlib.PathStr, d fs.DirEntry, err error) error {
	fmt.Println(expect(path.Rel(dir)))
	return nil
})
if err != nil {
	panic(err)
}
Output:

.
a
a/b
a/b/c
foo
foo/bar
foo/bar/baz

type File

type File PathStr

A path that represents a file.

Example (PurePath)
f := pathlib.File("~/.config/tool/../other-tool/config.toml")

fmt.Println("On Unix")
fmt.Printf("%T(%q)\n", f, f)
method := func(name string, val any) {
	fmt.Printf("\t.%s() => %T(%#v)\n", name, val, val)
}
method("Parts", f.Parts())
method("Clean", f.Clean())
method("Parent", f.Parent())
method("BaseName", f.BaseName())
method("IsAbsolute", f.IsAbsolute())
method("IsLocal", f.IsLocal())
method("Ext", f.Ext())
// method("Abs().Unwrap", d.Abs()))
// method("ExpandUser().Unwrap", d.ExpandUser()))
Output:

On Unix
pathlib.File("~/.config/tool/../other-tool/config.toml")
	.Parts() => []string([]string{"~", ".config", "tool", "..", "other-tool", "config.toml"})
	.Clean() => pathlib.File("~/.config/other-tool/config.toml")
	.Parent() => pathlib.Dir("~/.config/other-tool")
	.BaseName() => string("config.toml")
	.IsAbsolute() => bool(false)
	.IsLocal() => bool(true)
	.Ext() => string(".toml")

func (File) Abs

func (f File) Abs() (File, error)

Returns an absolute path, or an error if the path cannot be made absolute. Note that there may be more than one absolute path for a given input path.

See path/filepath.Abs.

Abs implements Transformer.

func (File) BaseName

func (f File) BaseName() string

BaseName implements PurePath.

func (File) Chmod

func (f File) Chmod(mode os.FileMode) error

See os.Chmod.

Chmod implements Changer.

func (File) Chown

func (f File) Chown(uid int, gid int) error

See os.Chown.

Chown implements Changer.

func (File) Clean

func (f File) Clean() File

Remove ".", "..", and repeated slashes from a path.

See path/filepath.Clean.

Clean implements Transformer.

func (File) Eq

func (f File) Eq(other File) bool

Returns true if the two paths represent the same path.

Eq implements Transformer.

func (File) Exists

func (f File) Exists() bool

Returns true if the path exists on-disk after following symlinks.

See os.Stat, fs.ErrNotExist.

Exists implements Beholder.

func (File) ExpandUser

func (f File) ExpandUser() (File, error)

ExpandUser implements Transformer.

func (File) Ext

func (f File) Ext() string

Ext implements PurePath.

func (File) IsAbsolute

func (f File) IsAbsolute() bool

IsAbsolute implements PurePath.

func (File) IsLocal

func (f File) IsLocal() bool

IsLocal implements PurePath.

func (File) Join

func (f File) Join(parts ...string) PathStr

Join implements PurePath.

func (File) Localize

func (f File) Localize() (File, error)

Localize implements Transformer.

func (File) Lstat

func (f File) Lstat() (info Info[File], err error)

Observe the file info of the path on-disk. Does not follow symlinks. If the observed info is not a file or a symlink, Lstat returns a WrongTypeOnDisk error.

See os.Lstat.

OnDisk implements Beholder

func (File) Make

func (f File) Make(perm fs.FileMode) (FileHandle, error)

Create the file if it doesn't exist. If it does, do nothing.

See os.Open, os.O_CREATE.

Make implements Maker.

func (File) MakeAll

func (f File) MakeAll(perm, parentPerm fs.FileMode) (result FileHandle, err error)

Create the file and any missing parents. If the file exists, do nothing.

See os.Open, os.O_CREATE.

Make implements Maker.

func (File) Open

func (f File) Open(flag int, perm fs.FileMode) (FileHandle, error)

See os.OpenFile.

func (File) Parent

func (f File) Parent() Dir

Parent implements PurePath.

func (File) Parts

func (f File) Parts() []string

Parts implements PurePath.

func (File) Read

func (f File) Read() ([]byte, error)

See os.ReadFile.

Read implements Readable.

func (File) Rel

func (f File) Rel(base Dir) (File, error)

Returns a relative path to the target directory, or an error if the path cannot be made relative.

See path/filepath.Rel.

Rel implements Transformer.

func (File) Remove

func (f File) Remove() error

See os.Remove.

Remove implements Remover.

func (File) Rename

func (f File) Rename(newPath PathStr) (File, error)

See os.Rename.

Rename implements Remover.

func (File) Stat

func (f File) Stat() (Info[File], error)

Observe the file info of the path on-disk. Follows symlinks. If the observed info is not a file Stat returns a WrongTypeOnDisk error.

See os.Stat.

Stat implements Beholder.

func (File) String

func (f File) String() string

Convenience method to cast get the untyped string representation of the path.

String implements Transformer.

type FileHandle

type FileHandle interface {
	Path() File
	PurePath
	Beholder[File]
	Transformer[File]
	Changer
	Remover[File]

	// from *os.File
	Name() string
	Truncate(size int64) error
	SyscallConn() (syscall.RawConn, error)
	SetDeadline(deadline time.Time) error
	SetReadDeadline(deadline time.Time) error
	SetWriteDeadline(deadline time.Time) error
	Fd() uintptr

	io.Closer
	io.Seeker
	io.Reader
	io.Writer
	io.StringWriter
}

An open file descriptor. Unlike an os.File, it can only represent a logical file (as in a document on-disk), never a directory.

type Info

type Info[P Kind] interface {
	fs.FileInfo
	PurePath
	Transformer[P]
	Changer
	Remover[P]
	// the typed version of [fs.FileInfo.Name]
	Path() P
}

An observation of a path on-disk, including a constant observation timestamp.

type Kind

type Kind interface {
	PurePath
	~string
}

Any type constraint: any string type that represents a path

type Maker

type Maker[T any] interface {
	Make(perm fs.FileMode) (T, error)
	MakeAll(perm, parentPerm fs.FileMode) (T, error)
}

type PathStr

type PathStr string
Example (Beholder)
temp := expect(pathlib.TempDir().Join("path-str-beholder").AsDir().Make(0777))
defer func() { _, _ = temp.RemoveAll() }()

file := temp.Join("file.txt")
expect(file.AsFile().Make(0644))

rel := expect(file.Rel(temp))
fmt.Printf("OnDisk: %q %s\n", rel, expect(file.Stat()).Mode())
fmt.Printf(" Lstat: %q %s\n", rel, expect(file.Lstat()).Mode())
fmt.Printf("  Stat: %q %s\n", rel, expect(file.Stat()).Mode())
Output:

OnDisk: "file.txt" -rw-r--r--
 Lstat: "file.txt" -rw-r--r--
  Stat: "file.txt" -rw-r--r--
Example (Read)
tmpDir := expect(pathlib.TempDir().Join("example-pathStr-read").AsDir().Make(0777))
example := tmpDir.Join("example")

{
	expect(example.AsDir().Make(0777))
	expect(example.Join("foo").AsFile().Make(0644))
	expect(example.Join("bar").AsDir().Make(0777))
	entries := expect(example.Read()).([]fs.DirEntry)
	for entry := range entries {
		fmt.Println(entry)
	}
	enforce(example.Remove())
}
{
	_, err := expect(example.AsFile().Make(0644)).WriteString("text")
	if err != nil {
		panic(err)
	}
	fmt.Println(expect(example.Read()))
	enforce(example.Remove())
}
{
	target := tmpDir.Join("target")
	if _, err := expect(target.AsFile().Make(0644)).WriteString("target"); err != nil {
		panic(err)
	}
	expect(example.AsSymlink().LinkTo(target))
	fmt.Println(expect(example.Read()))
}

func (PathStr) Abs

func (p PathStr) Abs() (PathStr, error)

Returns an absolute path, or an error if the path cannot be made absolute. Note that there may be more than one absolute path for a given input path.

See path/filepath.Abs.

Abs implements Transformer.

func (PathStr) Ancestors

func (p PathStr) Ancestors() iter.Seq[Dir]

experimental

Example (Absolute)
for d := range pathlib.PathStr("/foo/bar/baz").Ancestors() {
	fmt.Println(d)
}
Output:

/foo/bar
/foo
/
Example (Relative)
for d := range pathlib.PathStr("./foo/bar/baz").Ancestors() {
	fmt.Println(d)
}
// this is the same as:
for d := range pathlib.PathStr("foo/bar/baz").Ancestors() {
	fmt.Println(d)
}
Output:

foo/bar
foo
.
foo/bar
foo
.

func (PathStr) AsDir

func (p PathStr) AsDir() Dir

Utility function to declare that the PathStr represents a directory

func (PathStr) AsFile

func (p PathStr) AsFile() File

Utility function to declare that the PathStr represents a file

func (p PathStr) AsSymlink() Symlink

Utility function to declare that the PathStr represents a symlink.

func (PathStr) BaseName

func (p PathStr) BaseName() string

A wrapper around path/filepath.Base.

BaseName implements PurePath.

Example
example := func(p pathlib.PathStr) {
	fmt.Printf("%q => %q\n", p, p.BaseName())
}
fmt.Println("On Unix:")
example("/foo/bar/baz.js")
example("/foo/bar/baz")
example("/foo/bar/baz/")
example("dev.txt")
example("../todo.txt")
example("..")
example(".")
example("/")
example("")
Output:

On Unix:
"/foo/bar/baz.js" => "baz.js"
"/foo/bar/baz" => "baz"
"/foo/bar/baz/" => "baz"
"dev.txt" => "dev.txt"
"../todo.txt" => "todo.txt"
".." => ".."
"." => "."
"/" => "/"
"" => "."

func (PathStr) Chmod

func (p PathStr) Chmod(mode os.FileMode) error

See os.Chmod.

Chmod implements Changer.

func (PathStr) Chown

func (p PathStr) Chown(uid int, gid int) error

Change Ownership of the path.

Chown implements Changer.

func (PathStr) Clean

func (p PathStr) Clean() PathStr

Remove ".", "..", and repeated slashes from a path.

See path/filepath.Clean.

Clean implements Transformer.

func (PathStr) Eq

func (p PathStr) Eq(q PathStr) bool

----------------------------------------------------------------------------- Returns true if the two paths represent the same path.

Eq implements Transformer.

func (PathStr) Exists

func (p PathStr) Exists() bool

Exists implements Beholder.

func (PathStr) ExpandUser

func (p PathStr) ExpandUser() (PathStr, error)

Expand a leading "~" into the user's home directory. If the home directory cannot be determined, the path is returned unchanged.

Example
home := expect(pathlib.UserHomeDir())
example := func(p pathlib.PathStr) {
	expanded := expect(p.ExpandUser())
	fmt.Printf(
		"%q => %q\n",
		p,
		strings.Replace(string(expanded), string(home), "$HOME", 1),
	)
}
fmt.Println("On Unix:")
example("~")
example("~/foo/bar.txt")
example("foo/~/bar")
Output:

On Unix:
"~" => "$HOME"
"~/foo/bar.txt" => "$HOME/foo/bar.txt"
"foo/~/bar" => "foo/~/bar"

func (PathStr) Ext

func (p PathStr) Ext() string

A wrapper around path/filepath.Ext.

Ext implements PurePath

Example
example := func(p pathlib.PathStr) {
	fmt.Printf("%q => %q\n", p, p.Ext())
}
example("index")
example("index.js")
example("main.test.js")
Output:

"index" => ""
"index.js" => ".js"
"main.test.js" => ".js"

func (PathStr) IsAbsolute

func (p PathStr) IsAbsolute() bool

Returns true if the path is absolute, false otherwise. See path/filepath.IsAbs for more details.

IsAbsolute implements PurePath.

Example
fmt.Println("On Unix:")
fmt.Println(pathlib.PathStr("/home/gopher").IsAbsolute())
fmt.Println(pathlib.PathStr(".bashrc").IsAbsolute())
fmt.Println(pathlib.PathStr("..").IsAbsolute())
fmt.Println(pathlib.PathStr(".").IsAbsolute())
fmt.Println(pathlib.PathStr("/").IsAbsolute())
fmt.Println(pathlib.PathStr("").IsAbsolute())
Output:

On Unix:
true
false
false
false
true
false

func (PathStr) IsLocal

func (p PathStr) IsLocal() bool

returns true if the path is local/relative, false otherwise. see path/filepath.IsLocal for more details.

IsLocal implements PurePath.

Example
p := pathlib.PathStr("/absolute/path/to/file.txt")
fmt.Println(p.IsLocal()) // false

p = pathlib.PathStr("relative/path/to/file.txt")
fmt.Println(p.IsLocal()) // true

p = pathlib.PathStr("./local/path/to/file.txt")
fmt.Println(p.IsLocal()) // true
Output:

false
true
true

func (PathStr) Join

func (p PathStr) Join(segments ...string) PathStr

A wrapper around path/filepath.Join.

Example
fmt.Println("On Unix:")
example := func(p pathlib.PathStr, segments ...string) {
	q := p.Join(segments...)
	x := fmt.Sprintf("%q", segments)
	fmt.Printf("%T(%q).Join(%s) => %q\n", p, p, x[1:len(x)-1], q)
}
example(pathlib.PathStr("a"), "b", "c")
example(pathlib.PathStr("a"), "b/c")
example(pathlib.PathStr("a/b"), "c")
example(pathlib.PathStr("a/b"), "/c")

example(pathlib.PathStr("a/b"), "../../../xyz")
Output:

On Unix:
pathlib.PathStr("a").Join("b" "c") => "a/b/c"
pathlib.PathStr("a").Join("b/c") => "a/b/c"
pathlib.PathStr("a/b").Join("c") => "a/b/c"
pathlib.PathStr("a/b").Join("/c") => "a/b/c"
pathlib.PathStr("a/b").Join("../../../xyz") => "../xyz"

func (PathStr) Localize

func (p PathStr) Localize() (PathStr, error)

See path/filepath.Localize. Localize implements Transformer.

func (PathStr) Lstat

func (p PathStr) Lstat() (Info[PathStr], error)

Observe the file info of the path on-disk. Note that this does not follow symlinks.

see os.Lstat.

Lstat implements Beholder.

func (PathStr) Parent

func (p PathStr) Parent() Dir

a wrapper around path/filepath.Dir.

Parent implements PurePath.

func (PathStr) Parts

func (p PathStr) Parts() (parts []string)

Splits the path at every character that's a path separator. Omits empty segments. See os.IsPathSeparator.

Example
example := func(p pathlib.PathStr) {
	fmt.Printf("%q => %#v\n", p, p.Parts())
}
fmt.Println("On Unix:")
example("/a/b")
example("./a/b")
example("a/b")
example("a/../b")
example("a//b")
example("")
Output:

On Unix:
"/a/b" => []string{"/", "a", "b"}
"./a/b" => []string{".", "a", "b"}
"a/b" => []string{"a", "b"}
"a/../b" => []string{"a", "..", "b"}
"a//b" => []string{"a", "b"}
"" => []string(nil)

func (PathStr) Read

func (p PathStr) Read() (any, error)

Read attempts to read what the path represents. See File.Read, Dir.Read, and Symlink.Read for the possible return types.

Read implements Readable.

Example
var dir = pathlib.TempDir().Join("PathStr.Read")
var file = dir.Join("a.txt")
var link = dir.Join("link")

_, err := expect(file.AsFile().MakeAll(0666, 0777)).WriteString("file text")
if err != nil {
	panic(err)
}
defer func() { expect(dir.AsDir().RemoveAll()) }()
expect(link.AsSymlink().LinkTo(file))

var contents any
contents = expect(pathlib.PathStr(file).Read())
fmt.Printf("file.Read() => %T(%q)\n", contents, contents)

contents = expect(pathlib.PathStr(dir).Read())
fmt.Printf("dir.Read() => %T(%q)\n", contents, contents)

contents = expect(pathlib.PathStr(link).Read())
fmt.Printf("link.Read() => %T(%q)", contents, contents)
Output:

file.Read() => []uint8("file text")
dir.Read() => []fs.DirEntry(["- a.txt" "L link"])
link.Read() => pathlib.PathStr("/tmp/PathStr.Read/a.txt")

func (PathStr) Rel

func (p PathStr) Rel(base Dir) (PathStr, error)

Returns a relative path to the target directory, or an error if the path cannot be made relative.

See path/filepath.Rel.

Rel implements Transformer

Example
example := func(a pathlib.PathStr, b pathlib.Dir) {
	val, err := a.Rel(b)
	if err == nil {
		fmt.Printf("%T(%q).Rel(%q) => %T(%q)\n", a, a, b, val, val)
	} else {
		fmt.Printf("%T(%q).Rel(%q) => Err(%v)\n", a, a, b, err)
	}
}
fmt.Println("On Unix")
example("/a/b/c", "/a")
example("/b/c", "/a")
example("./b/c", "/a")
Output:

On Unix
pathlib.PathStr("/a/b/c").Rel("/a") => pathlib.PathStr("b/c")
pathlib.PathStr("/b/c").Rel("/a") => pathlib.PathStr("../b/c")
pathlib.PathStr("./b/c").Rel("/a") => Err(Rel: can't make ./b/c relative to /a)

func (PathStr) Remove

func (p PathStr) Remove() error

See os.Remove.

Remove implements Remover.

Example
dir := expect(pathlib.TempDir().Join("pathStr-remove").AsDir().Make(0777))
defer func() { expect(dir.RemoveAll()) }()

f := dir.Join("foo.txt")
expect(f.AsFile().Make(0666))
link := dir.Join("link")
expect(link.AsSymlink().LinkTo(f))
err := pathlib.PathStr(dir).Remove()
if err != nil {
	fmt.Println("Unable to delete non-empty directory " + dir)
}

enforce(link.Remove())
if !link.Exists() {
	fmt.Println("removed symlink " + link)
}
if f.Exists() {
	fmt.Println("removing " + link + " didn't affect link target " + f)
} else {
	panic("removing " + link + " removed " + f)
}

enforce(f.Remove())
if !f.Exists() {
	fmt.Println("removed file " + f)
}
Output:

Unable to delete non-empty directory /tmp/pathStr-remove
removed symlink /tmp/pathStr-remove/link
removing /tmp/pathStr-remove/link didn't affect link target /tmp/pathStr-remove/foo.txt
removed file /tmp/pathStr-remove/foo.txt

func (PathStr) Rename

func (p PathStr) Rename(newPath PathStr) (PathStr, error)

See os.Rename.

Rename implements Remover.

func (PathStr) Stat

func (p PathStr) Stat() (Info[PathStr], error)

See os.Stat.

Stat implements Beholder.

func (PathStr) String

func (p PathStr) String() string

Convenience method to cast get the untyped string representation of the path.

String implements Transformer.

type PurePath

type PurePath interface {
	// See [path/filepath.Join].
	Join(segments ...string) PathStr
	// Return the parent directory. Should have the same properties as [path/filepath.Dir].
	Parent() Dir
	// See [path/filepath.Base].
	BaseName() string
	// See [path/filepath.Ext].
	Ext() string
	// Split the path into multiple non-empty segments.
	Parts() []string

	// Returns true if the path is absolute. See [path/filepath.IsAbs].
	IsAbsolute() bool
	// Returns true if the path is local/relative. See [path/filepath.IsLocal].
	IsLocal() bool
}

String-only infallible path operations that do not require filesystem access or syscalls.

type Readable

type Readable[T any] interface {
	Read() (T, error)
}

type Remover

type Remover[P Kind] interface {
	// see [os.Remove].
	Remove() error
	// see [os.Rename].
	Rename(newPath PathStr) (P, error)
}

Behaviors that cause something at a path to no longer be there.

type Symlink PathStr

func (Symlink) Abs

func (s Symlink) Abs() (Symlink, error)

Returns an absolute path, or an error if the path cannot be made absolute. Note that there may be more than one absolute path for a given input path.

See path/filepath.Abs.

Abs implements Transformer.

func (Symlink) BaseName

func (s Symlink) BaseName() string

BaseName implements PurePath.

func (Symlink) Chmod

func (s Symlink) Chmod(mode os.FileMode) error

See os.Chmod.

Chmod implements Changer.

func (Symlink) Chown

func (s Symlink) Chown(uid int, gid int) error

See os.Chown.

Chown implements Changer.

func (Symlink) Clean

func (s Symlink) Clean() Symlink

Remove ".", "..", and repeated slashes from a path.

See path/filepath.Clean.

Clean implements Transformer

func (Symlink) Eq

func (s Symlink) Eq(other Symlink) bool

Returns true if the two paths represent the same path. This does not take into account any links.

Eq implements Transformer.

func (Symlink) Exists

func (s Symlink) Exists() bool

Returns true if the path exists on-disk WITHOUT following symlinks.

See os.Stat, fs.ErrNotExist.

Exists implements Beholder.

func (Symlink) ExpandUser

func (s Symlink) ExpandUser() (Symlink, error)

ExpandUser implements Transformer

func (Symlink) Ext

func (s Symlink) Ext() string

Ext implements PurePath

func (Symlink) IsAbsolute

func (s Symlink) IsAbsolute() bool

IsAbsolute implements PurePath.

func (Symlink) IsLocal

func (s Symlink) IsLocal() bool

IsLocal implements PurePath.

func (Symlink) Join

func (s Symlink) Join(parts ...string) PathStr

Join implements PurePath.

func (Symlink) LinkTo

func (s Symlink) LinkTo(target PathStr) (Symlink, error)

See os.Symlink.

func (Symlink) Localize

func (s Symlink) Localize() (Symlink, error)

Localize implements Transformer.

func (Symlink) Lstat

func (s Symlink) Lstat() (result Info[Symlink], err error)

Looks up the symlink's info on-disk. Note that this returns information about the symlink itself, not its target. If the file info's mode not match fs.ModeSymlink, Lstat returns a WrongTypeOnDisk error.

See os.Lstat.

Lstat implements Beholder.

Example
tempDir := expect(pathlib.Cwd()).
	Join("temp", "symlink-lstat").
	AsDir()
expect(tempDir.MakeAll(0o755, 0o755))
defer func() { expect(tempDir.RemoveAll()) }()

file := tempDir.Join("file.txt").AsFile()
expect(file.Make(0o666))

{
	link := tempDir.Join("link")
	if link.String() != tempDir.String()+"/link" {
		panic(link)
	}
}
link := expect(tempDir.Join("link").
	AsSymlink().
	LinkTo(pathlib.PathStr(file.String())))

onDisk := expect(link.Lstat())

fmt.Printf(
	"%s -> %s",
	expect(onDisk.Path().Rel(tempDir)),
	expect(expect(link.Read()).Rel(tempDir)),
)
Output:

link -> file.txt

func (Symlink) Parent

func (s Symlink) Parent() Dir

Parent implements PurePath.

func (Symlink) Parts

func (s Symlink) Parts() []string

func (Symlink) Read

func (s Symlink) Read() (PathStr, error)

Returns the target of the symlink. Note that the target may not exist.

See os.Readlink.

Read implements Readable.

func (Symlink) Rel

func (s Symlink) Rel(base Dir) (Symlink, error)

Returns a relative path to the target directory, or an error if the path cannot be made relative.

See path/filepath.Rel.

Rel implements Transformer.

func (Symlink) Remove

func (s Symlink) Remove() error

Remove the link without affecting the link target.

See os.Remove.

Remove implements Remover.

func (Symlink) Rename

func (s Symlink) Rename(newPath PathStr) (Symlink, error)

Rename the link without affecting the link target.

see os.Rename.

Rename implements Remover.

func (Symlink) Stat

func (s Symlink) Stat() (Info[Symlink], error)

Since Stat follows symlinks, it doesn't perform any validation of returned Info's file mode.

See os.Stat.

Stat implements Beholder.

func (Symlink) String

func (s Symlink) String() string

Convenience method to cast get the untyped string representation of the path.

String implements Transformer.

type Transformer

type Transformer[P Kind] interface {
	// Returns an absolute path, or an error if the path cannot be made absolute. Note that there may be more than one
	// absolute path for a given input path.
	//
	// See [path/filepath.Abs].
	Abs() (P, error)
	// Returns a relative path to the target directory, or an error if the path cannot be made relative.
	//
	// See [path/filepath.Rel].
	Rel(target Dir) (P, error)
	// See [path/filepath.Localize].
	Localize() (P, error)
	// Expand `~` into the home directory of the current user.
	ExpandUser() (P, error)
	// Remove ".", "..", and repeated slashes from a path.
	//
	// See [path/filepath.Clean].
	Clean() P
	// Returns true if the two paths represent the same path.
	Eq(other P) bool

	// Convenience method to cast get the untyped string representation of the path.
	String() string
}

transforms the appearance of a path, but not what it represents.

type WrongTypeOnDisk

type WrongTypeOnDisk[P Kind] struct{ Observed Info[P] }

func (WrongTypeOnDisk[P]) Error

func (w WrongTypeOnDisk[P]) Error() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL