1#[allow(dead_code)]
4#[derive(Clone, PartialEq, Debug)]
5pub enum ExportTarget {
6 Glb,
7 Obj,
8 Fbx,
9 Usdz,
10 Ply,
11 Stl,
12 Collada,
13 Custom(String),
14}
15
16#[allow(dead_code)]
17#[derive(Clone)]
18pub struct ExportPreset {
19 pub name: String,
20 pub target: ExportTarget,
21 pub include_normals: bool,
22 pub include_uvs: bool,
23 pub include_animations: bool,
24 pub include_materials: bool,
25 pub scale: f32,
26 pub up_axis: String,
27 pub custom_options: Vec<(String, String)>,
28}
29
30#[allow(dead_code)]
31pub struct PresetLibraryExport {
32 pub presets: Vec<ExportPreset>,
33 pub default_preset: Option<String>,
34}
35
36#[allow(dead_code)]
37pub fn default_glb_preset() -> ExportPreset {
38 ExportPreset {
39 name: "Default GLB".to_string(),
40 target: ExportTarget::Glb,
41 include_normals: true,
42 include_uvs: true,
43 include_animations: true,
44 include_materials: true,
45 scale: 1.0,
46 up_axis: "Y".to_string(),
47 custom_options: Vec::new(),
48 }
49}
50
51#[allow(dead_code)]
52pub fn default_obj_preset() -> ExportPreset {
53 ExportPreset {
54 name: "Default OBJ".to_string(),
55 target: ExportTarget::Obj,
56 include_normals: true,
57 include_uvs: true,
58 include_animations: false,
59 include_materials: true,
60 scale: 1.0,
61 up_axis: "Y".to_string(),
62 custom_options: Vec::new(),
63 }
64}
65
66#[allow(dead_code)]
67pub fn new_preset_library() -> PresetLibraryExport {
68 PresetLibraryExport {
69 presets: Vec::new(),
70 default_preset: None,
71 }
72}
73
74#[allow(dead_code)]
75pub fn add_preset(lib: &mut PresetLibraryExport, preset: ExportPreset) {
76 lib.presets.push(preset);
77}
78
79#[allow(dead_code)]
80pub fn get_preset_by_name<'a>(
81 lib: &'a PresetLibraryExport,
82 name: &str,
83) -> Option<&'a ExportPreset> {
84 lib.presets.iter().find(|p| p.name == name)
85}
86
87#[allow(dead_code)]
88pub fn remove_preset(lib: &mut PresetLibraryExport, name: &str) -> bool {
89 let before = lib.presets.len();
90 lib.presets.retain(|p| p.name != name);
91 let removed = lib.presets.len() < before;
92 if removed {
93 if let Some(ref default) = lib.default_preset.clone() {
94 if default == name {
95 lib.default_preset = None;
96 }
97 }
98 }
99 removed
100}
101
102#[allow(dead_code)]
103pub fn set_default_preset(lib: &mut PresetLibraryExport, name: &str) {
104 lib.default_preset = Some(name.to_string());
105}
106
107#[allow(dead_code)]
108pub fn preset_count(lib: &PresetLibraryExport) -> usize {
109 lib.presets.len()
110}
111
112#[allow(dead_code)]
113pub fn presets_for_target<'a>(
114 lib: &'a PresetLibraryExport,
115 target: &ExportTarget,
116) -> Vec<&'a ExportPreset> {
117 lib.presets.iter().filter(|p| &p.target == target).collect()
118}
119
120#[allow(dead_code)]
121pub fn preset_to_json(preset: &ExportPreset) -> String {
122 let target_str = match &preset.target {
123 ExportTarget::Glb => "glb",
124 ExportTarget::Obj => "obj",
125 ExportTarget::Fbx => "fbx",
126 ExportTarget::Usdz => "usdz",
127 ExportTarget::Ply => "ply",
128 ExportTarget::Stl => "stl",
129 ExportTarget::Collada => "collada",
130 ExportTarget::Custom(s) => s.as_str(),
131 };
132 let custom_opts: Vec<String> = preset
133 .custom_options
134 .iter()
135 .map(|(k, v)| format!("{{\"key\":\"{k}\",\"value\":\"{v}\"}}"))
136 .collect();
137 format!(
138 "{{\"name\":\"{}\",\"target\":\"{}\",\"include_normals\":{},\"include_uvs\":{},\
139 \"include_animations\":{},\"include_materials\":{},\"scale\":{},\"up_axis\":\"{}\",\
140 \"custom_options\":[{}]}}",
141 preset.name,
142 target_str,
143 preset.include_normals,
144 preset.include_uvs,
145 preset.include_animations,
146 preset.include_materials,
147 preset.scale,
148 preset.up_axis,
149 custom_opts.join(",")
150 )
151}
152
153#[allow(dead_code)]
154pub fn preset_library_to_json(lib: &PresetLibraryExport) -> String {
155 let presets_json: Vec<String> = lib.presets.iter().map(preset_to_json).collect();
156 let default_str = match &lib.default_preset {
157 Some(name) => format!("\"{name}\""),
158 None => "null".to_string(),
159 };
160 format!(
161 "{{\"presets\":[{}],\"default_preset\":{}}}",
162 presets_json.join(","),
163 default_str
164 )
165}
166
167#[allow(dead_code)]
168pub fn clone_preset(preset: &ExportPreset, new_name: &str) -> ExportPreset {
169 let mut cloned = preset.clone();
170 cloned.name = new_name.to_string();
171 cloned
172}
173
174#[allow(dead_code)]
175pub fn add_custom_option(preset: &mut ExportPreset, key: &str, value: &str) {
176 preset
177 .custom_options
178 .push((key.to_string(), value.to_string()));
179}
180
181#[allow(dead_code)]
182pub fn target_extension(target: &ExportTarget) -> &'static str {
183 match target {
184 ExportTarget::Glb => ".glb",
185 ExportTarget::Obj => ".obj",
186 ExportTarget::Fbx => ".fbx",
187 ExportTarget::Usdz => ".usdz",
188 ExportTarget::Ply => ".ply",
189 ExportTarget::Stl => ".stl",
190 ExportTarget::Collada => ".dae",
191 ExportTarget::Custom(_) => "",
192 }
193}
194
195#[cfg(test)]
196mod tests {
197 use super::*;
198
199 #[test]
200 fn test_default_glb_preset() {
201 let p = default_glb_preset();
202 assert_eq!(p.target, ExportTarget::Glb);
203 assert!(p.include_normals);
204 assert!(p.include_animations);
205 assert!((p.scale - 1.0).abs() < f32::EPSILON);
206 }
207
208 #[test]
209 fn test_default_obj_preset() {
210 let p = default_obj_preset();
211 assert_eq!(p.target, ExportTarget::Obj);
212 assert!(!p.include_animations);
213 assert!(p.include_uvs);
214 }
215
216 #[test]
217 fn test_new_library() {
218 let lib = new_preset_library();
219 assert!(lib.presets.is_empty());
220 assert!(lib.default_preset.is_none());
221 }
222
223 #[test]
224 fn test_add_preset() {
225 let mut lib = new_preset_library();
226 add_preset(&mut lib, default_glb_preset());
227 assert_eq!(preset_count(&lib), 1);
228 }
229
230 #[test]
231 fn test_get_preset_by_name() {
232 let mut lib = new_preset_library();
233 add_preset(&mut lib, default_glb_preset());
234 let found = get_preset_by_name(&lib, "Default GLB");
235 assert!(found.is_some());
236 assert_eq!(found.expect("should succeed").target, ExportTarget::Glb);
237 }
238
239 #[test]
240 fn test_get_preset_by_name_not_found() {
241 let lib = new_preset_library();
242 assert!(get_preset_by_name(&lib, "Missing").is_none());
243 }
244
245 #[test]
246 fn test_remove_preset() {
247 let mut lib = new_preset_library();
248 add_preset(&mut lib, default_glb_preset());
249 let removed = remove_preset(&mut lib, "Default GLB");
250 assert!(removed);
251 assert_eq!(preset_count(&lib), 0);
252 }
253
254 #[test]
255 fn test_remove_preset_not_found() {
256 let mut lib = new_preset_library();
257 let removed = remove_preset(&mut lib, "Nonexistent");
258 assert!(!removed);
259 }
260
261 #[test]
262 fn test_set_default_preset() {
263 let mut lib = new_preset_library();
264 add_preset(&mut lib, default_glb_preset());
265 set_default_preset(&mut lib, "Default GLB");
266 assert_eq!(lib.default_preset.as_deref(), Some("Default GLB"));
267 }
268
269 #[test]
270 fn test_preset_count() {
271 let mut lib = new_preset_library();
272 assert_eq!(preset_count(&lib), 0);
273 add_preset(&mut lib, default_glb_preset());
274 add_preset(&mut lib, default_obj_preset());
275 assert_eq!(preset_count(&lib), 2);
276 }
277
278 #[test]
279 fn test_presets_for_target() {
280 let mut lib = new_preset_library();
281 add_preset(&mut lib, default_glb_preset());
282 add_preset(&mut lib, default_obj_preset());
283 let glb_presets = presets_for_target(&lib, &ExportTarget::Glb);
284 assert_eq!(glb_presets.len(), 1);
285 assert_eq!(glb_presets[0].target, ExportTarget::Glb);
286 }
287
288 #[test]
289 fn test_preset_to_json_non_empty() {
290 let p = default_glb_preset();
291 let json = preset_to_json(&p);
292 assert!(!json.is_empty());
293 assert!(json.contains("glb"));
294 assert!(json.contains("Default GLB"));
295 }
296
297 #[test]
298 fn test_preset_library_to_json() {
299 let mut lib = new_preset_library();
300 add_preset(&mut lib, default_glb_preset());
301 let json = preset_library_to_json(&lib);
302 assert!(!json.is_empty());
303 assert!(json.contains("presets"));
304 }
305
306 #[test]
307 fn test_clone_preset() {
308 let p = default_glb_preset();
309 let cloned = clone_preset(&p, "My GLB Clone");
310 assert_eq!(cloned.name, "My GLB Clone");
311 assert_eq!(cloned.target, ExportTarget::Glb);
312 }
313
314 #[test]
315 fn test_add_custom_option() {
316 let mut p = default_glb_preset();
317 add_custom_option(&mut p, "compress", "true");
318 assert_eq!(p.custom_options.len(), 1);
319 assert_eq!(p.custom_options[0].0, "compress");
320 assert_eq!(p.custom_options[0].1, "true");
321 }
322
323 #[test]
324 fn test_target_extension() {
325 assert_eq!(target_extension(&ExportTarget::Glb), ".glb");
326 assert_eq!(target_extension(&ExportTarget::Obj), ".obj");
327 assert_eq!(target_extension(&ExportTarget::Fbx), ".fbx");
328 assert_eq!(target_extension(&ExportTarget::Usdz), ".usdz");
329 assert_eq!(target_extension(&ExportTarget::Ply), ".ply");
330 assert_eq!(target_extension(&ExportTarget::Stl), ".stl");
331 assert_eq!(target_extension(&ExportTarget::Collada), ".dae");
332 assert_eq!(
333 target_extension(&ExportTarget::Custom("abc".to_string())),
334 ""
335 );
336 }
337
338 #[test]
339 fn test_remove_default_preset_clears_default() {
340 let mut lib = new_preset_library();
341 add_preset(&mut lib, default_glb_preset());
342 set_default_preset(&mut lib, "Default GLB");
343 remove_preset(&mut lib, "Default GLB");
344 assert!(lib.default_preset.is_none());
345 }
346}