dsl

package
v1.5.1 Latest Latest
Warning

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

Go to latest
Published: May 18, 2026 License: Apache-2.0 Imports: 22 Imported by: 0

Documentation

Overview

Package dsl implements the .warden declarative configuration language.

The language is purpose-built for warden's domain. Inspired by SpiceDB for resource types and permission expressions, with HCL-like blocks for roles and policies. One parser, one AST, one set of references — entity definitions, permission expressions, and ABAC conditions all share the same grammar.

See the project design doc and the canonical plan for the full grammar specification.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CompileExpr

func CompileExpr(file, src string) (Expr, []*Diagnostic)

CompileExpr parses a textual permission expression into an AST. Used by the engine to compile ResourceType.Permissions[].Expression at Check time (with caching).

func Export

func Export(ctx context.Context, eng *warden.Engine, opts ExportOptions) (int, error)

Export reads tenant state from the engine's store and writes .warden source to opts.OutputDir according to the chosen Layout. It returns the number of files written.

The output is canonical (passes through Format), so apply(export(state)) produces zero diff against the original state.

func Format

func Format(prog *Program) string

Format renders a Program back to canonical .warden source. The output is stable: parsing then formatting again yields the same bytes.

Canonical rules (see plan section D.1.a):

  • 4-space indent; LF line endings; final newline.
  • One blank line between top-level decls; two between section dividers.
  • Decls within a section sorted by name/slug for stability.
  • Within blocks, fields ordered (name, description, flags…, grants/when last).
  • Permission expressions rendered with parens elided where precedence permits.
  • Multi-line string lists when len > 3, inline otherwise.

func FormatBytes

func FormatBytes(prog *Program) []byte

FormatBytes is a byte-slice convenience wrapper around Format.

func FormatExpr

func FormatExpr(e Expr) string

FormatExpr renders an expression AST back to its canonical textual form. Used by the applier to store ResourceType.Permissions[].Expression.

func Load

func Load(arg string, opts ...LoadOption) (*Program, []*Diagnostic, error)

Load auto-detects whether arg is a file, directory, or glob pattern.

func LoadDir

func LoadDir(dir string, opts ...LoadOption) (*Program, []*Diagnostic, error)

LoadDir walks a directory tree and loads every .warden file under it. Hidden directories (those starting with `.`) are skipped; underscore- prefixed directories like `_shared/` are kept by convention.

func LoadFS

func LoadFS(fsys fs.FS, root string, opts ...LoadOption) (*Program, []*Diagnostic, error)

LoadFS reads .warden files from an fs.FS rooted at root. Useful for embedded configs (//go:embed).

func LoadFile

func LoadFile(path string, opts ...LoadOption) (*Program, []*Diagnostic, error)

LoadFile reads and parses a single .warden file.

func LoadFiles

func LoadFiles(paths []string, opts ...LoadOption) (*Program, []*Diagnostic, error)

LoadFiles reads, parses, and merges a fixed list of .warden files.

Cross-file conflicts (same role/permission/policy/resource type declared in multiple files) produce diagnostics on the merged Program. The order of files determines tie-breaking only for the scope (tenant/app) fields — the first file with a non-empty value wins, and any later file declaring a conflicting non-empty value yields a diagnostic.

func LoadGlob

func LoadGlob(pattern string, opts ...LoadOption) (*Program, []*Diagnostic, error)

LoadGlob loads every .warden file matching a glob pattern. Pattern semantics match filepath.Glob plus `**` for recursive matching (we don't expand `**` here — use LoadDir for that, or expand the pattern yourself with doublestar).

func Parse

func Parse(file string, src []byte) (*Program, []*Diagnostic)

Parse parses `.warden` source into a Program. Errors are accumulated (one per problem) and returned as a slice; a non-nil Program is returned even when errors occur, partially populated, so editor tooling can still surface useful information.

Types

type AndExpr

type AndExpr struct {
	Left, Right Expr
	Pos         Pos
}

AndExpr is set intersection (e.g. `editor and not banned`).

func (*AndExpr) Position

func (e *AndExpr) Position() Pos

type ApplyOptions

type ApplyOptions struct {
	// TenantID overrides Program.Tenant when non-empty.
	TenantID string
	// AppID overrides Program.App when non-empty.
	AppID string
	// DryRun, when true, plans the changes and returns the diff but writes
	// nothing to the store.
	DryRun bool
	// Prune, when true, deletes tenant entries (within the namespaces
	// covered by the program) that are not declared in the program.
	// kubectl-style apply with prune.
	Prune bool
	// Now is the time used for CreatedAt/UpdatedAt timestamps. Defaults to
	// time.Now().UTC().
	Now time.Time
}

ApplyOptions configures the DSL applier.

type ApplyResult

type ApplyResult struct {
	Created []string // human-readable summary lines: "+ kind/name"
	Updated []string // "~ kind/name (field: old → new)"
	Deleted []string // "- kind/name"
	NoOps   int      // count of unchanged entries
}

ApplyResult summarizes the outcome of an apply.

func Apply

func Apply(ctx context.Context, eng *warden.Engine, prog *Program, opts ApplyOptions) (*ApplyResult, error)

Apply materializes the program against the engine's store. It is idempotent — applying the same program twice produces the same state.

Tenant scope is optional. When neither opts.TenantID nor `tenant` in source is set, every entity is written with an empty `tenant_id` — the **global scope**. Single-tenant apps that never call `warden.WithTenant` see this as the natural default; multi-tenant apps should always pass a tenant explicitly to avoid accidentally landing entities in the global bucket.

func ApplyDir

func ApplyDir(ctx context.Context, eng *warden.Engine, dir string, opts ApplyOptions, loadOpts ...LoadOption) (*ApplyResult, error)

ApplyDir loads, validates, and applies every .warden file under dir. Hidden directories are skipped; underscore-prefixed directories (`_shared/`, etc.) are kept by convention.

Cross-file conflicts (duplicate roles, mismatched tenant) are reported as parse-time diagnostics and short-circuit Apply.

func ApplyFS

func ApplyFS(ctx context.Context, eng *warden.Engine, fsys fs.FS, root string, opts ApplyOptions, loadOpts ...LoadOption) (*ApplyResult, error)

ApplyFS loads, validates, and applies every .warden file under root in fsys. Designed for `//go:embed`: pass an embed.FS as fsys and the helper will walk it like a real filesystem.

//go:embed all:config
var configFS embed.FS

res, err := dsl.ApplyFS(ctx, eng, configFS, "config",
    dsl.ApplyOptions{TenantID: "acme"},
    dsl.WithVariables(dsl.Variables{"REGION": region}))

Note: include the `all:` prefix in the //go:embed directive when your config tree contains directories beginning with `_` (like `_shared`) — the default //go:embed pattern strips those, but Warden's underscore-prefixed convention expects them.

func ApplyFile

func ApplyFile(ctx context.Context, eng *warden.Engine, path string, opts ApplyOptions, loadOpts ...LoadOption) (*ApplyResult, error)

ApplyFile loads, validates, and applies a single .warden file against the engine. It is a convenience wrapper for the common Load → check → Apply sequence:

prog, parseErrs, err := dsl.LoadFile(path, loadOpts...)
// check err, parseErrs, then call dsl.Apply(ctx, eng, prog, opts)

Parse-time diagnostics short-circuit Apply and are returned as *DiagnosticError. Resolver diagnostics surface the same way (Apply emits them itself). Use errors.As to inspect the diagnostics list.

Pass dsl.WithVariables(...) via loadOpts to expand ${NAME} placeholders before parsing.

type Condition

type Condition struct {
	// Atomic predicate.
	Field    string
	Operator string
	Value    any
	Negate   bool

	// Boolean groups.
	AllOf []*Condition
	AnyOf []*Condition

	Pos Pos
}

Condition is a single ABAC predicate or boolean group.

Exactly one of {Field+Operator+Value, AllOf, AnyOf} is populated.

type Diagnostic

type Diagnostic struct {
	Pos Pos
	Msg string
}

Diagnostic is a parser/checker error with a source position and message.

func Resolve

func Resolve(prog *Program) []*Diagnostic

Resolve performs name resolution and type checking against a parsed program (or merged program from a multi-file load set). Returns any diagnostics found; the program may still be applied even with warnings.

Checks performed:

  • duplicate slugs / names within and across files
  • role parent slug resolves to a real role (local or ancestor namespace)
  • cycle detection in the role parent graph
  • resource-type permission expressions reference declared relations
  • traversal expressions hop through declared relation targets
  • condition operators are valid
  • identifier conventions (slug regex, name regex, namespace path)

func SubstituteVariables

func SubstituteVariables(file string, src []byte, vars Variables) ([]byte, []*Diagnostic)

SubstituteVariables expands `${NAME}` placeholders in src using vars. Each well-formed `${NAME}` reference is replaced with `vars[NAME]`.

Escape: a literal `$$` in the source emits a single `$` and skips substitution at that byte. So `$${VAR}` produces `${VAR}` in the output (useful for documentation strings that mention the syntax).

Diagnostics are produced for:

  • unclosed `${...` (no matching `}` before EOL or EOF)
  • invalid identifiers inside `${...}`
  • undefined variables (name not in vars)

Diagnostics carry the placeholder's source position so the LSP / `warden lint` can underline them precisely. The placeholder is left in the output untouched on diagnostic so downstream parsing still has bytes to work with — the parser's own errors then point at the resulting (still-broken) source.

Substitution is purely textual: placeholders inside string literals and inside line/block comments are substituted just like everywhere else. This keeps the function simple and the rules predictable — users who genuinely want a literal `${...}` write `$$` to escape.

func (*Diagnostic) Error

func (d *Diagnostic) Error() string

Error returns the textual diagnostic string for use as an error.

func (*Diagnostic) String

func (d *Diagnostic) String() string

type DiagnosticError

type DiagnosticError struct {
	Diags []*Diagnostic
}

DiagnosticError is the error type Apply / ApplyFile / ApplyDir / ApplyFS return when source-level errors prevent application. Use errors.As to extract the underlying diagnostics:

var derr *dsl.DiagnosticError
if errors.As(err, &derr) {
    for _, d := range derr.Diagnostics() {
        fmt.Println(d) // file:line:col: message
    }
}

The Error() representation joins every diagnostic with a newline so printing the error directly produces a multi-line list suitable for CI logs.

func (*DiagnosticError) Diagnostics

func (e *DiagnosticError) Diagnostics() []*Diagnostic

Diagnostics returns the underlying diagnostics for inspection.

func (*DiagnosticError) Error

func (e *DiagnosticError) Error() string

type EngineEvaluator

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

EngineEvaluator implements warden.ExpressionEvaluator by looking up the resource type's permission expression, compiling+caching it, and evaluating against the relation graph.

Wire it via:

ev := dsl.NewEngineEvaluator(store)
eng := warden.NewEngine(warden.WithStore(store), warden.WithExpressionEvaluator(ev))

func NewEngineEvaluator

func NewEngineEvaluator(s engineStore) *EngineEvaluator

NewEngineEvaluator constructs an evaluator backed by the engine's store. The store must implement both relation.Store and resourcetype.Store — every backend in this repo (memory, sqlite, postgres, mongo) satisfies that.

func (*EngineEvaluator) EvalPermission

func (e *EngineEvaluator) EvalPermission(
	ctx context.Context,
	tenantID, namespacePath, resourceType, permName, subjectKind, subjectID, resourceID string,
) (bool, error)

EvalPermission implements warden.ExpressionEvaluator.

Walks the namespace ancestor chain looking for a resource type with this name, then within that resource type's permissions for one named after the request's action. If found and the expression is non-empty, it's compiled (cached) and evaluated.

type EvalContext

type EvalContext struct {
	TenantID      string
	NamespacePath string

	ObjectType string
	ObjectID   string

	SubjectType string
	SubjectID   string

	// MaxDepth bounds traversal recursion. The engine sets this from its
	// configured graph walker depth.
	MaxDepth int
}

EvalContext is the runtime input to expression evaluation.

The evaluator walks an Expr AST against the relation graph to decide whether `subject` has the named permission on `resource`. It uses the store to look up tuples and recurses across traversal hops up to a bounded depth (taken from MaxDepth).

type Evaluator

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

Evaluator evaluates resource-type permission expressions.

func NewEvaluator

func NewEvaluator(relStore relation.Store) *Evaluator

NewEvaluator constructs an evaluator backed by the given relation store.

func (*Evaluator) CompileAndCache

func (e *Evaluator) CompileAndCache(tenantID, ns, resourceType, permName, exprSrc string) (Expr, []*Diagnostic)

CompileAndCache parses the expression for a resource-type permission and caches the result. Subsequent Eval calls reuse the AST.

func (*Evaluator) Eval

func (e *Evaluator) Eval(ctx context.Context, expr Expr, ec EvalContext) (bool, error)

Eval walks the expression AST and returns true iff the subject has the permission on the object given the relation tuples in store.

func (*Evaluator) Invalidate

func (e *Evaluator) Invalidate(tenantID, resourceType string)

Invalidate clears all cached expressions for a tenant/resource type. Engine calls this when ResourceType records are updated.

func (*Evaluator) WithPermResolver

func (e *Evaluator) WithPermResolver(r PermResolver) *Evaluator

WithPermResolver sets the permission resolver used during traversal to recursively evaluate permissions on hopped-to resource types.

type ExportOptions

type ExportOptions struct {
	// TenantID is required — the tenant whose state is exported.
	TenantID string
	// AppID is optional and propagates to the emitted header.
	AppID string
	// NamespacePrefix limits the export to entities whose namespace_path
	// equals or descends from this value. Empty means "all namespaces".
	NamespacePrefix string
	// Layout controls file layout (see Layout consts).
	Layout Layout
	// OutputDir is the destination root. Created if missing.
	OutputDir string
}

ExportOptions configures Export.

type Expr

type Expr interface {
	Position() Pos
	// contains filtered or unexported methods
}

Expr is the interface for permission-expression AST nodes.

type ImportDecl

type ImportDecl struct {
	Path string
	Pos  Pos
}

ImportDecl is a literal `import "<path>"` directive (reserved for the future explicit-import flow; the loader does not consume them today).

type Layout

type Layout int

Layout controls how `warden export` distributes a tenant's state across .warden files.

const (
	// FlatLayout writes everything to a single main.warden file.
	FlatLayout Layout = iota
	// SectionalLayout splits by entity kind: 00-resource-types.warden,
	// 10-permissions.warden, 20-roles.warden, 30-policies.warden,
	// 40-relations.warden.
	SectionalLayout
	// DomainLayout groups by namespace path. Each top-level segment becomes
	// a directory; entities at the tenant root go to a `_root/` directory.
	DomainLayout
)

type Lexer

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

Lexer is a hand-written tokenizer for `.warden` source.

Encoding is UTF-8; positions are tracked in 1-based line and byte column. The lexer never errors fatally — it returns ILLEGAL tokens for malformed input and lets the parser report richer diagnostics.

func NewLexer

func NewLexer(file string, src []byte) *Lexer

NewLexer constructs a fresh Lexer over the given source bytes.

func (*Lexer) Next

func (l *Lexer) Next() Token

Next returns the next token. After EOF is returned, every subsequent call keeps returning EOF.

type LoadOption

type LoadOption func(*loadConfig)

LoadOption configures a Load* call. Use the With* helpers below.

func WithVariables

func WithVariables(v Variables) LoadOption

WithVariables expands `${NAME}` placeholders in every loaded source file using the supplied map. Names match `[A-Za-z_][A-Za-z0-9_]*`. Substitution is purely textual; see SubstituteVariables for the full rules. Pass nil or an empty map to disable substitution (the default).

type NamespaceDecl

type NamespaceDecl struct {
	// Name is the path segment, not the absolute path.
	Name string

	// Children are the decls declared directly inside this block. After
	// flattening, the absolute namespace path of each is recorded on the
	// decl itself; this field is kept for reference / formatter use.
	ResourceTypes []*ResourceDecl
	Permissions   []*PermissionDecl
	Roles         []*RoleDecl
	Policies      []*PolicyDecl
	Relations     []*RelationDecl
	Namespaces    []*NamespaceDecl

	Pos Pos
}

NamespaceDecl wraps a nested `namespace "name" { ... }` block. The parser flattens nested namespaces into per-decl NamespacePath strings, but it also keeps the original NamespaceDecl tree around for fmt-friendly re-emission.

type NotExpr

type NotExpr struct {
	Inner Expr
	Pos   Pos
}

NotExpr is set complement (e.g. `not banned`).

func (*NotExpr) Position

func (e *NotExpr) Position() Pos

type OrExpr

type OrExpr struct {
	Left, Right Expr
	Pos         Pos
}

OrExpr is set union (e.g. `viewer or editor`).

func (*OrExpr) Position

func (e *OrExpr) Position() Pos

type PermResolver

type PermResolver func(ctx context.Context, tenantID, namespacePath, resourceType, permName string) (Expr, bool)

PermResolver returns the compiled permission expression for a resource type's named permission, if one exists. Used by the traversal walker to recursively evaluate permissions across resource-type hops (e.g. `parent->read` where `read` is itself a permission expression on the hopped resource type).

Returns (nil, false) when no expression is defined for that combination.

type PermissionDecl

type PermissionDecl struct {
	Name          string // the literal "resource:action" string
	NamespacePath string
	Resource      string // parsed from Name or set explicitly
	Action        string // parsed from Name or set explicitly
	Description   string
	IsSystem      bool
	Pos           Pos
}

PermissionDecl is a top-level `permission "<name>" (...)` or `{...}` block.

type PolicyDecl

type PolicyDecl struct {
	Name          string
	NamespacePath string
	Description   string
	Effect        string // "allow" | "deny"
	Priority      int
	Active        bool
	NotBefore     *time.Time
	NotAfter      *time.Time
	Obligations   []string
	Actions       []string
	Resources     []string
	Conditions    []*Condition
	Pos           Pos
}

PolicyDecl is a top-level `policy "<name>" { ... }` block.

PBAC fields:

  • NotBefore / NotAfter: optional RFC3339 instants bounding when the policy is effective. Either may be nil ("no bound on that side").
  • Obligations: list of named side-effect actions emitted when the policy matches (e.g. "audit-log", "require-mfa").

type Pos

type Pos struct {
	File string
	Line int // 1-based
	Col  int // 1-based, byte column
}

Pos is the source position of a token or AST node.

func (Pos) String

func (p Pos) String() string

String formats the position for error messages.

type Program

type Program struct {
	// File this program was parsed from. For multi-file load sets, this is
	// the root path (the load entrypoint).
	File string

	// Header values.
	Version int
	Tenant  string // optional; resolved at apply time
	App     string // optional

	// Top-level declarations.
	ResourceTypes []*ResourceDecl
	Permissions   []*PermissionDecl
	Roles         []*RoleDecl
	Policies      []*PolicyDecl
	Relations     []*RelationDecl
	Imports       []*ImportDecl
	Namespaces    []*NamespaceDecl

	// HeaderPos is the position of the `warden config N` line.
	HeaderPos Pos
}

Program is the root AST node — one parsed `.warden` file or set of files.

All decls within a program share the same logical namespace; cross-file references are resolved against the merged Program.

func BuildProgram

func BuildProgram(ctx context.Context, eng *warden.Engine, opts ExportOptions) (*Program, error)

BuildProgram reads tenant state and constructs an in-memory Program. Useful for tests that want to round-trip without writing to disk, and for custom emitters.

type RefExpr

type RefExpr struct {
	Name string
	Pos  Pos
}

RefExpr references a relation by name. Used as the leaf of expressions inside a resource type ("viewer", "editor").

func (*RefExpr) Position

func (e *RefExpr) Position() Pos

type RelationDecl

type RelationDecl struct {
	NamespacePath   string
	ObjectType      string
	ObjectID        string
	Relation        string
	SubjectType     string
	SubjectID       string
	SubjectRelation string
	Pos             Pos
}

RelationDecl is a top-level `relation <obj_type>:<obj_id> <rel> = <subj_type>:<subj_id>[#<subj_rel>]`.

type RelationDef

type RelationDef struct {
	Name            string
	AllowedSubjects []SubjectType
	Pos             Pos
}

RelationDef is a `relation <name>: <subj> | <subj>#<rel> | ...` line inside a resource block.

type ResourceDecl

type ResourceDecl struct {
	Name          string
	NamespacePath string // absolute path, empty = tenant root
	Description   string
	Relations     []*RelationDef
	Permissions   []*ResourcePermissionDecl
	Pos           Pos
}

ResourceDecl is a `resource <name> { ... }` block.

type ResourcePermissionDecl

type ResourcePermissionDecl struct {
	Name string
	Expr Expr
	Pos  Pos
}

ResourcePermissionDecl is a `permission <name> = <expr>` inside a resource block.

type RoleDecl

type RoleDecl struct {
	Slug          string
	NamespacePath string
	Parent        string // raw — may be a local slug or absolute path starting with "/"
	Name          string
	Description   string
	IsSystem      bool
	IsDefault     bool
	MaxMembers    int
	Grants        []string // permission names; possibly globs
	GrantsAppend  bool     // true if `grants += [...]`
	Pos           Pos
}

RoleDecl is a `role <slug> [: <parent>] { ... }` block.

type SubjectType

type SubjectType struct {
	Type     string
	Relation string
	Pos      Pos
}

SubjectType is one entry in a relation's allowed_subjects list. Relation, when non-empty, restricts to a relation-set (`group#member`).

type Token

type Token struct {
	Kind  TokenKind
	Value string // raw lexeme; for STRING this is the unescaped string literal
	Pos   Pos
}

Token represents a single lexed token.

type TokenKind

type TokenKind int

TokenKind enumerates lexical token categories.

const (
	// EOF marks end-of-input.
	EOF TokenKind = iota

	// ILLEGAL is an unrecognized character or unterminated literal.
	ILLEGAL

	// IDENT is a lowercase identifier — `[a-z_][a-z0-9_-]*`.
	IDENT

	// STRING is a double-quoted string literal with the value already
	// unescaped (\\, \", \n, \t).
	STRING

	// INT is an unsigned integer literal — `[0-9]+`.
	INT

	// BOOL is `true` or `false`.
	BOOL

	// Single-character punctuation.
	LBRACE   // {
	RBRACE   // }
	LPAREN   // (
	RPAREN   // )
	LBRACKET // [
	RBRACKET // ]
	COMMA    // ,
	COLON    // :
	SEMI     // ;
	DOT      // .
	PIPE     // |
	HASH     // #
	BANG     // !
	SLASH    // /  (used for absolute namespace paths in role parent refs)

	// Operators.
	ASSIGN // =
	APPEND // +=
	ARROW  // ->
	EQ     // ==
	NE     // !=
	LT     // <
	GT     // >
	LE     // <=
	GE     // >=
	PLUS   // +  (also used as union operator on expressions)
	MINUS  // -  (also unary negation)
	AMP    // &  (also intersection on expressions)
	REGEX  // =~

	// Keywords. Lexer picks these out of the IDENT stream.
	WARDEN      // warden
	CONFIG      // config
	TENANT      // tenant
	APP         // app
	NAMESPACE   // namespace
	IMPORT      // import
	RESOURCE    // resource
	RELATION    // relation
	PERMISSION  // permission
	ROLE        // role
	POLICY      // policy
	EFFECT      // effect
	ALLOW       // allow
	DENY        // deny
	ACTIONS     // actions
	RESOURCES   // resources
	SUBJECTS    // subjects
	WHEN        // when
	NEGATE      // negate
	GRANTS      // grants
	NAME        // name
	DESCRIPTION // description
	PRIORITY    // priority
	ACTIVE      // active
	IS_SYSTEM   //nolint:revive // matches DSL keyword spelling
	IS_DEFAULT  //nolint:revive // matches DSL keyword spelling
	MAX_MEMBERS //nolint:revive // matches DSL keyword spelling
	METADATA    // metadata
	OR          // or
	AND         // and
	NOT         // not
	IN          // in
	CONTAINS    // contains
	STARTS_WITH //nolint:revive // matches DSL keyword spelling
	ENDS_WITH   //nolint:revive // matches DSL keyword spelling
	EXISTS      // exists
	IP_IN_CIDR  //nolint:revive // matches DSL keyword spelling
	TIME_AFTER  //nolint:revive // matches DSL keyword spelling
	TIME_BEFORE //nolint:revive // matches DSL keyword spelling
	ALL_OF      //nolint:revive // matches DSL keyword spelling
	ANY_OF      //nolint:revive // matches DSL keyword spelling
	NOT_BEFORE  //nolint:revive // matches DSL keyword spelling
	NOT_AFTER   //nolint:revive // matches DSL keyword spelling
	OBLIGATIONS // obligations (PBAC: named side-effect actions)
)

func (TokenKind) String

func (k TokenKind) String() string

String renders a token kind for diagnostics.

type TraverseExpr

type TraverseExpr struct {
	// Steps holds at least 2 names: the first is a relation on the current
	// resource type, subsequent names are relations or permissions on the
	// target type after each hop.
	Steps []string
	Pos   Pos
}

TraverseExpr is `<rel>->...->target` — walk one or more relation hops and then check the final permission/relation on the resulting object.

func (*TraverseExpr) Position

func (e *TraverseExpr) Position() Pos

type Variables

type Variables map[string]string

Variables is a name → value map of template variables expanded during loading. Names match the regex `[A-Za-z_][A-Za-z0-9_]*`. Values are inserted verbatim — substitution is purely textual, including inside string literals and comments.

Use cases: per-environment tenant IDs, namespace prefixes, region tags, or any other field that varies between deployments without changing the rest of the config.

func EnvVariables

func EnvVariables() Variables

EnvVariables collects every os.Environ() entry whose name starts with "WARDEN_VAR_" and exposes them under the suffix as a Variables map. Example: `WARDEN_VAR_TENANT=acme` becomes `${TENANT}` in source.

Use this to thread CI-injected values through to the loader without per-flag plumbing. Returns an empty map (never nil) if nothing matches.

func MergeVariables

func MergeVariables(layers ...Variables) Variables

MergeVariables returns a new Variables map composed of every layer. Later layers override earlier ones, so the typical call order is (defaults, env, cli) — CLI flags win over env, env wins over defaults. Nil layers are skipped. Empty-string values are treated as set.

Jump to

Keyboard shortcuts

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