Skip to main content

raps_da/
engines.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2024-2025 Dmytro Yemelianov
3
4//! Engine operations for the Design Automation API.
5
6use anyhow::{Context, Result};
7
8use raps_kernel::http;
9
10use crate::DesignAutomationClient;
11use crate::types::*;
12
13impl DesignAutomationClient {
14    /// Get the configured nickname (or "default")
15    pub fn nickname(&self) -> &str {
16        self.config.da_nickname.as_deref().unwrap_or("default")
17    }
18
19    /// Fetch the effective nickname from the DA API.
20    ///
21    /// Returns the configured nickname if set, otherwise calls
22    /// `GET /forgeapps/me` to get the actual owner name (usually the client_id).
23    pub async fn effective_nickname(&self) -> Result<String> {
24        if let Some(ref nick) = self.config.da_nickname {
25            return Ok(nick.clone());
26        }
27        let token = self.auth.get_token().await?;
28        let url = format!("{}/forgeapps/me", self.config.da_url());
29        let response = http::send_with_retry(&self.config.http_config, || {
30            self.http_client.get(&url).bearer_auth(&token)
31        })
32        .await?;
33        if response.status().is_success() {
34            let text = response.text().await.unwrap_or_default();
35            // Response is a plain string (the nickname) wrapped in quotes
36            let trimmed = text.trim().trim_matches('"');
37            if !trimmed.is_empty() {
38                return Ok(trimmed.to_string());
39            }
40        }
41        Ok("default".to_string())
42    }
43
44    /// List available engines
45    ///
46    /// Returns a list of engine IDs (e.g., "Autodesk.Revit+2024").
47    /// Use `get_engine` to fetch full details for a specific engine.
48    pub async fn list_engines(&self) -> Result<Vec<String>> {
49        let token = self.auth.get_token().await?;
50        let url = format!("{}/engines", self.config.da_url());
51
52        let response = http::send_with_retry(&self.config.http_config, || {
53            self.http_client.get(&url).bearer_auth(&token)
54        })
55        .await?;
56
57        if !response.status().is_success() {
58            let status = response.status();
59            let error_text = response.text().await.unwrap_or_default();
60            anyhow::bail!("Failed to list engines ({status}): {error_text}");
61        }
62
63        let paginated: PaginatedResponse<String> = response
64            .json()
65            .await
66            .context("Failed to parse engines response")?;
67
68        Ok(paginated.data)
69    }
70
71    /// List all engines with pagination, returning structured Engine objects.
72    ///
73    /// The API returns engine IDs as strings. This method parses the ID to
74    /// extract product name and version as the description.
75    pub async fn list_engines_detailed(&self) -> Result<Vec<Engine>> {
76        let token = self.auth.get_token().await?;
77        let base_url = format!("{}/engines", self.config.da_url());
78        let mut all_engines = Vec::new();
79        let mut page_token: Option<String> = None;
80
81        loop {
82            let url = match &page_token {
83                Some(tok) => format!("{base_url}?page={tok}"),
84                None => base_url.clone(),
85            };
86
87            let token_clone = token.clone();
88            let response = http::send_with_retry(&self.config.http_config, || {
89                self.http_client.get(&url).bearer_auth(&token_clone)
90            })
91            .await?;
92
93            if !response.status().is_success() {
94                let status = response.status();
95                let error_text = response.text().await.unwrap_or_default();
96                anyhow::bail!("Failed to list engines ({status}): {error_text}");
97            }
98
99            let paginated: PaginatedResponse<String> = response
100                .json()
101                .await
102                .context("Failed to parse engines response")?;
103
104            // Convert string IDs to Engine structs, parsing description from the ID.
105            // Format: "Autodesk.ProductName+VersionNumber"
106            for id in paginated.data {
107                let description = id
108                    .split('.')
109                    .next_back()
110                    .map(|s| s.replace('+', " "))
111                    .unwrap_or_default();
112                all_engines.push(Engine {
113                    id,
114                    description: Some(description),
115                    product_version: None,
116                });
117            }
118
119            match paginated.pagination_token {
120                Some(tok) if !tok.is_empty() => page_token = Some(tok),
121                _ => break,
122            }
123        }
124
125        Ok(all_engines)
126    }
127}