All checks were successful
Test / Create distribution (push) Successful in 1m13s
Test / Sandbox (push) Successful in 3m5s
Test / Hakurei (push) Successful in 4m15s
Test / ShareFS (push) Successful in 4m16s
Test / Sandbox (race detector) (push) Successful in 5m34s
Test / Hakurei (race detector) (push) Successful in 6m41s
Test / Flake checks (push) Successful in 1m25s
This turned out not to require specific treatment, so the shared interfaces are cleaner. Signed-off-by: Ophestra <cat@gensokyo.uk>
132 lines
3.6 KiB
Go
132 lines
3.6 KiB
Go
// Package check provides types yielding values checked to meet a condition.
|
|
package check
|
|
|
|
import (
|
|
"encoding"
|
|
"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] }
|
|
|
|
var (
|
|
_ encoding.TextAppender = new(Absolute)
|
|
_ encoding.TextMarshaler = new(Absolute)
|
|
_ encoding.TextUnmarshaler = new(Absolute)
|
|
|
|
_ encoding.BinaryAppender = new(Absolute)
|
|
_ encoding.BinaryMarshaler = new(Absolute)
|
|
_ encoding.BinaryUnmarshaler = new(Absolute)
|
|
)
|
|
|
|
// 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())) }
|
|
|
|
// AppendText appends the checked pathname.
|
|
func (a *Absolute) AppendText(data []byte) ([]byte, error) {
|
|
return append(data, a.String()...), nil
|
|
}
|
|
|
|
// MarshalText returns the checked pathname.
|
|
func (a *Absolute) MarshalText() ([]byte, error) { return a.AppendText(nil) }
|
|
|
|
// UnmarshalText stores data if it represents an absolute pathname.
|
|
func (a *Absolute) UnmarshalText(data []byte) error {
|
|
pathname := string(data)
|
|
if !filepath.IsAbs(pathname) {
|
|
return AbsoluteError(pathname)
|
|
}
|
|
a.pathname = unique.Make(pathname)
|
|
return nil
|
|
}
|
|
|
|
func (a *Absolute) AppendBinary(data []byte) ([]byte, error) { return a.AppendText(data) }
|
|
func (a *Absolute) MarshalBinary() ([]byte, error) { return a.MarshalText() }
|
|
func (a *Absolute) UnmarshalBinary(data []byte) error { return a.UnmarshalText(data) }
|
|
|
|
// 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)
|
|
})
|
|
}
|