Skip to main content

raps_cli/commands/translate/
mod.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2024-2025 Dmytro Yemelianov
3
4//! Translation commands for Model Derivative API
5//!
6//! Commands for starting translations, checking status, viewing manifests,
7//! downloading derivatives, querying metadata, and managing presets.
8
9mod metadata;
10mod presets;
11mod translations;
12
13use anyhow::Result;
14use clap::Subcommand;
15use std::path::PathBuf;
16
17use crate::output::OutputFormat;
18use raps_derivative::DerivativeClient;
19
20#[derive(Debug, Subcommand)]
21pub enum TranslateCommands {
22    /// Start a translation job
23    Start {
24        /// Base64-encoded URN of the source file
25        urn: Option<String>,
26
27        /// Output format (svf2, svf, obj, stl, step, iges, ifc)
28        #[arg(short, long)]
29        format: Option<String>,
30
31        /// Root filename (for ZIP files with multiple design files)
32        #[arg(short, long)]
33        root_filename: Option<String>,
34
35        /// Wait for translation to complete (polls until done)
36        #[arg(short, long)]
37        wait: bool,
38
39        /// APS data center region (US, EMEA, AUS, CAN, DEU, IND, JPN, GBR)
40        #[arg(long, default_value = "US")]
41        region: String,
42
43        /// Force re-translation by deleting existing manifest.
44        /// Note: Prior versions always forced re-translation. The default is now
45        /// to preserve existing manifests.
46        #[arg(long)]
47        force: bool,
48    },
49
50    /// Check translation status
51    Status {
52        /// Base64-encoded URN of the source file
53        urn: String,
54
55        /// Wait for translation to complete
56        #[arg(short, long)]
57        wait: bool,
58    },
59
60    /// Get translation manifest (available derivatives)
61    Manifest {
62        /// Base64-encoded URN of the source file
63        urn: String,
64    },
65
66    /// List downloadable derivatives
67    Derivatives {
68        /// Base64-encoded URN of the source file
69        urn: String,
70
71        /// Filter by format (obj, stl, step, etc.)
72        #[arg(short, long)]
73        format: Option<String>,
74    },
75
76    /// Download translated derivatives
77    Download {
78        /// Base64-encoded URN of the source file
79        urn: String,
80
81        /// Download by format (obj, stl, step, etc.) - downloads all matching
82        #[arg(short, long)]
83        format: Option<String>,
84
85        /// Download specific derivative by GUID
86        #[arg(short, long)]
87        guid: Option<String>,
88
89        /// Output directory (defaults to current directory)
90        #[arg(long = "out-dir")]
91        out_dir: Option<PathBuf>,
92
93        /// Download all available derivatives
94        #[arg(short, long)]
95        all: bool,
96    },
97
98    /// List model views/viewables (metadata)
99    Metadata {
100        /// Base64-encoded URN of the source file
101        urn: String,
102
103        /// APS data center region (US, EMEA, AUS, CAN, DEU, IND, JPN, GBR)
104        #[arg(long)]
105        region: Option<String>,
106
107        /// Output format
108        #[arg(short, long, default_value = "table")]
109        output: String,
110    },
111
112    /// Get object tree hierarchy for a model view
113    Tree {
114        /// Base64-encoded URN of the source file
115        urn: String,
116
117        /// Model view GUID (from metadata command)
118        guid: String,
119
120        /// APS data center region (US, EMEA, AUS, CAN, DEU, IND, JPN, GBR)
121        #[arg(long)]
122        region: Option<String>,
123
124        /// Output format
125        #[arg(short, long, default_value = "table")]
126        output: String,
127    },
128
129    /// Get properties for a model view
130    Properties {
131        /// Base64-encoded URN of the source file
132        urn: String,
133
134        /// Model view GUID (from metadata command)
135        guid: String,
136
137        /// Filter by object ID
138        #[arg(long = "object-id")]
139        object_id: Option<i64>,
140
141        /// APS data center region (US, EMEA, AUS, CAN, DEU, IND, JPN, GBR)
142        #[arg(long)]
143        region: Option<String>,
144
145        /// Output format
146        #[arg(short, long, default_value = "table")]
147        output: String,
148    },
149
150    /// Query specific properties by object IDs
151    #[command(name = "query-properties")]
152    QueryProperties {
153        /// Base64-encoded URN of the source file
154        urn: String,
155
156        /// Model view GUID (from metadata command)
157        guid: String,
158
159        /// Comma-separated object IDs to filter (e.g., "1,2,3")
160        #[arg(short, long)]
161        filter: String,
162
163        /// Comma-separated field names to return
164        #[arg(long)]
165        fields: Option<String>,
166
167        /// APS data center region (US, EMEA, AUS, CAN, DEU, IND, JPN, GBR)
168        #[arg(long)]
169        region: Option<String>,
170
171        /// Output format
172        #[arg(short, long, default_value = "table")]
173        output: String,
174    },
175
176    /// Manage translation presets
177    #[command(subcommand)]
178    Preset(PresetCommands),
179}
180
181#[derive(Debug, Subcommand)]
182pub enum PresetCommands {
183    /// List available translation presets
184    List,
185
186    /// Show preset details
187    Show {
188        /// Preset name
189        name: String,
190    },
191
192    /// Create a new preset
193    Create {
194        /// Preset name
195        name: String,
196        /// Output format (svf2, obj, stl, step, etc.)
197        #[arg(short, long)]
198        format: String,
199        /// Description
200        #[arg(short, long)]
201        description: Option<String>,
202    },
203
204    /// Delete a preset
205    Delete {
206        /// Preset name
207        name: String,
208    },
209
210    /// Use a preset to start translation
211    Use {
212        /// Base64-encoded URN of the source file
213        urn: String,
214        /// Preset name
215        preset: String,
216    },
217}
218
219impl TranslateCommands {
220    pub async fn execute(
221        self,
222        client: &DerivativeClient,
223        output_format: OutputFormat,
224    ) -> Result<()> {
225        match self {
226            TranslateCommands::Start {
227                urn,
228                format,
229                root_filename,
230                wait,
231                region,
232                force,
233            } => {
234                translations::start_translation(
235                    client,
236                    urn,
237                    format,
238                    root_filename,
239                    wait,
240                    output_format,
241                    region,
242                    force,
243                )
244                .await
245            }
246            TranslateCommands::Status { urn, wait } => {
247                translations::check_status(client, &urn, wait, output_format).await
248            }
249            TranslateCommands::Manifest { urn } => {
250                translations::show_manifest(client, &urn, output_format).await
251            }
252            TranslateCommands::Derivatives { urn, format } => {
253                translations::list_derivatives(client, &urn, format, output_format).await
254            }
255            TranslateCommands::Download {
256                urn,
257                format,
258                guid,
259                out_dir,
260                all,
261            } => {
262                translations::download_derivatives(
263                    client,
264                    &urn,
265                    format,
266                    guid,
267                    out_dir,
268                    all,
269                    output_format,
270                )
271                .await
272            }
273            TranslateCommands::Metadata {
274                urn,
275                region,
276                output,
277            } => metadata::show_metadata(client, &urn, region, &output).await,
278            TranslateCommands::Tree {
279                urn,
280                guid,
281                region,
282                output,
283            } => metadata::show_object_tree(client, &urn, &guid, region, &output).await,
284            TranslateCommands::Properties {
285                urn,
286                guid,
287                object_id,
288                region,
289                output,
290            } => metadata::show_properties(client, &urn, &guid, object_id, region, &output).await,
291            TranslateCommands::QueryProperties {
292                urn,
293                guid,
294                filter,
295                fields,
296                region,
297                output,
298            } => {
299                metadata::query_properties(client, &urn, &guid, &filter, fields, region, &output)
300                    .await
301            }
302            TranslateCommands::Preset(cmd) => cmd.execute(client, output_format).await,
303        }
304    }
305}
306
307impl PresetCommands {
308    pub async fn execute(
309        self,
310        client: &DerivativeClient,
311        output_format: OutputFormat,
312    ) -> Result<()> {
313        match self {
314            PresetCommands::List => presets::list_presets(output_format),
315            PresetCommands::Show { name } => presets::show_preset(&name, output_format),
316            PresetCommands::Create {
317                name,
318                format,
319                description,
320            } => presets::create_preset(&name, &format, description, output_format),
321            PresetCommands::Delete { name } => presets::delete_preset(&name, output_format),
322            PresetCommands::Use { urn, preset } => {
323                presets::use_preset(client, &urn, &preset, output_format).await
324            }
325        }
326    }
327}
328
329#[cfg(test)]
330mod tests {
331    use super::translations::TRANSLATE_POLL_TIMEOUT;
332    use std::time::Duration;
333
334    #[test]
335    fn test_translate_poll_timeout_is_two_hours() {
336        assert_eq!(TRANSLATE_POLL_TIMEOUT, Duration::from_secs(7200));
337        assert_eq!(TRANSLATE_POLL_TIMEOUT.as_secs() / 3600, 2);
338    }
339}