Skip to main content

tripo_api/
enums.rs

1//! Shared typed enums used across request and response structs.
2//!
3//! Two flavors:
4//! - `string_enum!` — strict. Unknown wire values fail to deserialize. Used
5//!   for request payloads so typos in user-supplied config (RON, CLI args)
6//!   surface as errors instead of silently becoming an `Unknown` variant.
7//! - `string_enum_open!` — forward-compatible. Adds a `#[serde(other)]
8//!   Unknown` catchall so new server-side values don't break response parsing.
9
10use 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            /// Unknown value received from a forward-compatible server.
41            #[serde(other)]
42            Unknown,
43        }
44    };
45}
46
47string_enum! {
48    /// Output mesh format accepted by `convert_model`.
49    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    /// Texture image format for `convert_model`.
61    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    /// Biological rig classification. Strict — used in `rig_model` requests.
70    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    /// Rig classification reported by `check_riggable`. Forward-compatible —
79    /// unrecognized server values deserialize as `Unknown`.
80    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    /// Target rigging convention.
89    pub enum RigSpec {
90        Mixamo => "mixamo",
91        Tripo  => "tripo",
92    }
93}
94
95string_enum! {
96    /// Post-processing stylization preset.
97    pub enum PostStyle {
98        Lego => "lego", Voxel => "voxel", Voronoi => "voronoi", Minecraft => "minecraft",
99    }
100}
101
102string_enum! {
103    /// Animation preset (`retarget_animation`). Values are prefixed `preset:`.
104    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    /// Texture quality level (`texture_quality`).
119    ///
120    /// `extreme` (added in API 1.9.7, June 2026) is the top tier — Tripo bills
121    /// it as the highest-resolution PBR texture output. It is valid only for
122    /// `texture_quality`, not `geometry_quality`; see [`GeometryQuality`].
123    pub enum TextureQuality {
124        Standard => "standard",
125        Detailed => "detailed",
126        Extreme  => "extreme",
127    }
128}
129
130string_enum! {
131    /// Geometry quality level (`geometry_quality`).
132    ///
133    /// Note: unlike [`TextureQuality`], there is no `extreme` geometry tier.
134    pub enum GeometryQuality {
135        Standard => "standard",
136        Detailed => "detailed",
137    }
138}
139
140string_enum! {
141    /// Texture alignment strategy (image-to-model / texture-model).
142    pub enum TextureAlignment {
143        OriginalImage => "original_image",
144        Geometry => "geometry",
145    }
146}
147
148string_enum! {
149    /// Output orientation hint (image-to-model).
150    pub enum Orientation {
151        Default => "default",
152        AlignImage => "align_image",
153    }
154}
155
156string_enum! {
157    /// FBX preset selection (`convert_model`).
158    pub enum FbxPreset {
159        Blender => "blender", Mixamo => "mixamo", ThreeDsMax => "3dsmax",
160    }
161}
162
163string_enum! {
164    /// Export orientation vector (`convert_model`).
165    pub enum ExportOrientation {
166        PlusX => "+x", PlusY => "+y", MinusX => "-x", MinusY => "-y",
167    }
168}
169
170string_enum! {
171    /// Output file format for `rig_model` / `retarget_animation`.
172    pub enum RigOutputFormat {
173        Glb => "glb",
174        Fbx => "fbx",
175    }
176}
177
178#[cfg(test)]
179mod tests {
180    use super::*;
181
182    #[test]
183    fn animation_serializes_with_preset_prefix() {
184        let json = serde_json::to_string(&Animation::QuadrupedWalk).unwrap();
185        assert_eq!(json, "\"preset:quadruped:walk\"");
186    }
187
188    #[test]
189    fn post_style_roundtrips() {
190        for s in [
191            PostStyle::Lego,
192            PostStyle::Voxel,
193            PostStyle::Voronoi,
194            PostStyle::Minecraft,
195        ] {
196            let j = serde_json::to_string(&s).unwrap();
197            let back: PostStyle = serde_json::from_str(&j).unwrap();
198            assert_eq!(s, back);
199        }
200    }
201
202    #[test]
203    fn strict_enum_rejects_unknown_value() {
204        let err = serde_json::from_str::<PostStyle>("\"brand_new_style\"").unwrap_err();
205        assert!(err.to_string().contains("unknown variant"), "{err}");
206    }
207
208    #[test]
209    fn strict_enum_rejects_wrong_case() {
210        // Guards against the footgun where e.g. `"Detailed"` silently became
211        // `TextureQuality::Unknown` instead of erroring, since the wire value
212        // is lowercase `"detailed"`.
213        let err = serde_json::from_str::<TextureQuality>("\"Detailed\"").unwrap_err();
214        assert!(err.to_string().contains("unknown variant"), "{err}");
215    }
216
217    #[test]
218    fn texture_quality_has_extreme_tier() {
219        let j = serde_json::to_string(&TextureQuality::Extreme).unwrap();
220        assert_eq!(j, "\"extreme\"");
221        let back: TextureQuality = serde_json::from_str("\"extreme\"").unwrap();
222        assert_eq!(back, TextureQuality::Extreme);
223        // `extreme` is texture-only — it must not deserialize as a geometry tier.
224        assert!(serde_json::from_str::<GeometryQuality>("\"extreme\"").is_err());
225    }
226
227    #[test]
228    fn open_enum_accepts_unknown_value() {
229        let got: RigTypeResponse = serde_json::from_str("\"brand_new_rig\"").unwrap();
230        assert_eq!(got, RigTypeResponse::Unknown);
231    }
232
233    #[test]
234    fn output_format_uppercase_wire() {
235        assert_eq!(
236            serde_json::to_string(&OutputFormat::ThreeMf).unwrap(),
237            "\"3MF\""
238        );
239        let gltf: OutputFormat = serde_json::from_str("\"GLTF\"").unwrap();
240        assert_eq!(gltf, OutputFormat::Gltf);
241    }
242
243    #[test]
244    fn export_orientation_sign_prefix() {
245        assert_eq!(
246            serde_json::to_string(&ExportOrientation::MinusY).unwrap(),
247            "\"-y\""
248        );
249    }
250}