Skip to main content

oxihuman_export/
ron_export.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! RON (Rusty Object Notation) export.
6
7#[derive(Debug, Clone)]
8pub enum RonValue {
9    Bool(bool),
10    Int(i64),
11    Float(f64),
12    Str(String),
13    Option(Option<Box<RonValue>>),
14    List(Vec<RonValue>),
15    Map(Vec<(String, RonValue)>),
16    Struct {
17        name: String,
18        fields: Vec<(String, RonValue)>,
19    },
20}
21
22pub fn ron_bool(b: bool) -> RonValue {
23    RonValue::Bool(b)
24}
25pub fn ron_int(i: i64) -> RonValue {
26    RonValue::Int(i)
27}
28pub fn ron_float(f: f64) -> RonValue {
29    RonValue::Float(f)
30}
31pub fn ron_str(s: &str) -> RonValue {
32    RonValue::Str(s.to_string())
33}
34pub fn ron_none() -> RonValue {
35    RonValue::Option(None)
36}
37pub fn ron_some(v: RonValue) -> RonValue {
38    RonValue::Option(Some(Box::new(v)))
39}
40pub fn ron_list(items: Vec<RonValue>) -> RonValue {
41    RonValue::List(items)
42}
43pub fn ron_map(entries: Vec<(String, RonValue)>) -> RonValue {
44    RonValue::Map(entries)
45}
46pub fn ron_struct(name: &str, fields: Vec<(String, RonValue)>) -> RonValue {
47    RonValue::Struct {
48        name: name.to_string(),
49        fields,
50    }
51}
52
53fn render_ron_value(v: &RonValue) -> String {
54    match v {
55        RonValue::Bool(b) => b.to_string(),
56        RonValue::Int(i) => i.to_string(),
57        RonValue::Float(f) => format!("{:?}", f),
58        RonValue::Str(s) => format!("\"{}\"", s.replace('"', "\\\"")),
59        RonValue::Option(None) => "None".to_string(),
60        RonValue::Option(Some(inner)) => format!("Some({})", render_ron_value(inner)),
61        RonValue::List(items) => {
62            let inner: Vec<String> = items.iter().map(render_ron_value).collect();
63            format!("[{}]", inner.join(", "))
64        }
65        RonValue::Map(entries) => {
66            let inner: Vec<String> = entries
67                .iter()
68                .map(|(k, v)| format!("\"{}\": {}", k, render_ron_value(v)))
69                .collect();
70            format!("{{{}}}", inner.join(", "))
71        }
72        RonValue::Struct { name, fields } => {
73            let inner: Vec<String> = fields
74                .iter()
75                .map(|(k, v)| format!("{}: {}", k, render_ron_value(v)))
76                .collect();
77            format!("{}({})", name, inner.join(", "))
78        }
79    }
80}
81
82pub fn render_ron(root: &RonValue) -> String {
83    format!("(\n    {}\n)\n", render_ron_value(root))
84}
85
86pub fn export_ron(root: &RonValue) -> Vec<u8> {
87    render_ron(root).into_bytes()
88}
89pub fn ron_size_bytes(root: &RonValue) -> usize {
90    render_ron(root).len()
91}
92pub fn validate_ron_value(v: &RonValue) -> bool {
93    match v {
94        RonValue::Float(f) => f.is_finite(),
95        _ => true,
96    }
97}
98
99pub fn scene_to_ron(name: &str, mesh_count: usize) -> RonValue {
100    ron_struct(
101        "Scene",
102        vec![
103            ("name".to_string(), ron_str(name)),
104            ("mesh_count".to_string(), ron_int(mesh_count as i64)),
105        ],
106    )
107}
108
109pub fn ron_list_len(v: &RonValue) -> usize {
110    if let RonValue::List(items) = v {
111        items.len()
112    } else {
113        0
114    }
115}
116
117pub fn ron_map_get<'a>(v: &'a RonValue, key: &str) -> Option<&'a RonValue> {
118    if let RonValue::Map(entries) = v {
119        entries.iter().find(|(k, _)| k == key).map(|(_, v)| v)
120    } else {
121        None
122    }
123}
124
125#[cfg(test)]
126mod tests {
127    use super::*;
128
129    #[test]
130    fn test_ron_bool() {
131        let s = render_ron_value(&ron_bool(true));
132        assert_eq!(s, "true");
133    }
134
135    #[test]
136    fn test_ron_int() {
137        let s = render_ron_value(&ron_int(99));
138        assert!(s.contains("99"));
139    }
140
141    #[test]
142    fn test_ron_option_none() {
143        let s = render_ron_value(&ron_none());
144        assert_eq!(s, "None");
145    }
146
147    #[test]
148    fn test_ron_option_some() {
149        let s = render_ron_value(&ron_some(ron_int(1)));
150        assert!(s.starts_with("Some("));
151    }
152
153    #[test]
154    fn test_ron_struct() {
155        let v = ron_struct("Foo", vec![("x".to_string(), ron_int(1))]);
156        let s = render_ron_value(&v);
157        assert!(s.contains("Foo"));
158    }
159
160    #[test]
161    fn test_export_ron_nonempty() {
162        let v = ron_bool(false);
163        assert!(!export_ron(&v).is_empty());
164    }
165
166    #[test]
167    fn test_validate_ron_value() {
168        assert!(validate_ron_value(&ron_float(1.0)));
169    }
170
171    #[test]
172    fn test_scene_to_ron() {
173        let v = scene_to_ron("MyScene", 5);
174        let s = render_ron_value(&v);
175        assert!(s.contains("MyScene"));
176    }
177}