Skip to main content

tripo_api/
types.rs

1//! Core data types exposed by the public API.
2
3use std::collections::BTreeMap;
4
5use serde::{Deserialize, Serialize};
6
7/// Opaque task identifier.
8#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
9#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
10#[serde(transparent)]
11pub struct TaskId(pub String);
12
13impl TaskId {
14    /// Construct from any string-like.
15    #[must_use]
16    pub fn new(s: impl Into<String>) -> Self {
17        Self(s.into())
18    }
19    /// Borrow as `&str`.
20    #[must_use]
21    pub fn as_str(&self) -> &str {
22        &self.0
23    }
24}
25
26impl std::fmt::Display for TaskId {
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        f.write_str(&self.0)
29    }
30}
31
32impl From<String> for TaskId {
33    fn from(s: String) -> Self {
34        Self(s)
35    }
36}
37impl From<&str> for TaskId {
38    fn from(s: &str) -> Self {
39        Self(s.to_string())
40    }
41}
42
43/// Task lifecycle status.
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
45#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
46#[serde(rename_all = "lowercase")]
47pub enum TaskStatus {
48    /// Waiting in the queue.
49    Queued,
50    /// Currently being processed.
51    Running,
52    /// Completed successfully.
53    Success,
54    /// Completed with failure.
55    Failed,
56    /// User or system cancelled.
57    Cancelled,
58    /// Unknown / uncategorized.
59    Unknown,
60    /// Banned by moderation.
61    Banned,
62    /// Past retention.
63    Expired,
64}
65
66impl TaskStatus {
67    /// True for statuses that cause `wait_for_task` to stop polling.
68    #[must_use]
69    pub fn is_terminal(self) -> bool {
70        matches!(
71            self,
72            Self::Success | Self::Failed | Self::Cancelled | Self::Banned | Self::Expired
73        )
74    }
75}
76
77/// User account balance.
78#[derive(Debug, Clone, Deserialize, Serialize)]
79#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
80pub struct Balance {
81    /// Available credit balance.
82    pub balance: f64,
83    /// Reserved (in-flight) balance.
84    pub frozen: f64,
85}
86
87/// Server-side result of `upload_file`.
88#[derive(Debug, Clone, Deserialize, Serialize)]
89#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
90pub struct UploadedFile {
91    /// Opaque token to pass back as `ImageInput::FileToken`.
92    pub file_token: uuid::Uuid,
93}
94
95/// Download URLs and auxiliary output fields returned on the task object.
96#[derive(Debug, Clone, Default, Deserialize, Serialize)]
97#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
98pub struct TaskOutput {
99    /// URL for the main output model.
100    #[serde(default)]
101    pub model: Option<String>,
102    /// URL for the base (pre-texture) model.
103    #[serde(default)]
104    pub base_model: Option<String>,
105    /// URL for the PBR-textured model.
106    #[serde(default)]
107    pub pbr_model: Option<String>,
108    /// URL for a rendered preview image.
109    #[serde(default)]
110    pub rendered_image: Option<String>,
111    /// Populated by `check_riggable`.
112    #[serde(default)]
113    pub riggable: Option<bool>,
114    /// Populated by `check_riggable`.
115    #[serde(default)]
116    pub rig_type: Option<crate::enums::RigTypeResponse>,
117}
118
119/// Task record returned by `GET /task/{id}`.
120#[derive(Debug, Clone, Deserialize, Serialize)]
121#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
122pub struct Task {
123    /// Identifier.
124    pub task_id: TaskId,
125    /// Wire-format task type string (e.g. `text_to_model`, `animate_rig`).
126    #[serde(rename = "type")]
127    pub task_type: String,
128    /// Current status.
129    pub status: TaskStatus,
130    /// Echo of request parameters.
131    #[serde(default)]
132    pub input: BTreeMap<String, serde_json::Value>,
133    /// Output URLs and flags.
134    #[serde(default)]
135    pub output: TaskOutput,
136    /// Progress percent 0–100.
137    #[serde(default)]
138    pub progress: i32,
139    /// Unix seconds.
140    #[serde(default)]
141    pub create_time: i64,
142    /// Estimated seconds until completion; used by the polling backoff.
143    #[serde(default)]
144    pub running_left_time: Option<i64>,
145    /// Queue depth ahead of this task.
146    #[serde(default)]
147    pub queuing_num: Option<i32>,
148    /// Non-zero on failure.
149    #[serde(default)]
150    pub error_code: Option<i32>,
151    /// Human-readable error message.
152    #[serde(default)]
153    pub error_msg: Option<String>,
154}
155
156#[cfg(test)]
157mod tests {
158    use super::*;
159
160    #[test]
161    fn task_status_terminality() {
162        assert!(TaskStatus::Success.is_terminal());
163        assert!(TaskStatus::Failed.is_terminal());
164        assert!(TaskStatus::Banned.is_terminal());
165        assert!(!TaskStatus::Queued.is_terminal());
166        assert!(!TaskStatus::Running.is_terminal());
167        assert!(!TaskStatus::Unknown.is_terminal());
168    }
169
170    #[test]
171    fn deserializes_task_with_minimal_body() {
172        let body = r#"{
173            "task_id":"abc123","type":"text_to_model","status":"running","progress":42
174        }"#;
175        let task: Task = serde_json::from_str(body).unwrap();
176        assert_eq!(task.task_id.as_str(), "abc123");
177        assert_eq!(task.status, TaskStatus::Running);
178        assert_eq!(task.progress, 42);
179        assert!(task.output.model.is_none());
180    }
181}