Skip to main content

shape_runtime/stdlib/
toml_module.rs

1//! Native `toml` module for TOML parsing and serialization.
2//!
3//! Exports: toml.parse(text), toml.stringify(value), toml.is_valid(text)
4//!
5//! Phase 1.B (ADR-006 §2.7.4) status: `toml.parse` / `toml.stringify`
6//! REMAIN DEFERRED pending the **N4** (any-input typed marshal) and
7//! **N6** (any-output typed marshal) architectural decisions per
8//! `docs/defections.md` HashMap-marshal cluster's sub-decision queue.
9//!
10//! - `toml.parse(text) -> Result<any>` returns a polymorphic recursive
11//!   `toml::Value`-equivalent tree. Mapping to the N6 architectural
12//!   surface.
13//! - `toml.stringify(value: any)` takes a polymorphic `value: any`
14//!   input. Mapping to the N4 architectural surface.
15//!
16//! `toml.is_valid(text)` is migratable standalone — it only needs the
17//! incoming `string` slot.
18//!
19//! Until N4 + N6 land, the bodies use the variadic
20//! [`register_typed_function`] shape (per ADR-006 §2.7.4 ruling) and
21//! return `Err(...)` for the deferred halves; `is_valid` is fully
22//! functional.
23
24use crate::module_exports::{ModuleExports, ModuleParam};
25use crate::typed_module_exports::{
26    ConcreteReturn, ConcreteType, TypedReturn, register_typed_function,
27};
28use shape_value::KindedSlot;
29use std::sync::Arc;
30
31/// Read a [`KindedSlot`]'s bits as an `Arc<String>` payload. Mirrors
32/// the helper in `stdlib/json.rs` — Phase 1.B variadic shim until per-
33/// position kind threading lands in Phase 2c.
34fn slot_as_string(slot: &KindedSlot) -> Option<Arc<String>> {
35    let bits = slot.slot().raw();
36    if bits == 0 {
37        return None;
38    }
39    // SAFETY: variadic-arg slots whose registered param type is
40    // `string` store `Arc::into_raw::<String>` bits. Reconstitute via
41    // `from_raw` + `clone` + `forget` to bump the refcount without
42    // consuming the slot's strong-count share.
43    unsafe {
44        let arc = Arc::<String>::from_raw(bits as *const String);
45        let cloned = arc.clone();
46        std::mem::forget(arc);
47        Some(cloned)
48    }
49}
50
51/// Create the `toml` module with TOML parsing and serialization functions.
52pub fn create_toml_module() -> ModuleExports {
53    let mut module = ModuleExports::new("std::core::toml");
54    module.description = "TOML parsing and serialization".to_string();
55
56    // toml.parse(text: string) -> Result<HashMap>
57    register_typed_function(
58        &mut module,
59        "parse",
60        "Parse a TOML string into Shape values",
61        vec![ModuleParam {
62            name: "text".to_string(),
63            type_name: "string".to_string(),
64            required: true,
65            description: "TOML string to parse".to_string(),
66            ..Default::default()
67        }],
68        ConcreteType::Result(Box::new(ConcreteType::HashMap)),
69        |_args, _ctx| {
70            Ok(TypedReturn::Err(ConcreteReturn::String(
71                "toml.parse() pending N6 (any-output marshal) — see ADR-006 §2.7.4".to_string(),
72            )))
73        },
74    );
75
76    // toml.stringify(value: any) -> Result<string>
77    register_typed_function(
78        &mut module,
79        "stringify",
80        "Serialize Shape values to a TOML string",
81        vec![ModuleParam {
82            name: "value".to_string(),
83            type_name: "any".to_string(),
84            required: true,
85            description: "Value to serialize (must be a HashMap or TypedObject)".to_string(),
86            ..Default::default()
87        }],
88        ConcreteType::Result(Box::new(ConcreteType::String)),
89        |_args, _ctx| {
90            Ok(TypedReturn::Err(ConcreteReturn::String(
91                "toml.stringify() pending N4 (any-input marshal) — see ADR-006 §2.7.4"
92                    .to_string(),
93            )))
94        },
95    );
96
97    // toml.is_valid(text: string) -> bool
98    register_typed_function(
99        &mut module,
100        "is_valid",
101        "Check if a string is valid TOML",
102        vec![ModuleParam {
103            name: "text".to_string(),
104            type_name: "string".to_string(),
105            required: true,
106            description: "String to validate as TOML".to_string(),
107            ..Default::default()
108        }],
109        ConcreteType::Bool,
110        |args, _ctx| {
111            let slot = args
112                .first()
113                .ok_or_else(|| "toml.is_valid() requires a string argument".to_string())?;
114            let text = slot_as_string(slot)
115                .ok_or_else(|| "toml.is_valid() requires a string argument".to_string())?;
116            let valid = toml::from_str::<toml::Value>(text.as_str()).is_ok();
117            Ok(TypedReturn::Concrete(ConcreteReturn::Bool(valid)))
118        },
119    );
120
121    module
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127
128    #[test]
129    fn test_toml_module_creation() {
130        let module = create_toml_module();
131        assert_eq!(module.name, "std::core::toml");
132        assert!(module.has_export("parse"));
133        assert!(module.has_export("stringify"));
134        assert!(module.has_export("is_valid"));
135    }
136
137    #[test]
138    fn test_toml_typed_registry_populated() {
139        let module = create_toml_module();
140        let typed = module.typed_exports();
141        assert!(typed.get("parse").is_some());
142        assert!(typed.get("stringify").is_some());
143        assert!(typed.get("is_valid").is_some());
144    }
145
146    // Behavioural roundtrip tests deleted alongside `module.invoke_export()`
147    // and the deleted `ValueWord` constructors. They return when N4 + N6
148    // land and the bodies become real serializers.
149}