Skip to main content

oxihuman_export/
capnp_stub_export.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3
4//! Stub exporter that generates Cap'n Proto schema text from mesh/scene data.
5
6#![allow(dead_code)]
7
8/// A Cap'n Proto field descriptor.
9#[allow(dead_code)]
10#[derive(Debug, Clone)]
11pub struct CapnpField {
12    pub name: String,
13    pub field_type: String,
14    pub index: usize,
15}
16
17/// A Cap'n Proto struct descriptor.
18#[allow(dead_code)]
19#[derive(Debug, Clone)]
20pub struct CapnpStruct {
21    pub name: String,
22    pub fields: Vec<CapnpField>,
23}
24
25/// A Cap'n Proto schema export.
26#[allow(dead_code)]
27#[derive(Debug, Clone, Default)]
28pub struct CapnpExport {
29    pub file_id: u64,
30    pub structs: Vec<CapnpStruct>,
31}
32
33/// Create a new Cap'n Proto export with a given file ID.
34#[allow(dead_code)]
35pub fn new_capnp_export(file_id: u64) -> CapnpExport {
36    CapnpExport {
37        file_id,
38        structs: Vec::new(),
39    }
40}
41
42/// Add a struct.
43#[allow(dead_code)]
44pub fn add_capnp_struct(doc: &mut CapnpExport, name: &str) {
45    doc.structs.push(CapnpStruct {
46        name: name.to_string(),
47        fields: Vec::new(),
48    });
49}
50
51/// Add a field to the last struct.
52#[allow(dead_code)]
53pub fn add_capnp_field(doc: &mut CapnpExport, name: &str, field_type: &str) {
54    if let Some(s) = doc.structs.last_mut() {
55        let idx = s.fields.len();
56        s.fields.push(CapnpField {
57            name: name.to_string(),
58            field_type: field_type.to_string(),
59            index: idx,
60        });
61    }
62}
63
64/// Return the number of structs.
65#[allow(dead_code)]
66pub fn capnp_struct_count(doc: &CapnpExport) -> usize {
67    doc.structs.len()
68}
69
70/// Return the number of fields in the last struct.
71#[allow(dead_code)]
72pub fn capnp_last_struct_field_count(doc: &CapnpExport) -> usize {
73    doc.structs.last().map_or(0, |s| s.fields.len())
74}
75
76/// Serialise as Cap'n Proto schema text (stub).
77#[allow(dead_code)]
78pub fn to_capnp_schema(doc: &CapnpExport) -> String {
79    let mut out = format!("@0x{:016x};\n\n", doc.file_id);
80    for st in &doc.structs {
81        out.push_str(&format!("struct {} {{\n", st.name));
82        for f in &st.fields {
83            out.push_str(&format!("  {} @{} :{},\n", f.name, f.index, f.field_type));
84        }
85        out.push_str("}\n\n");
86    }
87    out
88}
89
90/// Generate a stub schema for a mesh.
91#[allow(dead_code)]
92pub fn export_mesh_capnp_schema(vertex_count: usize, index_count: usize) -> String {
93    let mut doc = new_capnp_export(0xdeadbeefcafe0001);
94    add_capnp_struct(&mut doc, "Mesh");
95    add_capnp_field(&mut doc, "vertexCount", "UInt32");
96    add_capnp_field(&mut doc, "indexCount", "UInt32");
97    add_capnp_field(&mut doc, "positions", "List(Float32)");
98
99    let mut comment = format!(
100        "# vertexCount={}, indexCount={}\n",
101        vertex_count, index_count
102    );
103    comment.push_str(&to_capnp_schema(&doc));
104    comment
105}
106
107/// Find a struct by name.
108#[allow(dead_code)]
109pub fn find_capnp_struct<'a>(doc: &'a CapnpExport, name: &str) -> Option<&'a CapnpStruct> {
110    doc.structs.iter().find(|s| s.name == name)
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116
117    #[test]
118    fn test_new_capnp_export_empty() {
119        let doc = new_capnp_export(1);
120        assert_eq!(capnp_struct_count(&doc), 0);
121    }
122
123    #[test]
124    fn test_add_struct() {
125        let mut doc = new_capnp_export(1);
126        add_capnp_struct(&mut doc, "Vertex");
127        assert_eq!(capnp_struct_count(&doc), 1);
128    }
129
130    #[test]
131    fn test_add_field() {
132        let mut doc = new_capnp_export(1);
133        add_capnp_struct(&mut doc, "Mesh");
134        add_capnp_field(&mut doc, "count", "UInt32");
135        assert_eq!(capnp_last_struct_field_count(&doc), 1);
136    }
137
138    #[test]
139    fn test_to_capnp_schema_contains_struct() {
140        let mut doc = new_capnp_export(1);
141        add_capnp_struct(&mut doc, "MyStruct");
142        let s = to_capnp_schema(&doc);
143        assert!(s.contains("struct MyStruct"));
144    }
145
146    #[test]
147    fn test_to_capnp_schema_contains_field() {
148        let mut doc = new_capnp_export(1);
149        add_capnp_struct(&mut doc, "M");
150        add_capnp_field(&mut doc, "vertices", "UInt32");
151        let s = to_capnp_schema(&doc);
152        assert!(s.contains("vertices"));
153    }
154
155    #[test]
156    fn test_to_capnp_schema_has_file_id() {
157        let doc = new_capnp_export(0x1234);
158        let s = to_capnp_schema(&doc);
159        assert!(s.contains("@0x"));
160    }
161
162    #[test]
163    fn test_export_mesh_capnp_schema() {
164        let s = export_mesh_capnp_schema(100, 300);
165        assert!(s.contains("Mesh"));
166        assert!(s.contains("vertexCount=100"));
167    }
168
169    #[test]
170    fn test_find_capnp_struct() {
171        let mut doc = new_capnp_export(1);
172        add_capnp_struct(&mut doc, "Bone");
173        assert!(find_capnp_struct(&doc, "Bone").is_some());
174    }
175
176    #[test]
177    fn test_find_missing_struct() {
178        let doc = new_capnp_export(1);
179        assert!(find_capnp_struct(&doc, "Ghost").is_none());
180    }
181
182    #[test]
183    fn test_field_index_increments() {
184        let mut doc = new_capnp_export(1);
185        add_capnp_struct(&mut doc, "M");
186        add_capnp_field(&mut doc, "a", "Int32");
187        add_capnp_field(&mut doc, "b", "Int32");
188        let st = find_capnp_struct(&doc, "M").expect("should succeed");
189        assert_eq!(st.fields[1].index, 1);
190    }
191}