Skip to main content

shape_runtime/stdlib/
yaml.rs

1//! Native `yaml` module for YAML parsing and serialization.
2//!
3//! Exports: yaml.parse(text), yaml.parse_all(text), yaml.stringify(value), yaml.is_valid(text)
4//!
5//! Phase 1.B (ADR-006 §2.7.4) status: yaml.parse / yaml.parse_all /
6//! yaml.stringify REMAIN DEFERRED pending the **N4** (any-input typed
7//! marshal) and **N6** (any-output typed marshal) architectural
8//! decisions per `docs/defections.md` HashMap-marshal cluster's
9//! sub-decision queue.
10//!
11//! - `yaml.parse(text) -> Result<any>` and
12//!   `yaml.parse_all(text) -> Result<Array<any>>` return polymorphic
13//!   recursive `serde_yaml::Value`-equivalent trees. Mapping to N6.
14//! - `yaml.stringify(value: any)` takes a polymorphic `value: any`
15//!   input parameter that maps to N4.
16//!
17//! `yaml.is_valid(text)` is migratable standalone.
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. See
32/// `stdlib/json.rs` for the same Phase 1.B variadic shim.
33fn slot_as_string(slot: &KindedSlot) -> Option<Arc<String>> {
34    let bits = slot.slot().raw();
35    if bits == 0 {
36        return None;
37    }
38    unsafe {
39        let arc = Arc::<String>::from_raw(bits as *const String);
40        let cloned = arc.clone();
41        std::mem::forget(arc);
42        Some(cloned)
43    }
44}
45
46/// Create the `yaml` module with YAML parsing and serialization functions.
47pub fn create_yaml_module() -> ModuleExports {
48    let mut module = ModuleExports::new("std::core::yaml");
49    module.description = "YAML parsing and serialization".to_string();
50
51    // yaml.parse(text: string) -> Result<HashMap>
52    register_typed_function(
53        &mut module,
54        "parse",
55        "Parse a YAML string into Shape values",
56        vec![ModuleParam {
57            name: "text".to_string(),
58            type_name: "string".to_string(),
59            required: true,
60            description: "YAML string to parse".to_string(),
61            ..Default::default()
62        }],
63        ConcreteType::Result(Box::new(ConcreteType::HashMap)),
64        |_args, _ctx| {
65            Ok(TypedReturn::Err(ConcreteReturn::String(
66                "yaml.parse() pending N6 (any-output marshal) — see ADR-006 §2.7.4".to_string(),
67            )))
68        },
69    );
70
71    // yaml.parse_all(text: string) -> Result<Array>
72    register_typed_function(
73        &mut module,
74        "parse_all",
75        "Parse a multi-document YAML string into an array of Shape values",
76        vec![ModuleParam {
77            name: "text".to_string(),
78            type_name: "string".to_string(),
79            required: true,
80            description: "YAML string with one or more documents".to_string(),
81            ..Default::default()
82        }],
83        ConcreteType::Result(Box::new(ConcreteType::Array)),
84        |_args, _ctx| {
85            Ok(TypedReturn::Err(ConcreteReturn::String(
86                "yaml.parse_all() pending N6 (any-output marshal) — see ADR-006 §2.7.4"
87                    .to_string(),
88            )))
89        },
90    );
91
92    // yaml.stringify(value: any) -> Result<string>
93    register_typed_function(
94        &mut module,
95        "stringify",
96        "Serialize Shape values to a YAML string",
97        vec![ModuleParam {
98            name: "value".to_string(),
99            type_name: "any".to_string(),
100            required: true,
101            description: "Value to serialize".to_string(),
102            ..Default::default()
103        }],
104        ConcreteType::Result(Box::new(ConcreteType::String)),
105        |_args, _ctx| {
106            Ok(TypedReturn::Err(ConcreteReturn::String(
107                "yaml.stringify() pending N4 (any-input marshal) — see ADR-006 §2.7.4"
108                    .to_string(),
109            )))
110        },
111    );
112
113    // yaml.is_valid(text: string) -> bool
114    register_typed_function(
115        &mut module,
116        "is_valid",
117        "Check if a string is valid YAML",
118        vec![ModuleParam {
119            name: "text".to_string(),
120            type_name: "string".to_string(),
121            required: true,
122            description: "String to validate as YAML".to_string(),
123            ..Default::default()
124        }],
125        ConcreteType::Bool,
126        |args, _ctx| {
127            let slot = args
128                .first()
129                .ok_or_else(|| "yaml.is_valid() requires a string argument".to_string())?;
130            let text = slot_as_string(slot)
131                .ok_or_else(|| "yaml.is_valid() requires a string argument".to_string())?;
132            let valid = serde_yaml::from_str::<serde_yaml::Value>(text.as_str()).is_ok();
133            Ok(TypedReturn::Concrete(ConcreteReturn::Bool(valid)))
134        },
135    );
136
137    module
138}
139
140#[cfg(test)]
141mod tests {
142    use super::*;
143
144    #[test]
145    fn test_yaml_module_creation() {
146        let module = create_yaml_module();
147        assert_eq!(module.name, "std::core::yaml");
148        assert!(module.has_export("parse"));
149        assert!(module.has_export("parse_all"));
150        assert!(module.has_export("stringify"));
151        assert!(module.has_export("is_valid"));
152    }
153
154    #[test]
155    fn test_yaml_typed_registry_populated() {
156        let module = create_yaml_module();
157        let typed = module.typed_exports();
158        assert!(typed.get("parse").is_some());
159        assert!(typed.get("parse_all").is_some());
160        assert!(typed.get("stringify").is_some());
161        assert!(typed.get("is_valid").is_some());
162    }
163
164    // Behavioural roundtrip tests deleted alongside `module.invoke_export()`
165    // and the deleted `ValueWord` constructors. They return when N4 + N6
166    // land and the bodies become real serializers.
167}