Documentation
¶
Overview ¶
typical (TYPed predICAte Library) is a library for building better predicate expression parsers faster. It is built on top of predicate(github.com/gravitational/predicate).
typical helps you (and forces you) to separate parse and evaluation into two distinct stages so that parsing can be cached and evaluation can be fast. Expressions can also be parsed to check their syntax without needing to evaluate them with some specific input.
Functions can be defined by providing implementations accepting and returning values of ordinary types, the library handles the details that enable any function argument to be a subexpression that evaluates to the correct type.
By default, typical provides strong type checking at parse time without any reflection during evaluation. If you define a function that evaluates to type any, you are opting in to evaluation-time type checking everywhere that function is called, but types will still be checked for all other parts of the expression. If you define a function that accepts an interface type it will be called via reflection during evaluation, but interface satisfaction is still checked at parse time.
Example ¶
/* * Teleport * Copyright (C) 2023 Gravitational, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package main import ( "fmt" "slices" "github.com/gravitational/teleport/lib/utils/typical" ) type expressionEnv struct { traits map[string][]string labels func(key string) string } func main() { parser, err := typical.NewParser[expressionEnv, bool](typical.ParserSpec{ Variables: map[string]typical.Variable{ "traits": typical.DynamicVariable(func(e expressionEnv) (map[string][]string, error) { return e.traits, nil }), "labels": typical.DynamicMapFunction(func(e expressionEnv, key string) (string, error) { return e.labels(key), nil }), "true": true, "false": false, }, Functions: map[string]typical.Function{ "contains": typical.BinaryFunction[expressionEnv](func(list []string, item string) (bool, error) { return slices.Contains(list, item), nil }), "contains_all": typical.BinaryVariadicFunction[expressionEnv](func(list []string, strs ...string) (bool, error) { for _, str := range strs { if !slices.Contains(list, str) { return false, nil } } return true, nil }), }, }) if err != nil { fmt.Println(err) return } env := expressionEnv{ traits: map[string][]string{ "groups": {"devs", "security"}, }, labels: func(key string) string { if key == "owner" { return "devs" } return "" }, } for _, expr := range []string{ `contains(traits["groups"], labels["owner"])`, `contains_all(traits["groups"], "devs", "admins")`, `contains(traits["groups"], false)`, } { parsed, err := parser.Parse(expr) if err != nil { fmt.Println("parse error:", err) continue } match, err := parsed.Evaluate(env) if err != nil { fmt.Println("evaluation error:", err) continue } fmt.Println(match) } }
Output: true false parse error: parsing expression parsing second argument to (contains) expected type string, got value (false) with type (bool)
Index ¶
- type CachedParser
- type Expression
- type Function
- func BinaryFunction[TEnv, TArg1, TArg2, TResult any](impl func(TArg1, TArg2) (TResult, error)) Function
- func BinaryVariadicFunction[TEnv, TArg1, TVarArgs, TResult any](impl func(TArg1, ...TVarArgs) (TResult, error)) Function
- func TernaryFunction[TEnv, TArg1, TArg2, TArg3, TResult any](impl func(TArg1, TArg2, TArg3) (TResult, error)) Function
- func TernaryVariadicFunction[TEnv, TArg1, Targ2, TVarArgs, TResult any](impl func(TArg1, Targ2, ...TVarArgs) (TResult, error)) Function
- func UnaryFunction[TEnv, TArg, TResult any](impl func(TArg) (TResult, error)) Function
- func UnaryFunctionWithEnv[TEnv, TArg, TResult any](impl func(TEnv, TArg) (TResult, error)) Function
- func UnaryVariadicFunction[TEnv, TVarArgs, TResult any](impl func(...TVarArgs) (TResult, error)) Function
- type Getter
- type LiteralExpr
- type Parser
- type ParserOption
- type ParserSpec
- type UnknownIdentifierError
- type Variable
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type CachedParser ¶
type CachedParser[TEnv, TResult any] struct { Parser[TEnv, TResult] // contains filtered or unexported fields }
CachedParser is a Parser that caches each parsed expression.
func NewCachedParser ¶
func NewCachedParser[TEnv, TResult any](spec ParserSpec, opts ...ParserOption) (*CachedParser[TEnv, TResult], error)
NewCachedParser creates a cached predicate expression parser with the given specification.
func (*CachedParser[TEnv, TResult]) Parse ¶
func (c *CachedParser[TEnv, TResult]) Parse(expression string) (Expression[TEnv, TResult], error)
Parse checks if [expression] is already present in the cache and returns the cached version if present, or else parses the expression to produce an Expression[TEnv, TResult] which is stored in the cache and returned.
type Expression ¶
type Expression[TEnv, TResult any] interface { // Evaluate evaluates the already parsed predicate expression with the given // environment. Evaluate(environment TEnv) (TResult, error) }
Expression is a generic interface representing a parsed predicate expression which can be evaluated with environment type TEnv to produce a result of type TResult.
type Function ¶
type Function interface {
// contains filtered or unexported methods
}
Function holds the definition of a function. It is expected to be the result of calling one of (Unary|Binary|Ternary)(Variadic)?Function.
func BinaryFunction ¶
func BinaryFunction[TEnv, TArg1, TArg2, TResult any](impl func(TArg1, TArg2) (TResult, error)) Function
BinaryFunction returns a definition for a function that can be called with two arguments. The arguments may be literals or subexpressions.
func BinaryVariadicFunction ¶
func BinaryVariadicFunction[TEnv, TArg1, TVarArgs, TResult any](impl func(TArg1, ...TVarArgs) (TResult, error)) Function
BinaryVariadicFunction returns a definition for a function that can be called with one or more arguments. The arguments may be literals or subexpressions.
func TernaryFunction ¶
func TernaryFunction[TEnv, TArg1, TArg2, TArg3, TResult any](impl func(TArg1, TArg2, TArg3) (TResult, error)) Function
TernaryFunction returns a definition for a function that can be called with three arguments. The arguments may be literals or subexpressions.
func TernaryVariadicFunction ¶
func TernaryVariadicFunction[TEnv, TArg1, Targ2, TVarArgs, TResult any](impl func(TArg1, Targ2, ...TVarArgs) (TResult, error)) Function
TernaryVariadicFunction returns a definition for a function that can be called with one or more arguments. The arguments may be literals or subexpressions.
func UnaryFunction ¶
UnaryFunction returns a definition for a function that can be called with one argument. The argument may be a literal or a subexpression.
func UnaryFunctionWithEnv ¶
UnaryFunctionWithEnv returns a definition for a function that can be called with one argument. The argument may be a literal or a subexpression. The [impl] will be called with the evaluation env as the first argument, followed by the actual argument passed in the expression.
func UnaryVariadicFunction ¶
func UnaryVariadicFunction[TEnv, TVarArgs, TResult any](impl func(...TVarArgs) (TResult, error)) Function
UnaryVariadicFunction returns a definition for a function that can be called with any number of arguments of a single type. The arguments may be literals or subexpressions.
type Getter ¶
Getter is a generic interface for map-like values that allow you to Get a TValues by key
type LiteralExpr ¶
type LiteralExpr[TEnv, T any] struct { Value T }
func (LiteralExpr[TEnv, T]) Evaluate ¶
func (l LiteralExpr[TEnv, T]) Evaluate(TEnv) (T, error)
type Parser ¶
type Parser[TEnv, TResult any] struct { // contains filtered or unexported fields }
Parser is a predicate expression parser configured to parse expressions of a specific expression language.
func NewParser ¶
func NewParser[TEnv, TResult any](spec ParserSpec, opts ...ParserOption) (*Parser[TEnv, TResult], error)
NewParser creates a predicate expression parser with the given specification.
func (*Parser[TEnv, TResult]) Parse ¶
func (p *Parser[TEnv, TResult]) Parse(expression string) (Expression[TEnv, TResult], error)
Parse parses the given expression string to produce an Expression[TEnv, TResult]. The returned Expression can be safely cached and called multiple times with different TEnv inputs.
type ParserOption ¶
type ParserOption func(*parserOptions)
ParserOption is an optional option for configuring a Parser.
func WithInvalidNamespaceHack ¶
func WithInvalidNamespaceHack() ParserOption
WithInvalidNamespaceHack is necessary because old parser versions accidentally allowed "<anything>.trait" as an alias for "external.trait". Some people wrote typos and now we have to maintain this. See https://github.com/gravitational/teleport/pull/21551
type ParserSpec ¶
type ParserSpec struct { // Variables defines all of the literals and variables available to // expressions in the predicate language. It is a map from variable name to // definition. Variables map[string]Variable // Functions defines all of the functions available to expressions in the // predicate language. Functions map[string]Function // Methods defines all of the methods available to expressions in the // predicate language. Methods are just functions that take their receiver // as their first argument. Methods map[string]Function }
ParserSpec defines a predicate language.
type UnknownIdentifierError ¶
type UnknownIdentifierError string
UnknownIdentifierError is an error type that can be used to identify errors related to an unknown identifier in an expression.
func (UnknownIdentifierError) Error ¶
func (u UnknownIdentifierError) Error() string
func (UnknownIdentifierError) Identifier ¶
func (u UnknownIdentifierError) Identifier() string
type Variable ¶
type Variable any
Variable holds the definition of a literal or variable. It is expected to be either a literal static value, or the result of calling DynamicVariable or DynamicMap.
func DynamicMap ¶
func DynamicMap[TEnv any, TValues any, TMap Getter[TValues]](accessor func(TEnv) (TMap, error)) Variable
DynamicMap returns a definition for a variable that can be indexed with map[key] or map.key syntax to get a TValues, or passed directly to another function as TMap. TMap must implement Getter[TValues]. Each time the variable is reference in an expression, [accessor] will be called to retrieve the Getter.
func DynamicMapFunction ¶
func DynamicMapFunction[TEnv, TValues any](getFunc func(env TEnv, key string) (TValues, error)) Variable
DynamicMapFunction returns a definition for a variable that can be indexed with map[key] or map.key syntax to get a TValues. Each time the variable is indexed in an expression, [getFunc] will be called to retrieve the value.
func DynamicVariable ¶
DynamicVariable returns a normal variable definition. Whenever the variable is accessed during evaluation of an expression, accessor will be called to fetch the value of the variable from the current evaluation environment.