All checks were successful
Test / Create distribution (push) Successful in 33s
Test / Sandbox (push) Successful in 2m1s
Test / Hakurei (push) Successful in 2m57s
Test / Hpkg (push) Successful in 3m50s
Test / Sandbox (race detector) (push) Successful in 4m13s
Test / Hakurei (race detector) (push) Successful in 4m48s
Test / Flake checks (push) Successful in 1m25s
This is less error-prone, and allows pathname to be checked once. Signed-off-by: Ophestra <cat@gensokyo.uk>
79 lines
1.8 KiB
Go
79 lines
1.8 KiB
Go
package container
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"path"
|
|
"syscall"
|
|
)
|
|
|
|
// AbsoluteError is returned by [NewAbsolute] and holds the invalid pathname.
|
|
type AbsoluteError struct {
|
|
Pathname string
|
|
}
|
|
|
|
func (e *AbsoluteError) Error() string { return fmt.Sprintf("path %q is not absolute", e.Pathname) }
|
|
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 string
|
|
}
|
|
|
|
// isAbs wraps [path.IsAbs] in case additional checks are added in the future.
|
|
func isAbs(pathname string) bool { return path.IsAbs(pathname) }
|
|
|
|
func (a *Absolute) String() string {
|
|
if a.pathname == zeroString {
|
|
panic("attempted use of zero Absolute")
|
|
}
|
|
return a.pathname
|
|
}
|
|
|
|
// NewAbsolute checks pathname and returns a new [Absolute] if pathname is absolute.
|
|
func NewAbsolute(pathname string) (*Absolute, error) {
|
|
if !isAbs(pathname) {
|
|
return nil, &AbsoluteError{pathname}
|
|
}
|
|
return &Absolute{pathname}, nil
|
|
}
|
|
|
|
// MustAbs calls [NewAbsolute] and panics on error.
|
|
func MustAbs(pathname string) *Absolute {
|
|
if a, err := NewAbsolute(pathname); err != nil {
|
|
panic(err.Error())
|
|
} else {
|
|
return a
|
|
}
|
|
}
|
|
|
|
func (a *Absolute) GobEncode() ([]byte, error) { return []byte(a.String()), nil }
|
|
func (a *Absolute) GobDecode(data []byte) error {
|
|
pathname := string(data)
|
|
if !isAbs(pathname) {
|
|
return &AbsoluteError{pathname}
|
|
}
|
|
a.pathname = pathname
|
|
return nil
|
|
}
|
|
|
|
func (a *Absolute) MarshalJSON() ([]byte, error) { return json.Marshal(a.String()) }
|
|
func (a *Absolute) UnmarshalJSON(data []byte) error {
|
|
var pathname string
|
|
if err := json.Unmarshal(data, &pathname); err != nil {
|
|
return err
|
|
}
|
|
if !isAbs(pathname) {
|
|
return &AbsoluteError{pathname}
|
|
}
|
|
a.pathname = pathname
|
|
return nil
|
|
}
|