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<()> {
38 let non_biped = matches!(
39 self.rig_type,
40 Some(
41 RigType::Avian
42 | RigType::Quadruped
43 | RigType::Hexapod
44 | RigType::Octopod
45 | RigType::Serpentine
46 | RigType::Aquatic
47 | RigType::Others
48 )
49 );
50 let biped_only_version = match self.model_version.as_deref() {
51 None | Some(versions::rig::V1_0) => true,
52 Some(_) => false,
53 };
54 if non_biped && biped_only_version {
55 return Err(Error::InvalidRequest(format!(
56 "rig_type {:?} requires model_version {} — the default/v1.0 rigger only supports biped",
57 self.rig_type.as_ref().unwrap(),
58 versions::rig::V2_5,
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 #[allow(deprecated)]
109 req(Some(RigType::Avian), Some(versions::rig::V2_0))
110 .validate()
111 .unwrap();
112 req(Some(RigType::Avian), Some(versions::rig::V2_5))
113 .validate()
114 .unwrap();
115 }
116
117 #[test]
118 fn non_biped_rig_types_all_rejected_on_v1() {
119 for t in [
120 RigType::Avian,
121 RigType::Quadruped,
122 RigType::Hexapod,
123 RigType::Octopod,
124 RigType::Serpentine,
125 RigType::Aquatic,
126 RigType::Others,
127 ] {
128 req(Some(t.clone()), None).validate().unwrap_err();
129 }
130 }
131}