92 lines
2.2 KiB
Go
92 lines
2.2 KiB
Go
package ficha
|
|
|
|
// Matcher is a composable predicate evaluated against a Token.
|
|
// Use the package-level constructors All, Any, Not, Perm, and Check
|
|
// (on Token) to combine them.
|
|
type Matcher interface {
|
|
matches(t *Token) bool
|
|
}
|
|
|
|
// Perm returns a Matcher that checks for a single permission.
|
|
func Perm(p string) Matcher { return permMatcher(p) }
|
|
|
|
// All returns a Matcher that holds when every child matcher holds.
|
|
// Permission strings are accepted directly and treated as Perm(s).
|
|
// All() with no arguments holds (vacuous truth).
|
|
func All(matchers ...any) Matcher { return andMatcher(toMatchers(matchers)) }
|
|
|
|
// Any returns a Matcher that holds when at least one child matcher holds.
|
|
// Any() with no arguments does not hold.
|
|
func Any(matchers ...any) Matcher { return orMatcher(toMatchers(matchers)) }
|
|
|
|
// Not returns a Matcher that holds when the inner matcher does not.
|
|
// Strings are accepted and treated as Perm(s).
|
|
func Not(m any) Matcher {
|
|
return notMatcher{inner: toMatcher(m)}
|
|
}
|
|
|
|
// Check evaluates a Matcher against the token.
|
|
func (t *Token) Check(m Matcher) bool {
|
|
if m == nil {
|
|
return false
|
|
}
|
|
return m.matches(t)
|
|
}
|
|
|
|
// --- internal matcher implementations ---
|
|
|
|
type permMatcher string
|
|
|
|
func (p permMatcher) matches(t *Token) bool { return t.Has(string(p)) }
|
|
|
|
type andMatcher []Matcher
|
|
|
|
func (a andMatcher) matches(t *Token) bool {
|
|
for _, m := range a {
|
|
if !m.matches(t) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
type orMatcher []Matcher
|
|
|
|
func (o orMatcher) matches(t *Token) bool {
|
|
for _, m := range o {
|
|
if m.matches(t) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
type notMatcher struct{ inner Matcher }
|
|
|
|
func (n notMatcher) matches(t *Token) bool { return !n.inner.matches(t) }
|
|
|
|
// toMatcher accepts either a string (treated as Perm) or a Matcher.
|
|
// Anything else produces a matcher that never holds — defensive but
|
|
// not panicking, since Matcher trees are often built dynamically.
|
|
func toMatcher(x any) Matcher {
|
|
switch v := x.(type) {
|
|
case string:
|
|
return permMatcher(v)
|
|
case Matcher:
|
|
return v
|
|
default:
|
|
return alwaysFalse{}
|
|
}
|
|
}
|
|
|
|
func toMatchers(xs []any) []Matcher {
|
|
out := make([]Matcher, len(xs))
|
|
for i, x := range xs {
|
|
out[i] = toMatcher(x)
|
|
}
|
|
return out
|
|
}
|
|
|
|
type alwaysFalse struct{}
|
|
|
|
func (alwaysFalse) matches(*Token) bool { return false }
|