Skip to main content

sim_codec_json/
lib.rs

1//! General-purpose JSON codec for the SIM runtime.
2//!
3//! This crate is a first-class, general-purpose codec: it round-trips every
4//! kernel `Expr` losslessly by projecting the shared `Expr` graph onto
5//! `serde_json::Value` using `$expr`-tagged forms (and `$located` forms when
6//! source origins are carried). On top of that canonical projection it also
7//! exposes interop surfaces aimed at foreign JSON consumers: a lossy untagged
8//! projection mode and a small JSON Schema view for describing shapes.
9//!
10//! The public surface is the [`JsonCodec`] runtime object (registered via
11//! [`JsonCodecLib`]) plus the free conversion functions: the canonical
12//! [`expr_to_json`] / [`json_to_expr`] and located/tree variants, the
13//! mode-aware projection in [`project_expr_to_json`] / [`project_json_to_expr`]
14//! ([`JsonProjectionMode`]), and the schema lowering in
15//! [`shape_to_json_schema`] ([`ShapeSchema`]).
16//!
17//! # Examples
18//!
19//! Register the codec and round-trip s-expression-free text through the shared
20//! `Expr` graph -- decode JSON into an [`Expr`], then encode it back:
21//!
22//! ```
23//! use std::sync::Arc;
24//! use sim_codec::{Input, decode_with_codec, encode_with_codec};
25//! use sim_codec_json::JsonCodecLib;
26//! use sim_kernel::{Cx, DefaultFactory, EagerPolicy, Expr, ReadPolicy, Symbol};
27//!
28//! let mut cx = Cx::new(Arc::new(EagerPolicy), Arc::new(DefaultFactory));
29//! sim_test_support::register_core_classes(&mut cx);
30//!
31//! let lib = JsonCodecLib::new(cx.registry_mut().fresh_codec_id());
32//! cx.load_lib(&lib)?;
33//! let json = Symbol::qualified("codec", "json");
34//!
35//! let expr = decode_with_codec(
36//!     &mut cx,
37//!     &json,
38//!     Input::Text(r#"{"$expr":"bool","value":true}"#.to_owned()),
39//!     ReadPolicy::default(),
40//! )?;
41//! assert_eq!(expr, Expr::Bool(true));
42//!
43//! let text = encode_with_codec(&mut cx, &json, &expr, Default::default())?
44//!     .into_text()
45//!     .unwrap();
46//! assert_eq!(text, r#"{"$expr":"bool","value":true}"#);
47//! # Ok::<(), sim_kernel::Error>(())
48//! ```
49//!
50//! The canonical projection functions can be used directly, without a runtime
51//! context, for a pure `Expr <-> JSON` round-trip:
52//!
53//! ```
54//! use sim_codec::{DecodeBudget, DecodeLimits};
55//! use sim_codec_json::{expr_to_json, json_to_expr};
56//! use sim_kernel::{CodecId, Expr};
57//!
58//! let expr = Expr::Bytes(vec![0xfb, 0xef]);
59//! let value = expr_to_json(&expr);
60//! assert_eq!(value["base64"], serde_json::json!("++8="));
61//!
62//! let mut budget = DecodeBudget::new(DecodeLimits::default());
63//! let back = json_to_expr(CodecId(1), &value, &mut budget, 0)?;
64//! assert_eq!(back, expr);
65//! # Ok::<(), sim_kernel::Error>(())
66//! ```
67//!
68//! [`Expr`]: sim_kernel::Expr
69
70#![forbid(unsafe_code)]
71#![deny(missing_docs)]
72
73mod codec;
74mod expr_json;
75mod helpers;
76mod projection;
77mod schema;
78#[cfg(test)]
79mod tests;
80mod tree_json;
81
82/// Cookbook recipes for the JSON codec, embedded at build time from the crate's
83/// `recipes/` directory and exposed for help and browse surfaces.
84pub static RECIPES: sim_cookbook::EmbeddedDir =
85    include!(concat!(env!("OUT_DIR"), "/cookbook_recipes.rs"));
86
87pub use codec::{JsonCodec, JsonCodecLib};
88pub use expr_json::{expr_to_json, json_to_expr};
89pub use projection::{
90    JsonProjectionMode, json_number_to_u64, project_expr_to_json, project_json_to_expr,
91    project_json_to_expr_budgeted,
92};
93pub use schema::{ShapeSchema, shape_to_json_schema};
94pub use tree_json::{json_to_located_expr, json_to_tree, located_expr_to_json, tree_to_json};