predicate

package
v0.3.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 10, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package predicate parses and evaluates a subset of NSPredicate syntax for declarative device management activations.

Design

The engine validates activation predicates at upload, and the simulator evaluates them against @property values and @status items. Unsupported forms return an explicit error. Membership resolution determines which declarations a device sees; predicate evaluation determines whether an activation applies its configurations.

# Grammar Keywords and operators are case-insensitive. Whitespace between tokens is insignificant.

predicate   := or
or          := and { ("OR" | "||") and }
and         := not { ("AND" | "&&") not }
not         := ("NOT" | "!") not | primary
primary     := "(" or ")" | "TRUEPREDICATE" | "FALSEPREDICATE" | comparison
comparison  := operand [ "[c]" ] op [ "[c]" ] operand
operand     := "@property" "(" key ")" | "@status" "(" path ")" | literal
literal     := string | number | "TRUE" | "FALSE" | "YES" | "NO"
             | "NULL" | "NIL" | "{" [ literal { "," literal } ] "}"
op          := "==" | "=" | "!=" | "<>" | "<" | "<=" | ">" | ">="
             | "IN" | "CONTAINS" | "BEGINSWITH" | "ENDSWITH"
key, path   := [A-Za-z0-9_.-]+ | string
string      := ( "'" ... "'" | '"' ... '"' ) with escapes \\ \' \" \n \r \t \uXXXX
number      := [+-] digits [ "." digits ] [ ("e" | "E") [+-] digits ]

The [c] modifier requests case-insensitive string comparison. Apple writes it after the operator (`==[c]`); the modifier is also accepted immediately before the operator. It is permitted only on ==, !=, IN, CONTAINS, BEGINSWITH and ENDSWITH.

Unsupported constructs

The following NSPredicate features are recognised and rejected with a *SyntaxError whose Err is ErrUnsupported and whose Msg names the construct: SELF, bare key paths outside @property or @status, %K and %@ format arguments, $variables, MATCHES, LIKE, BETWEEN, ANY, ALL, NONE, SOME, SUBQUERY, FUNCTION and any identifier followed by "(", arithmetic operators, the [d], [cd] and [n] modifiers, and CAST.

Evaluation

A missing property or status item evaluates to nil. The == and != operators compare nil like any other value, so nil == nil is true. Ordering, IN, CONTAINS, BEGINSWITH and ENDSWITH involving nil are false. Integers and floats from the environment promote to float64, strings compare lexically, and booleans support only == and !=. Mixing a string with a number, or a boolean with anything else, is a type mismatch and Eval returns an error wrapping ErrType. IN requires an aggregate on its right-hand side, either a `{...}` literal or a slice supplied by the environment. CONTAINS, BEGINSWITH and ENDSWITH require strings on both sides. TRUEPREDICATE and FALSEPREDICATE are constants.

References

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrSyntax      = errors.New("predicate: syntax error")
	ErrUnsupported = errors.New("predicate: unsupported construct")
	ErrType        = errors.New("predicate: type mismatch")
)

Sentinel errors. Errors returned by Parse and Validate wrap ErrSyntax or ErrUnsupported and are always of type *SyntaxError. Errors returned by Eval wrap ErrType when operand types are incompatible.

Functions

func Validate

func Validate(s string) error

Validate reports whether s is a predicate this package can evaluate.

Types

type Env

type Env interface {
	// Property returns the activation property for key.
	Property(key string) (any, bool)
	// Status returns the status item at path.
	Status(path string) (any, bool)
}

Env supplies the values that @property and @status references resolve to. A reference whose lookup reports false evaluates to nil. Values may be nil, bool, string, any integer or float type, json.Number, or a slice of those.

type MapEnv

type MapEnv struct {
	// Properties resolves @property(key).
	Properties map[string]any
	// StatusItems resolves @status(path).
	StatusItems map[string]any
}

MapEnv is an Env backed by two maps. A nil map simply has no entries.

func (MapEnv) Property

func (m MapEnv) Property(key string) (any, bool)

Property implements Env.

func (MapEnv) Status

func (m MapEnv) Status(path string) (any, bool)

Status implements Env.

type Predicate

type Predicate struct {
	// contains filtered or unexported fields
}

Predicate is a parsed activation predicate.

func MustParse

func MustParse(s string) *Predicate

MustParse is like Parse but panics on error. It is intended for predicates fixed at compile time.

func Parse

func Parse(s string) (*Predicate, error)

Parse parses a predicate format string. The returned error wraps ErrSyntax or ErrUnsupported and is of type *SyntaxError.

Example
package main

import (
	"fmt"

	"github.com/deploymenttheory/go-apple-dm/mdmprotocol/ddm/predicate"
)

func main() {
	p, err := predicate.Parse(
		`(@property(shard) <= 75) and @status(device.identifier.serial-number) beginswith 'ZYXW'`,
	)
	if err != nil {
		fmt.Println("parse:", err)
		return
	}
	env := predicate.MapEnv{
		Properties:  map[string]any{"shard": 40},
		StatusItems: map[string]any{"device.identifier.serial-number": "ZYXW4321"},
	}
	ok, err := p.Eval(env)
	fmt.Println(p)
	fmt.Println(ok, err)

	_, err = predicate.Parse(`SELF.name LIKE 'a*'`)
	fmt.Println(err)
}
Output:
@property(shard) <= 75 AND @status(device.identifier.serial-number) BEGINSWITH 'ZYXW'
true <nil>
predicate: unsupported construct at offset 0: SELF is not supported

func (*Predicate) Eval

func (p *Predicate) Eval(env Env) (bool, error)

Eval evaluates the predicate against env. A nil env resolves every reference to nil. Errors wrap ErrType.

func (*Predicate) Source

func (p *Predicate) Source() string

Source returns the string the predicate was parsed from.

func (*Predicate) String

func (p *Predicate) String() string

String renders the predicate canonically: upper-case keywords, single quoted strings, symbolic operators in their primary spelling and parentheses only where precedence requires them. Parsing the result yields a predicate equal to p.

type Properties

type Properties map[string]any

Properties is an Env backed by a map of activation properties. Status lookups are never found.

func (Properties) Property

func (p Properties) Property(key string) (any, bool)

Property implements Env.

func (Properties) Status

func (Properties) Status(string) (any, bool)

Status implements Env and always reports a missing item.

type SyntaxError

type SyntaxError struct {
	// Offset is the byte offset in the source at which the problem was found.
	Offset int
	// Msg describes the problem and, for unsupported constructs, names the
	// construct.
	Msg string
	// Err is ErrSyntax or ErrUnsupported.
	Err error
}

SyntaxError describes why a predicate failed to parse.

func (*SyntaxError) Error

func (e *SyntaxError) Error() string

Error implements error.

func (*SyntaxError) Unwrap

func (e *SyntaxError) Unwrap() error

Unwrap returns the sentinel error.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL