A3S Docs
A3S ACL

Rust API

Rust bounded parsing, schema admission, multi-diagnostics, schema-aware canonical digests, native ACL generation, AST, and builders for a3s-acl.

Rust API

Install the Rust crate:

[dependencies]
a3s-acl = "0.3.0"

Parse And Generate

use a3s_acl::{generate, parse};

let doc = parse(r#"
default_model = "provider/model-id"

providers "provider" {
  api_key = env("PROVIDER_API_KEY")
}
"#)?;

let text = generate(&doc);
# Ok::<(), a3s_acl::ParseError>(())

The public crate exports:

APIRole
parse / parse_aclParse ACL text into a Document.
parse_with_limits, ParseLimitsParse untrusted input with explicit document, nesting, collection, token, and diagnostic limits.
collect_diagnostics, collect_diagnostics_with_limitsCollect bounded diagnostics without constructing a partial AST.
DiagnosticCode, ParseError, DiagnosticReportReturn stable codes, UTF-8 byte spans, redacted messages, and explicit truncation state.
validate_document, validate_document_with_limitsValidate a parsed Document against a declarative schema with a bounded report.
Schema, AttributeSchema, BlockSchema, ValueSchemaDefine closed-by-default document, block, and recursive value rules.
SchemaDiagnosticCode, SchemaDiagnostic, SchemaReportReturn stable schema codes, logical paths, redacted messages, and explicit truncation state.
generate / generate_aclGenerate ACL text from a Document.
generate_from_mapBuild ACL text from a HashMap<String, Value>.
canonical_bytes, canonical_digestProduce stable cross-SDK bytes and an algorithm-prefixed SHA-256 digest.
canonical_bytes_with_schema, canonical_digest_with_schemaRecursively normalize schema-declared unordered block occurrences before producing bytes or a digest.
CanonicalErrorReject unsupported programmatic values without echoing them.
Lexer / ParserAdvanced tokenization and parsing entry points.

Bounded Admission

use a3s_acl::{parse_with_limits, ParseLimits};

let input = "limits { max_tasks = 10 }";
let document = parse_with_limits(
    input,
    ParseLimits {
        max_document_bytes: 64 * 1024,
        max_nesting_depth: 32,
        max_collection_items: 1_000,
        max_token_bytes: 16 * 1024,
        max_diagnostics: 20,
    },
)?;

Document and token sizes count UTF-8 bytes. ParseError exposes a stable DiagnosticCode, a complete source span with UTF-8 byte offsets, and compatibility line / column fields. Messages identify token kinds without including source token values or snippets.

The parse APIs remain fail-fast. CLI and editor integrations can collect multiple errors with a bounded budget:

use a3s_acl::{collect_diagnostics_with_limits, ParseLimits};

let report = collect_diagnostics_with_limits(
    "first = ]\nsecond = ]",
    ParseLimits {
        max_diagnostics: 20,
        ..ParseLimits::default()
    },
);

After a syntax error, collection resumes at the next source line. Resource-limit diagnostics remain fatal. A report stores at most max_diagnostics errors and sets truncated only after observing another error beyond that budget.

Schema Admission

Parse untrusted text with explicit limits, then validate the resulting document before activating it:

use a3s_acl::{
    parse_with_limits, validate_document_with_limits, AttributeSchema,
    ParseLimits, Schema, ValueSchema,
};

let limits = ParseLimits {
    max_diagnostics: 20,
    ..ParseLimits::default()
};
let document = parse_with_limits("version = 1", limits)?;
let schema = Schema::new().attribute(
    "version",
    AttributeSchema::required(ValueSchema::number()),
);
let report = validate_document_with_limits(&document, &schema, limits);
assert!(report.is_empty());

Schemas reject undeclared attributes, blocks, and object fields by default. They can declare required or optional attributes, block occurrence and label cardinalities, and recursive Any, String, Number, Bool, Null, List, Object, Call, and OneOf rules. Extension points require the corresponding explicit allow flag. BlockSchema::unordered(true) marks matching occurrences as semantically unordered for schema-aware canonicalization; it does not change validation.

Schema reports use stable acl.schema.* codes and logical paths such as $.blocks.provider[0].attributes.api_key. Messages and paths never include attribute values, call arguments, or block labels. The report uses ParseLimits::max_diagnostics and sets truncated only after observing an additional schema error beyond that budget.

Invalid cardinality ranges and empty unions return SchemaDefinitionError while constructing the trusted schema. Schema admission validates document shape; host components still own numeric ranges, secret resolution, provider credentials, and other product semantics.

AST

TypeFields
Documentblocks: Vec<Block>
Blockname, labels, blocks, attributes
ValueString, Number, Bool, List, Object, Null, Call

Value also exposes helpers such as as_str, as_number, as_bool, is_null, and is_string.

Builders

use a3s_acl::builder::{boolean, call, string, BlockBuilder, DocumentBuilder};

let provider = BlockBuilder::new("providers")
    .label("provider")
    .attr("api_key", call("env", vec![string("PROVIDER_API_KEY")]))
    .attr("enabled", boolean(true))
    .build();

let doc = DocumentBuilder::new().block(provider).build();

Builder helpers include string, number, integer, boolean, null, list, and call.

Native Generation

use a3s_acl::{Generator, GeneratorConfig};

let generator = Generator::with_config(GeneratorConfig {
    indent: "    ",
    comments: false,
});
let text = generator.generate(&doc);

Generation has one native ACL contract: block labels remain in block headers. There is no label-as-attribute compatibility mode.

Canonical Bytes And Digests

use a3s_acl::{
    canonical_bytes, canonical_bytes_with_schema, canonical_digest_with_schema,
    parse, validate_document, BlockSchema, Cardinality, Schema,
};

let document = parse(
    "provider \"z\" {}\nprovider \"a\" {}",
)?;
let schema = Schema::new().block(
    "provider",
    BlockSchema::new(Schema::new())
        .labels(Cardinality::exactly(1))
        .unordered(true),
);
assert!(validate_document(&document, &schema).is_empty());

let ordered_bytes = canonical_bytes(&document)?;
let normalized_bytes = canonical_bytes_with_schema(&document, &schema)?;
let digest = canonical_digest_with_schema(&document, &schema)?;
assert_ne!(ordered_bytes, normalized_bytes);
assert!(digest.starts_with("sha256:"));

Use canonical APIs for signatures, durable identity, and cross-SDK comparisons. Canonical bytes preserve ordered ACL constructs while sorting semantically unordered attribute and object keys. After schema admission, the schema-aware APIs additionally sort occurrences of each same declared block name whose rule sets unordered. Normalization is recursive and compares canonical UTF-8 bytes, but retains the occurrences' existing positions, so other block types and unknown blocks do not move. Labels, lists, calls, and unmarked blocks remain ordered.

Schema-aware canonicalization does not perform schema admission. Validate the document separately before using a trusted schema as normalization metadata.

Notes

  • Generated attributes are sorted by key for stable output.
  • Strings are escaped for quotes, backslashes, newlines, carriage returns, and tabs.
  • ACL parsing/generation does not resolve secrets or evaluate function calls.

On this page