# policy_engine

Rust implementation of the policy engine specified under [`docs/`](docs/).

## Scope

This crate implements the compiler and evaluator for the policy template
language defined in [`docs/LANGUAGE.md`](docs/LANGUAGE.md), the API defined
in [`docs/API.md`](docs/API.md), and the CLI entrypoint `policy-engine`
described in [`docs/CLI.md`](docs/CLI.md). Testing conventions are covered in
[`docs/TESTING.md`](docs/TESTING.md).

## API surface

The public library API consists of two free functions and the types they
take/return:

* `compile_source(&str) -> Result<PolicyTemplate, CompileError>`
* `evaluate_policy(&PolicyTemplate, &InputMap, &InputMap) -> Result<(), EvaluationError>`

Bytecode round-tripping and hashing (base58 SHA-256) live in
[`src/bytecode.rs`](src/bytecode.rs); the pretty-printer used by `policy-engine print` lives in
[`src/readable.rs`](src/readable.rs).

## Build and test

```sh
cargo +nightly fmt --all -- --check
cargo build
cargo test
cargo clippy --all-targets --all-features -- -D warnings -W clippy::pedantic
```

## CLI

```txt
policy-engine compile <TEMPLATE> [--out <BYTECODE>]    # default <BYTECODE> is out.bin
policy-engine print <BYTECODE>
policy-engine eval <BYTECODE> --evidence <EVIDENCE> \
    [--intent <INTENT>] [--check-hash <BASE58_SHA256>]
```

See [`docs/CLI.md`](docs/CLI.md) for the full specification and exit-code
rules.

### Building the CLI

The binary target is declared in `Cargo.toml` as `policy-engine` (hyphen),
with source in [`src/bin/policy-engine.rs`](src/bin/policy-engine.rs).

Build an optimized standalone binary:

```sh
cargo build --release --bin policy-engine
```

The executable is then at `target/release/policy-engine`. For a faster,
unoptimized dev build, drop `--release` and find it at
`target/debug/policy-engine`.

### Running it as `./policy-engine`

Copy or symlink the built binary next to where you want to run it:

```sh
cp target/release/policy-engine ./policy-engine
./policy-engine --help
```

or, to keep it in sync with rebuilds:

```sh
ln -s "$(pwd)/target/release/policy-engine" ./policy-engine
./policy-engine --help
```

### Installing into `PATH`

To invoke `policy-engine` from anywhere (no `./` prefix), install it into
`~/.cargo/bin`, which is normally on your `PATH`:

```sh
cargo install --path . --bin policy-engine
policy-engine --help
```

### End-to-end example

Using the fixtures in [`examples/`](examples/):

```sh
./policy-engine compile examples/clothing_purchase.template \
    --out /tmp/clothing_purchase.bytecode
./policy-engine print /tmp/clothing_purchase.bytecode
./policy-engine eval /tmp/clothing_purchase.bytecode \
    --intent examples/clothing_purchase.intent.json \
    --evidence examples/clothing_purchase.evidence.json
```

The example template comments use `cargo run --bin policy-engine -- ...`,
which does the same thing but rebuilds and runs through Cargo each time.
Building once with `cargo build --release` and invoking
`./policy-engine` directly is faster for repeated runs.

## Post-implementation report

See [post_implementation_report.md](post_implementation_report.md) for a
self-review of the shipped code: known gaps, things that would be done
differently next time, and a priority list for follow-up work.

Limitations:

* Contextually typed empty-set literals such as `"foo" in {}` are currently rejected during compilation instead of being inferred from surrounding type context.
* The source literal `-9223372036854775808` is currently rejected even though `int` is specified as signed 64-bit.
* Very deeply nested parenthesized expressions (around 2000 levels deep) can cause a stack overflow during `policy-engine compile`.
* Very long flat arithmetic chains (`x1 + x2 + ...`, around 2000 terms) can cause a stack overflow during `policy-engine compile`.
