container/absolute: wrap safe stdlib functions
All checks were successful
Test / Create distribution (push) Successful in 34s
Test / Sandbox (push) Successful in 2m0s
Test / Hakurei (push) Successful in 2m57s
Test / Hpkg (push) Successful in 3m52s
Test / Sandbox (race detector) (push) Successful in 4m4s
Test / Hakurei (race detector) (push) Successful in 4m49s
Test / Flake checks (push) Successful in 1m31s

These functions do not change the absoluteness of a pathname.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
Ophestra 2025-08-10 03:10:13 +09:00
parent 02271583fb
commit 41ac2be965
Signed by: cat
SSH Key Fingerprint: SHA256:gQ67O0enBZ7UdZypgtspB2FDM1g3GVw8nX0XSdcFw8Q
4 changed files with 64 additions and 12 deletions

View File

@ -5,10 +5,12 @@ import (
"errors"
"fmt"
"path"
"slices"
"strings"
"syscall"
)
// AbsoluteError is returned by [NewAbsolute] and holds the invalid pathname.
// AbsoluteError is returned by [NewAbs] and holds the invalid pathname.
type AbsoluteError struct {
Pathname string
}
@ -37,23 +39,31 @@ func (a *Absolute) String() string {
return a.pathname
}
// NewAbsolute checks pathname and returns a new [Absolute] if pathname is absolute.
func NewAbsolute(pathname string) (*Absolute, error) {
// NewAbs checks pathname and returns a new [Absolute] if pathname is absolute.
func NewAbs(pathname string) (*Absolute, error) {
if !isAbs(pathname) {
return nil, &AbsoluteError{pathname}
}
return &Absolute{pathname}, nil
}
// MustAbs calls [NewAbsolute] and panics on error.
// MustAbs calls [NewAbs] and panics on error.
func MustAbs(pathname string) *Absolute {
if a, err := NewAbsolute(pathname); err != nil {
if a, err := NewAbs(pathname); err != nil {
panic(err.Error())
} else {
return a
}
}
// Append calls [path.Join] with [Absolute] as the first element.
func (a *Absolute) Append(elem ...string) *Absolute {
return &Absolute{path.Join(append([]string{a.String()}, elem...)...)}
}
// Dir calls [path.Dir] with [Absolute] as its argument.
func (a *Absolute) Dir() *Absolute { return &Absolute{path.Dir(a.String())} }
func (a *Absolute) GobEncode() ([]byte, error) { return []byte(a.String()), nil }
func (a *Absolute) GobDecode(data []byte) error {
pathname := string(data)
@ -76,3 +86,13 @@ func (a *Absolute) UnmarshalJSON(data []byte) error {
a.pathname = 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.String() == b.String() })
}

View File

@ -39,7 +39,7 @@ func TestAbsoluteError(t *testing.T) {
})
}
func TestNewAbsolute(t *testing.T) {
func TestNewAbs(t *testing.T) {
testCases := []struct {
name string
@ -54,12 +54,12 @@ func TestNewAbsolute(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
got, err := NewAbsolute(tc.pathname)
got, err := NewAbs(tc.pathname)
if !reflect.DeepEqual(got, tc.want) {
t.Errorf("NewAbsolute: %#v, want %#v", got, tc.want)
t.Errorf("NewAbs: %#v, want %#v", got, tc.want)
}
if !errors.Is(err, tc.wantErr) {
t.Errorf("NewAbsolute: error = %v, want %v", err, tc.wantErr)
t.Errorf("NewAbs: error = %v, want %v", err, tc.wantErr)
}
})
}
@ -282,3 +282,35 @@ func TestCodecAbsolute(t *testing.T) {
}
})
}
func TestAbsoluteWrap(t *testing.T) {
t.Run("join", func(t *testing.T) {
want := "/etc/nix/nix.conf"
if got := MustAbs("/etc").Append("nix", "nix.conf"); got.String() != want {
t.Errorf("Append: %q, want %q", got, want)
}
})
t.Run("dir", func(t *testing.T) {
want := "/"
if got := MustAbs("/etc").Dir(); got.String() != want {
t.Errorf("Dir: %q, want %q", got, want)
}
})
t.Run("sort", func(t *testing.T) {
want := []*Absolute{MustAbs("/etc"), MustAbs("/proc"), MustAbs("/sys")}
got := []*Absolute{MustAbs("/proc"), MustAbs("/sys"), MustAbs("/etc")}
SortAbs(got)
if !reflect.DeepEqual(got, want) {
t.Errorf("SortAbs: %#v, want %#v", got, want)
}
})
t.Run("compact", func(t *testing.T) {
want := []*Absolute{MustAbs("/etc"), MustAbs("/proc"), MustAbs("/sys")}
if got := CompactAbs([]*Absolute{MustAbs("/etc"), MustAbs("/proc"), MustAbs("/proc"), MustAbs("/sys")}); !reflect.DeepEqual(got, want) {
t.Errorf("CompactAbs: %#v, want %#v", got, want)
}
})
}

View File

@ -29,7 +29,7 @@ func Exec(ctx context.Context, p string) ([]*Entry, error) {
var toolPath *container.Absolute
if s, err := exec.LookPath(lddName); err != nil {
return nil, err
} else if toolPath, err = container.NewAbsolute(s); err != nil {
} else if toolPath, err = container.NewAbs(s); err != nil {
return nil, err
}

View File

@ -43,10 +43,10 @@ func (p *Proxy) Start() error {
}, nil)
} else {
var toolPath *container.Absolute
if a, err := container.NewAbsolute(p.name); err != nil {
if a, err := container.NewAbs(p.name); err != nil {
if p.name, err = exec.LookPath(p.name); err != nil {
return err
} else if toolPath, err = container.NewAbsolute(p.name); err != nil {
} else if toolPath, err = container.NewAbs(p.name); err != nil {
return err
}
} else {