1use serde::{Deserialize, Serialize};
2use serde_json;
3use std::collections::HashMap;
4
5#[cfg(feature = "schema")]
6use schemars::JsonSchema;
7
8#[derive(Debug, Clone)]
9pub struct Config {
10 pub base_url: String,
12}
13
14#[derive(Serialize, Deserialize, Debug, Clone)]
15#[cfg_attr(feature = "schema", derive(JsonSchema))]
16
17pub struct CameraPositionAngles {
18 #[serde(rename = "xzAngle")]
19 pub xz_angle: f64,
20 #[serde(rename = "yAngle")]
21 pub y_angle: f64,
22}
23
24#[derive(Serialize, Deserialize, Debug, Clone)]
25#[cfg_attr(feature = "schema", derive(JsonSchema))]
26
27pub struct CameraPositionFixed {
28 pub position: Vector3,
29 #[serde(rename = "lookAt")]
30 pub look_at: Vector3,
31}
32
33#[derive(Serialize, Deserialize, Debug, Clone)]
34#[cfg_attr(feature = "schema", derive(JsonSchema))]
35#[serde(tag = "type", rename_all = "camelCase")]
36pub enum SandboxRenderCameraPositioning {
37 Fixed(CameraPositionFixed),
38 Angles(CameraPositionAngles),
39}
40
41#[derive(Serialize, Deserialize, Debug, Clone)]
42#[cfg_attr(feature = "schema", derive(JsonSchema))]
43
44pub struct Vector3 {
45 pub x: f64,
46 pub y: f64,
47 pub z: f64,
48}
49
50#[derive(Serialize, Deserialize, Debug, Clone)]
51#[cfg_attr(feature = "schema", derive(JsonSchema))]
52#[serde(rename_all = "snake_case")]
53pub enum SandboxRenderLightSetup {
54 DynamicLightCamCube,
55 DynamicLightPhong,
56 HdriLobeUm5cM6P,
57 #[serde(rename = "ig.Configurator")]
58 IgConfigurator,
59 #[serde(rename = "ig.Configurator.PBR_Cubemap")]
60 IgConfiguratorPBRCubemap,
61 #[serde(rename = "ig.Online")]
62 IgOnline,
63 Showcase,
64}
65#[derive(Serialize, Deserialize, Debug, Clone)]
66#[cfg_attr(feature = "schema", derive(JsonSchema))]
67#[serde(rename_all = "snake_case")]
68pub enum SandboxRenderFloor {
69 PlainWhite,
70 ShowcaseMirror,
71}
72
73#[derive(Serialize, Deserialize, Debug, Clone)]
74#[cfg_attr(feature = "schema", derive(JsonSchema))]
75#[serde(rename_all = "camelCase")]
76pub enum SandboxRenderMode {
77 Perspective,
78 Orthographic,
79 Architecture,
80}
81
82#[derive(Serialize, Deserialize, Debug, Clone)]
83#[cfg_attr(feature = "schema", derive(JsonSchema))]
84#[serde(rename_all = "camelCase")]
85pub enum SandboxRenderRenderMode {
86 Pictogram,
87}
88
89#[derive(Serialize, Deserialize, Debug, Clone)]
90#[cfg_attr(feature = "schema", derive(JsonSchema))]
91pub struct SandboxRenderConfiguration {
92 #[serde(rename = "lightSetup")]
93 pub light_setup: SandboxRenderLightSetup,
94 pub floor: SandboxRenderFloor,
95 pub mode: SandboxRenderMode,
96 #[serde(skip_serializing_if = "Option::is_none", rename = "renderMode")]
97 pub render_mode: Option<SandboxRenderRenderMode>,
98 #[serde(
99 skip_serializing_if = "Option::is_none",
100 rename = "transparentBackground"
101 )]
102 pub transparent_background: Option<bool>,
103 #[serde(skip_serializing_if = "Option::is_none")]
104 pub fov: Option<f64>,
105 #[serde(rename = "cameraPositioning")]
106 pub camera_positioning: SandboxRenderCameraPositioning,
107 pub width: u32,
108 pub height: u32,
109}
110
111#[derive(Serialize, Deserialize, Debug, Clone)]
112pub struct SandboxRenderRequest {
113 pub oc: HashMap<String, serde_json::Value>,
114 pub configurations: Vec<SandboxRenderConfiguration>,
115}
116
117#[derive(Serialize, Deserialize, Debug, Clone)]
118pub struct SandboxRenderResultSuccess {
119 #[serde(rename = "mimeType")]
120 pub mime_type: String,
121 pub content: String,
122}
123
124#[derive(Serialize, Deserialize, Debug, Clone)]
125pub struct SandboxRenderResultError {
126 pub message: String,
127}
128
129#[derive(Serialize, Deserialize, Debug, Clone)]
130#[serde(tag = "status", rename_all = "camelCase")]
131pub enum SandboxRenderResult {
132 Success(SandboxRenderResultSuccess),
133 Error(SandboxRenderResultError),
134}
135
136#[derive(Serialize, Deserialize, Debug, Clone)]
137pub struct SandboxRenderResponse {
138 pub results: Vec<SandboxRenderResult>,
139}
140
141#[derive(Serialize, Deserialize, Debug, Clone)]
142pub struct ConfiguratorRenderRequest {
143 #[serde(rename = "configuratorUrl")]
144 pub configurator_url: String,
145 pub pin: String,
146}
147
148#[derive(Serialize, Deserialize, Debug, Clone)]
149pub struct ConfiguratorInternalRenderResponseItem {
150 #[serde(rename = "mimeType")]
151 pub mime_type: String,
152 pub content: String,
153 pub identifier: String,
154 #[serde(rename = "attachmentType")]
155 pub attachment_type: String,
156}
157
158#[derive(Serialize, Deserialize, Debug, Clone)]
159pub struct ConfiguratorRenderResponseSuccess {
160 pub images: Vec<ConfiguratorInternalRenderResponseItem>,
161}
162
163#[derive(Serialize, Deserialize, Debug, Clone)]
164pub struct ConfiguratorRenderResponseError {
165 pub message: String,
166}
167
168#[derive(Serialize, Deserialize, Debug, Clone)]
169#[serde(tag = "status", rename_all = "camelCase")]
170pub enum ConfiguratorRenderResponse {
171 Success(ConfiguratorRenderResponseSuccess),
172 Error(ConfiguratorRenderResponseError),
173}
174
175#[cfg(feature = "client")]
176mod async_feature_module {
177
178 use anyhow::{anyhow, Result};
179 use reqwest::{Client, Url};
180 use std::time::Duration;
181
182 use crate::*;
183
184 pub async fn request_renderings(
185 config: &Config,
186 request: &SandboxRenderRequest,
187 client: Option<&Client>,
188 ) -> Result<SandboxRenderResponse> {
189 let client = match client {
190 Some(client) => client,
191 _ => &Client::new(),
192 };
193 let mut url = Url::parse(&config.base_url)?;
194 url = url.join("api/render")?;
195 let res = client
196 .post(url)
197 .json(request)
198 .timeout(Duration::from_secs(5 * 60))
199 .send()
200 .await?;
201 let status = res.status();
202 let text = res.text().await?;
203 if !status.is_success() {
204 return Err(anyhow!("HTTP error: {}", text));
205 }
206 let parsed: SandboxRenderResponse =
207 serde_json::de::from_str(&text).map_err(|e| anyhow!("Parse error: {e}"))?;
208 Ok(parsed)
209 }
210
211 pub async fn request_configurator_renderings(
212 config: &Config,
213 request: &ConfiguratorRenderRequest,
214 client: Option<&Client>,
215 ) -> Result<ConfiguratorRenderResponse> {
216 let client = match client {
217 Some(client) => client,
218 _ => &Client::new(),
219 };
220
221 let mut url = Url::parse(&config.base_url)?;
222 url = url.join("api/renderConfigurator")?;
223 let res = client
224 .post(url)
225 .json(request)
226 .timeout(Duration::from_secs(5 * 60))
227 .send()
228 .await?;
229 let status = res.status();
230 let text = res.text().await?;
231 if !status.is_success() {
232 return Err(anyhow!("HTTP error: {}", text));
233 }
234 let parsed: ConfiguratorRenderResponse =
235 serde_json::de::from_str(&text).map_err(|e| anyhow!("Parse error: {e}"))?;
236 Ok(parsed)
237 }
238}
239
240#[cfg(feature = "client")]
241pub use async_feature_module::*;