cairnlang-core 0.5.0

Cairn core: content-addressed AST store, the single type/confidence/effect checker, projection renderer, and WASM lowering. Owns the model.
Documentation
//! Tier-3 integration: the JSON quote API (`examples/quote_spec.rs`,
//! the single source) assembled through the public Core API and run
//! under `wasmtime`. The spec is `#[path]`-included from the example
//! so the runnable demo and this test cannot diverge.
//!
//! This is the *second* Tier-3 app — deliberately unlike the blog: no
//! DB, no CRUD; it exercises the `json` module (`json_parse`/
//! `json_stringify`) and the generic stdlib (`find`/`fold`/`lpad0`)
//! over the `web` framework. A different language muscle group, so
//! "the demo is solid" rests on more than one app.

#[allow(dead_code)] // `main` + helpers the test path doesn't reach
#[path = "../examples/quote_spec.rs"]
mod quote_spec;

use cairn_core::{lower, serve_request, Editor, HttpResponse, Store};

#[test]
fn the_json_quote_api_runs_on_json_and_the_stdlib() {
    let editor = Editor::new(Store::open_in_memory().unwrap());
    let (module, report) = editor
        .apply_module(&quote_spec::quote_module_spec())
        .unwrap();
    assert!(
        report.ok(),
        "the quote app did not type-check: {:?}",
        report.violations
    );
    assert_eq!(report.status, cairn_core::Status::Complete);
    let wasm = lower(editor.store(), &module).unwrap();

    let go = |body: &str| serve_request(&wasm, "route", "POST", "/", body).unwrap();
    let ok = |body: &str| HttpResponse {
        status: 200,
        body: body.into(),
    };

    // Two line items: 2*150 + 1*300 = 600c = $6.00.
    assert_eq!(
        go(r#"[{"qty":2,"price":150},{"qty":1,"price":300}]"#),
        ok(r#"{"count":2,"total_cents":600,"total":"$6.00"}"#)
    );
    // Empty cart.
    assert_eq!(
        go("[]"),
        ok(r#"{"count":0,"total_cents":0,"total":"$0.00"}"#)
    );
    // Money formatting (stdlib lpad0): sub-dime, sub-dollar, carry.
    assert_eq!(
        go(r#"[{"qty":1,"price":5}]"#),
        ok(r#"{"count":1,"total_cents":5,"total":"$0.05"}"#)
    );
    assert_eq!(
        go(r#"[{"qty":1,"price":60}]"#),
        ok(r#"{"count":1,"total_cents":60,"total":"$0.60"}"#)
    );
    assert_eq!(
        go(r#"[{"qty":3,"price":205}]"#),
        ok(r#"{"count":1,"total_cents":615,"total":"$6.15"}"#)
    );
    // Whitespace in the JSON body is fine (json_parse skips it).
    assert_eq!(
        go(r#" [ { "qty": 2 , "price": 150 } ] "#),
        ok(r#"{"count":1,"total_cents":300,"total":"$3.00"}"#)
    );
    // Not a JSON array → 400, every non-JArr shape (exhaustive match).
    let bad = HttpResponse {
        status: 400,
        body: "expected a JSON array".into(),
    };
    assert_eq!(go(r#"{"x":1}"#), bad); // JObj
    assert_eq!(go(r#""hi""#), bad); // JStr
    assert_eq!(go("42"), bad); // JNum
    assert_eq!(go("true"), bad); // JBool
    assert_eq!(go(""), bad); // empty → JNull
}