Files
hakurei/check/absolute.go
Ophestra a6600be34a
All checks were successful
Test / Create distribution (push) Successful in 1m17s
Test / Sandbox (push) Successful in 3m5s
Test / Hakurei (push) Successful in 4m12s
Test / ShareFS (push) Successful in 4m25s
Test / Sandbox (race detector) (push) Successful in 5m39s
Test / Hakurei (race detector) (push) Successful in 6m44s
Test / Flake checks (push) Successful in 1m24s
all: use filepath
This makes package check portable, and removes nonportable behaviour from package pkg, pipewire, and system. All other packages remain nonportable due to their nature. No latency increase was observed due to this change on amd64 and arm64 linux.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-03-30 18:24:53 +09:00

133 lines
3.4 KiB
Go

// Package check provides types yielding values checked to meet a condition.
package check
import (
"encoding/json"
"errors"
"fmt"
"path/filepath"
"slices"
"strings"
"syscall"
"unique"
)
// AbsoluteError is returned by [NewAbs] and holds the invalid pathname.
type AbsoluteError string
func (e AbsoluteError) Error() string {
return fmt.Sprintf("path %q is not absolute", string(e))
}
func (e AbsoluteError) Is(target error) bool {
var ce AbsoluteError
if !errors.As(target, &ce) {
return errors.Is(target, syscall.EINVAL)
}
return e == ce
}
// Absolute holds a pathname checked to be absolute.
type Absolute struct{ pathname unique.Handle[string] }
// ok returns whether [Absolute] is not the zero value.
func (a *Absolute) ok() bool { return a != nil && *a != (Absolute{}) }
// unsafeAbs returns [check.Absolute] on any string value.
func unsafeAbs(pathname string) *Absolute {
return &Absolute{unique.Make(pathname)}
}
// String returns the checked pathname.
func (a *Absolute) String() string {
if !a.ok() {
panic("attempted use of zero Absolute")
}
return a.pathname.Value()
}
// Handle returns the underlying [unique.Handle].
func (a *Absolute) Handle() unique.Handle[string] {
return a.pathname
}
// Is efficiently compares the underlying pathname.
func (a *Absolute) Is(v *Absolute) bool {
if a == nil && v == nil {
return true
}
return a.ok() && v.ok() && a.pathname == v.pathname
}
// NewAbs checks pathname and returns a new [Absolute] if pathname is absolute.
func NewAbs(pathname string) (*Absolute, error) {
if !filepath.IsAbs(pathname) {
return nil, AbsoluteError(pathname)
}
return unsafeAbs(pathname), nil
}
// MustAbs calls [NewAbs] and panics on error.
func MustAbs(pathname string) *Absolute {
if a, err := NewAbs(pathname); err != nil {
panic(err)
} else {
return a
}
}
// Append calls [filepath.Join] with [Absolute] as the first element.
func (a *Absolute) Append(elem ...string) *Absolute {
return unsafeAbs(filepath.Join(append([]string{a.String()}, elem...)...))
}
// Dir calls [filepath.Dir] with [Absolute] as its argument.
func (a *Absolute) Dir() *Absolute { return unsafeAbs(filepath.Dir(a.String())) }
// GobEncode returns the checked pathname.
func (a *Absolute) GobEncode() ([]byte, error) {
return []byte(a.String()), nil
}
// GobDecode stores data if it represents an absolute pathname.
func (a *Absolute) GobDecode(data []byte) error {
pathname := string(data)
if !filepath.IsAbs(pathname) {
return AbsoluteError(pathname)
}
a.pathname = unique.Make(pathname)
return nil
}
// MarshalJSON returns a JSON representation of the checked pathname.
func (a *Absolute) MarshalJSON() ([]byte, error) {
return json.Marshal(a.String())
}
// UnmarshalJSON stores data if it represents an absolute pathname.
func (a *Absolute) UnmarshalJSON(data []byte) error {
var pathname string
if err := json.Unmarshal(data, &pathname); err != nil {
return err
}
if !filepath.IsAbs(pathname) {
return AbsoluteError(pathname)
}
a.pathname = unique.Make(pathname)
return nil
}
// SortAbs calls [slices.SortFunc] for a slice of [Absolute].
func SortAbs(x []*Absolute) {
slices.SortFunc(x, func(a, b *Absolute) int {
return strings.Compare(a.String(), b.String())
})
}
// CompactAbs calls [slices.CompactFunc] for a slice of [Absolute].
func CompactAbs(s []*Absolute) []*Absolute {
return slices.CompactFunc(s, func(a *Absolute, b *Absolute) bool {
return a.Is(b)
})
}