1use serde::{Deserialize, Serialize};
11
12macro_rules! string_enum {
13 ($(#[$meta:meta])* $vis:vis enum $name:ident { $($(#[$vmeta:meta])* $variant:ident => $wire:literal),+ $(,)? }) => {
14 $(#[$meta])*
15 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
16 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
17 $vis enum $name {
18 $(
19 #[doc = concat!("Wire value: `", $wire, "`.")]
20 $(#[$vmeta])*
21 #[serde(rename = $wire)]
22 $variant,
23 )+
24 }
25 };
26}
27
28macro_rules! string_enum_open {
29 ($(#[$meta:meta])* $vis:vis enum $name:ident { $($(#[$vmeta:meta])* $variant:ident => $wire:literal),+ $(,)? }) => {
30 $(#[$meta])*
31 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
32 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
33 $vis enum $name {
34 $(
35 #[doc = concat!("Wire value: `", $wire, "`.")]
36 $(#[$vmeta])*
37 #[serde(rename = $wire)]
38 $variant,
39 )+
40 #[serde(other)]
42 Unknown,
43 }
44 };
45}
46
47string_enum! {
48 pub enum OutputFormat {
50 Gltf => "GLTF",
51 Usdz => "USDZ",
52 Fbx => "FBX",
53 Obj => "OBJ",
54 Stl => "STL",
55 ThreeMf => "3MF",
56 }
57}
58
59string_enum! {
60 pub enum TextureFormat {
62 Bmp => "BMP", Dpx => "DPX", Hdr => "HDR", Jpeg => "JPEG",
63 OpenExr => "OPEN_EXR", Png => "PNG", Targa => "TARGA",
64 Tiff => "TIFF", Webp => "WEBP",
65 }
66}
67
68string_enum! {
69 pub enum RigType {
71 Biped => "biped", Quadruped => "quadruped", Hexapod => "hexapod",
72 Octopod => "octopod", Avian => "avian", Serpentine => "serpentine",
73 Aquatic => "aquatic", Others => "others",
74 }
75}
76
77string_enum_open! {
78 pub enum RigTypeResponse {
81 Biped => "biped", Quadruped => "quadruped", Hexapod => "hexapod",
82 Octopod => "octopod", Avian => "avian", Serpentine => "serpentine",
83 Aquatic => "aquatic", Others => "others",
84 }
85}
86
87string_enum! {
88 pub enum RigSpec {
90 Mixamo => "mixamo",
91 Tripo => "tripo",
92 }
93}
94
95string_enum! {
96 pub enum PostStyle {
98 Lego => "lego", Voxel => "voxel", Voronoi => "voronoi", Minecraft => "minecraft",
99 }
100}
101
102string_enum! {
103 pub enum Animation {
105 Idle => "preset:idle", Walk => "preset:walk", Run => "preset:run",
106 Dive => "preset:dive", Climb => "preset:climb", Jump => "preset:jump",
107 Slash => "preset:slash", Shoot => "preset:shoot", Hurt => "preset:hurt",
108 Fall => "preset:fall", Turn => "preset:turn",
109 QuadrupedWalk => "preset:quadruped:walk",
110 HexapodWalk => "preset:hexapod:walk",
111 OctopodWalk => "preset:octopod:walk",
112 SerpentineMarch => "preset:serpentine:march",
113 AquaticMarch => "preset:aquatic:march",
114 }
115}
116
117string_enum! {
118 pub enum Quality {
120 Standard => "standard",
121 Detailed => "detailed",
122 }
123}
124
125string_enum! {
126 pub enum TextureAlignment {
128 OriginalImage => "original_image",
129 Geometry => "geometry",
130 }
131}
132
133string_enum! {
134 pub enum Orientation {
136 Default => "default",
137 AlignImage => "align_image",
138 }
139}
140
141string_enum! {
142 pub enum FbxPreset {
144 Blender => "blender", Mixamo => "mixamo", ThreeDsMax => "3dsmax",
145 }
146}
147
148string_enum! {
149 pub enum ExportOrientation {
151 PlusX => "+x", PlusY => "+y", MinusX => "-x", MinusY => "-y",
152 }
153}
154
155string_enum! {
156 pub enum RigOutputFormat {
158 Glb => "glb",
159 Fbx => "fbx",
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166
167 #[test]
168 fn animation_serializes_with_preset_prefix() {
169 let json = serde_json::to_string(&Animation::QuadrupedWalk).unwrap();
170 assert_eq!(json, "\"preset:quadruped:walk\"");
171 }
172
173 #[test]
174 fn post_style_roundtrips() {
175 for s in [
176 PostStyle::Lego,
177 PostStyle::Voxel,
178 PostStyle::Voronoi,
179 PostStyle::Minecraft,
180 ] {
181 let j = serde_json::to_string(&s).unwrap();
182 let back: PostStyle = serde_json::from_str(&j).unwrap();
183 assert_eq!(s, back);
184 }
185 }
186
187 #[test]
188 fn strict_enum_rejects_unknown_value() {
189 let err = serde_json::from_str::<PostStyle>("\"brand_new_style\"").unwrap_err();
190 assert!(err.to_string().contains("unknown variant"), "{err}");
191 }
192
193 #[test]
194 fn strict_enum_rejects_wrong_case() {
195 let err = serde_json::from_str::<Quality>("\"Detailed\"").unwrap_err();
199 assert!(err.to_string().contains("unknown variant"), "{err}");
200 }
201
202 #[test]
203 fn open_enum_accepts_unknown_value() {
204 let got: RigTypeResponse = serde_json::from_str("\"brand_new_rig\"").unwrap();
205 assert_eq!(got, RigTypeResponse::Unknown);
206 }
207
208 #[test]
209 fn output_format_uppercase_wire() {
210 assert_eq!(
211 serde_json::to_string(&OutputFormat::ThreeMf).unwrap(),
212 "\"3MF\""
213 );
214 let gltf: OutputFormat = serde_json::from_str("\"GLTF\"").unwrap();
215 assert_eq!(gltf, OutputFormat::Gltf);
216 }
217
218 #[test]
219 fn export_orientation_sign_prefix() {
220 assert_eq!(
221 serde_json::to_string(&ExportOrientation::MinusY).unwrap(),
222 "\"-y\""
223 );
224 }
225}