tripo_api/tasks/
rig_model.rs1use serde::{Deserialize, Serialize};
4
5use crate::enums::{RigOutputFormat, RigSpec, RigType};
6use crate::error::{Error, Result};
7use crate::versions;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
12#[serde(deny_unknown_fields)]
13pub struct RigModelRequest {
14 pub original_model_task_id: String,
16 #[serde(skip_serializing_if = "Option::is_none")]
18 pub model_version: Option<String>,
19 #[serde(skip_serializing_if = "Option::is_none")]
21 pub out_format: Option<RigOutputFormat>,
22 #[serde(skip_serializing_if = "Option::is_none")]
24 pub rig_type: Option<RigType>,
25 #[serde(skip_serializing_if = "Option::is_none")]
27 pub spec: Option<RigSpec>,
28}
29
30impl RigModelRequest {
31 pub(crate) fn validate(&self) -> Result<()> {
37 let non_biped = matches!(
38 self.rig_type,
39 Some(
40 RigType::Avian
41 | RigType::Quadruped
42 | RigType::Hexapod
43 | RigType::Octopod
44 | RigType::Serpentine
45 | RigType::Aquatic
46 | RigType::Others
47 )
48 );
49 let biped_only_version = match self.model_version.as_deref() {
50 None | Some(versions::rig::V1_0) => true,
51 Some(_) => false,
52 };
53 if non_biped && biped_only_version {
54 return Err(Error::InvalidRequest(format!(
55 "rig_type {:?} requires model_version {} or {} — the default/v1.0 rigger only supports biped",
56 self.rig_type.as_ref().unwrap(),
57 versions::rig::V2_5,
58 versions::rig::V2_0,
59 )));
60 }
61 Ok(())
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68
69 fn req(rig_type: Option<RigType>, model_version: Option<&str>) -> RigModelRequest {
70 RigModelRequest {
71 original_model_task_id: "task_id".into(),
72 model_version: model_version.map(str::to_owned),
73 out_format: None,
74 rig_type,
75 spec: None,
76 }
77 }
78
79 #[test]
80 fn biped_with_default_version_ok() {
81 req(Some(RigType::Biped), None).validate().unwrap();
82 }
83
84 #[test]
85 fn no_rig_type_any_version_ok() {
86 req(None, None).validate().unwrap();
87 req(None, Some(versions::rig::V1_0)).validate().unwrap();
88 req(None, Some(versions::rig::V2_5)).validate().unwrap();
89 }
90
91 #[test]
92 fn avian_with_v1_is_rejected() {
93 let err = req(Some(RigType::Avian), None).validate().unwrap_err();
94 assert!(
95 matches!(err, Error::InvalidRequest(ref msg) if msg.contains("v2.5") && msg.contains("biped")),
96 "{err}"
97 );
98 let err = req(Some(RigType::Avian), Some(versions::rig::V1_0))
99 .validate()
100 .unwrap_err();
101 assert!(matches!(err, Error::InvalidRequest(_)));
102 }
103
104 #[test]
105 fn avian_with_v2_is_accepted() {
106 req(Some(RigType::Avian), Some(versions::rig::V2_0))
107 .validate()
108 .unwrap();
109 req(Some(RigType::Avian), Some(versions::rig::V2_5))
110 .validate()
111 .unwrap();
112 }
113
114 #[test]
115 fn non_biped_rig_types_all_rejected_on_v1() {
116 for t in [
117 RigType::Avian,
118 RigType::Quadruped,
119 RigType::Hexapod,
120 RigType::Octopod,
121 RigType::Serpentine,
122 RigType::Aquatic,
123 RigType::Others,
124 ] {
125 req(Some(t.clone()), None).validate().unwrap_err();
126 }
127 }
128}