add token type with permission checks and composable matchers
This commit is contained in:
parent
5288df0b9e
commit
8b5fcc87c3
4 changed files with 499 additions and 0 deletions
92
matcher.go
Normal file
92
matcher.go
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
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 }
|
||||
Loading…
Add table
Add a link
Reference in a new issue