Skip to main content

oxihuman_export/
sat_export.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! ACIS SAT format export stub.
6
7/// A single ACIS SAT entity record.
8#[derive(Debug, Clone)]
9pub struct SatEntity {
10    pub index: u32,
11    pub entity_type: String,
12    pub fields: Vec<String>,
13}
14
15/// ACIS SAT export container.
16#[derive(Debug, Clone, Default)]
17pub struct SatExport {
18    pub header_version: u32,
19    pub entities: Vec<SatEntity>,
20    pub unit_scale: f64,
21}
22
23/// Create a new SAT export.
24pub fn new_sat_export(version: u32, unit_scale: f64) -> SatExport {
25    SatExport {
26        header_version: version,
27        entities: Vec::new(),
28        unit_scale,
29    }
30}
31
32/// Add an ACIS entity; returns its index.
33pub fn add_sat_entity(export: &mut SatExport, entity_type: &str, fields: Vec<String>) -> u32 {
34    let idx = export.entities.len() as u32;
35    export.entities.push(SatEntity {
36        index: idx,
37        entity_type: entity_type.to_string(),
38        fields,
39    });
40    idx
41}
42
43/// Return the entity count.
44pub fn sat_entity_count(export: &SatExport) -> usize {
45    export.entities.len()
46}
47
48/// Render the SAT header section (stub).
49pub fn sat_header(export: &SatExport) -> String {
50    format!(
51        "{} 0 0 0\n400 0 1 0\n{}",
52        export.header_version, export.unit_scale
53    )
54}
55
56/// Render a SAT entity line.
57pub fn sat_entity_line(entity: &SatEntity) -> String {
58    format!(
59        "-{}  {} {}  #",
60        entity.index,
61        entity.entity_type,
62        entity.fields.join(" ")
63    )
64}
65
66/// Find the first entity of the given type.
67pub fn find_sat_entity<'a>(export: &'a SatExport, entity_type: &str) -> Option<&'a SatEntity> {
68    export
69        .entities
70        .iter()
71        .find(|e| e.entity_type == entity_type)
72}
73
74/// Validate that all entity types are non-empty.
75pub fn validate_sat(export: &SatExport) -> bool {
76    export.entities.iter().all(|e| !e.entity_type.is_empty())
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    #[test]
84    fn test_new_sat_export_empty() {
85        let exp = new_sat_export(700, 1.0);
86        assert_eq!(sat_entity_count(&exp), 0);
87    }
88
89    #[test]
90    fn test_add_entity_returns_index() {
91        let mut exp = new_sat_export(700, 1.0);
92        let idx = add_sat_entity(&mut exp, "body", vec![]);
93        assert_eq!(idx, 0);
94    }
95
96    #[test]
97    fn test_entity_count_increments() {
98        let mut exp = new_sat_export(700, 1.0);
99        add_sat_entity(&mut exp, "body", vec![]);
100        add_sat_entity(&mut exp, "lump", vec![]);
101        assert_eq!(sat_entity_count(&exp), 2);
102    }
103
104    #[test]
105    fn test_sat_header_contains_version() {
106        let exp = new_sat_export(700, 25.4);
107        assert!(sat_header(&exp).contains("700"));
108    }
109
110    #[test]
111    fn test_sat_entity_line_contains_type() {
112        let e = SatEntity {
113            index: 0,
114            entity_type: "face".into(),
115            fields: vec![],
116        };
117        assert!(sat_entity_line(&e).contains("face"));
118    }
119
120    #[test]
121    fn test_find_entity_existing() {
122        let mut exp = new_sat_export(700, 1.0);
123        add_sat_entity(&mut exp, "body", vec![]);
124        assert!(find_sat_entity(&exp, "body").is_some());
125    }
126
127    #[test]
128    fn test_find_entity_missing() {
129        let exp = new_sat_export(700, 1.0);
130        assert!(find_sat_entity(&exp, "body").is_none());
131    }
132
133    #[test]
134    fn test_validate_valid() {
135        let mut exp = new_sat_export(700, 1.0);
136        add_sat_entity(&mut exp, "body", vec![]);
137        assert!(validate_sat(&exp));
138    }
139
140    #[test]
141    fn test_unit_scale_stored() {
142        let exp = new_sat_export(700, 25.4);
143        assert!((exp.unit_scale - 25.4).abs() < 1e-9);
144    }
145
146    #[test]
147    fn test_version_stored() {
148        let exp = new_sat_export(700, 1.0);
149        assert_eq!(exp.header_version, 700);
150    }
151}