entitypath

package
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Jun 8, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package entitypath provides filepath-like manipulation of wherehouse entity paths. Paths are sequences of segments joined by a separator (":"). A path with a leading separator is absolute (rooted at the entity tree root); one without is relative. The package is pure syntax — it never touches the DB.

Index

Examples

Constants

View Source
const Separator = ":"

Separator is the string between path segments.

Variables

View Source
var (
	// ErrInvalidPath is returned when a path string fails structural validation.
	ErrInvalidPath = errors.New("entitypath: invalid path")
	// ErrEmptySegment is returned when a segment is an empty string.
	ErrEmptySegment = errors.New("entitypath: empty segment")
	// ErrSegmentContainsSeparator is returned when a segment contains Separator.
	ErrSegmentContainsSeparator = errors.New("entitypath: segment contains separator")
	// ErrInvalidSegment is returned when a segment has leading/trailing whitespace
	// or contains control characters.
	ErrInvalidSegment = errors.New("entitypath: invalid segment")
	// ErrNotDescendant is returned by Rel when base is not an ancestor of p.
	ErrNotDescendant = errors.New("entitypath: not a descendant of base")
)

Sentinel errors returned by entitypath functions. Use errors.Is to test.

View Source
var Root = Path(Separator)

Root is the canonical absolute root path: a leading separator with no segments.

Functions

func ValidateSegment

func ValidateSegment(name string) error

ValidateSegment returns an error if name is not a valid path segment. It rejects empty strings, strings containing Separator, strings with leading/trailing whitespace, and strings containing control characters.

Types

type Path

type Path string //nolint:recvcheck // UnmarshalJSON requires *Path to satisfy json.Unmarshaler; all other methods use value receivers by design

Path is a syntactic entity path. The zero value Path("") is the valid empty relative path. Paths with a leading Separator are absolute (rooted).

func AppendTo added in v0.5.2

func AppendTo(parent, leaf string) (Path, error)

AppendTo parses parent as a Path and appends leaf as a single segment. It is a convenience wrapper for the common pattern of building a child path from a stored string (e.g. a FullPathDisplay from the DB) and a leaf name.

func MustParse

func MustParse(s string) Path

MustParse panics if s is not a valid path. Intended for test helpers and package-level vars only.

func New

func New(segments ...string) (Path, error)

New builds a relative path from segments. Each segment is validated. New() with no arguments returns Path("").

func Parse

func Parse(s string) (Path, error)

Parse validates s and returns it as a Path. It does not Clean; invalid paths return ErrInvalidPath (wrapping a more specific inner error).

Example
package main

import (
	"fmt"

	"github.com/asphaltbuffet/wherehouse/internal/entitypath"
)

func main() {
	p, err := entitypath.Parse(":Garage:Shelf A")
	if err != nil {
		panic(err)
	}
	fmt.Println(p)
	fmt.Println(p.IsAbs())
	fmt.Println(p.Depth())
}
Output:
:Garage:Shelf A
true
2

func (Path) Ancestors

func (p Path) Ancestors() []Path

Ancestors returns all ancestors of p from the root-most to the nearest, excluding p itself. Returns an empty slice for Root and Path("").

func (Path) Append

func (p Path) Append(name string) (Path, error)

Append appends exactly one segment to p. It is equivalent to Join with a single argument and is the dominant call shape (parent path + child name).

func (Path) Base

func (p Path) Base() string

Base returns the last segment, or "" for Root and the empty path.

func (Path) Clean

func (p Path) Clean() Path

Clean collapses adjacent separators and trims a single trailing separator (except for Root). It is idempotent. It does not resolve "." or "..".

Example
package main

import (
	"fmt"

	"github.com/asphaltbuffet/wherehouse/internal/entitypath"
)

func main() {
	p := entitypath.Path("Foo::Bar:")
	fmt.Println(p.Clean())
}
Output:
Foo:Bar

func (Path) Depth

func (p Path) Depth() int

Depth returns the number of segments. Root and the empty path both have depth 0.

func (Path) Dir

func (p Path) Dir() Path

Dir returns all but the last segment of p. Dir of a 1-segment absolute path returns Root; Dir of a 1-segment relative path returns Path(""). Dir of Root returns Root; Dir of Path("") returns Path("").

func (Path) HasPrefix

func (p Path) HasPrefix(other Path) bool

HasPrefix reports whether p == other or other.IsAncestor(p). Useful for subtree containment checks.

func (Path) IsAbs

func (p Path) IsAbs() bool

IsAbs reports whether p is an absolute (rooted) path.

func (Path) IsAncestor

func (p Path) IsAncestor(other Path) bool

IsAncestor reports whether p is a strict proper ancestor of other. p.IsAncestor(p) is always false. Mismatched absolute/relative paths return false.

func (Path) IsEmpty

func (p Path) IsEmpty() bool

IsEmpty reports whether p is the empty relative path (zero value).

func (Path) IsRoot

func (p Path) IsRoot() bool

IsRoot reports whether p is the absolute root path (Separator with no segments).

func (Path) Join

func (p Path) Join(segments ...string) (Path, error)

Join appends segments to p. Each segment is validated. Passing no segments returns p unchanged. An empty segment string is an error.

Example
package main

import (
	"fmt"

	"github.com/asphaltbuffet/wherehouse/internal/entitypath"
)

func main() {
	p := entitypath.MustParse(":Garage:Shelf A")
	child, err := p.Join("Bin 3")
	if err != nil {
		panic(err)
	}
	fmt.Println(child)
	fmt.Println(p.IsAncestor(child))
}
Output:
:Garage:Shelf A:Bin 3
true

func (Path) MarshalJSON

func (p Path) MarshalJSON() ([]byte, error)

MarshalJSON emits the path as a JSON string.

func (Path) Rel

func (p Path) Rel(base Path) (Path, error)

Rel returns the path of p relative to base. base must be an ancestor of p or equal p. If base == p, the empty relative path is returned. If base is not an ancestor, ErrNotDescendant is returned.

Example
package main

import (
	"fmt"

	"github.com/asphaltbuffet/wherehouse/internal/entitypath"
)

func main() {
	base := entitypath.MustParse(":Garage")
	child := entitypath.MustParse(":Garage:Shelf A:Bin 3")
	rel, err := child.Rel(base)
	if err != nil {
		panic(err)
	}
	fmt.Println(rel)
}
Output:
Shelf A:Bin 3

func (Path) Segments

func (p Path) Segments() []string

Segments returns the path segments. Never returns nil; empty paths and Root return an empty slice.

func (Path) String

func (p Path) String() string

String implements fmt.Stringer.

func (*Path) UnmarshalJSON

func (p *Path) UnmarshalJSON(b []byte) error

UnmarshalJSON accepts a JSON string and validates it via Parse. The pointer receiver is required to satisfy json.Unmarshaler.

func (Path) Walk

func (p Path) Walk(yield func(ancestor Path) bool)

Walk yields ancestors of p nearest-first (closest ancestor first), excluding p itself. Stops if yield returns false. Yields nothing for Root and Path(""). The range-over-func signature supports Go 1.23+ range loops.

Example
package main

import (
	"fmt"

	"github.com/asphaltbuffet/wherehouse/internal/entitypath"
)

func main() {
	p := entitypath.MustParse(":Garage:Shelf A:Bin 3")
	p.Walk(func(a entitypath.Path) bool {
		fmt.Println(a)
		return true
	})
}
Output:
:Garage:Shelf A
:Garage
:

func (Path) WalkSeq

func (p Path) WalkSeq() iter.Seq[Path]

WalkSeq returns an iterator over ancestors of p nearest-first. Equivalent to Walk but usable directly in range loops via iter.Seq.

Jump to

Keyboard shortcuts

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