carbone_sdk_rust/
carbone.rs

1use bytes::Bytes;
2
3use std::path::Path;
4use std::time::Duration;
5
6use reqwest::header;
7use reqwest::header::HeaderValue;
8use reqwest::multipart;
9use reqwest::Client;
10use reqwest::ClientBuilder;
11use reqwest::StatusCode;
12
13
14use crate::carbone_response::APIResponse;
15use crate::config::Config;
16use crate::errors::*;
17use crate::render::*;
18use crate::template::*;
19use crate::types::{ApiJsonToken, JsonData};
20
21use crate::types::Result;
22
23#[derive(Debug, Clone)]
24pub struct Carbone<'a> {
25    config: &'a Config,
26    http_client: Client,
27}
28
29impl<'a> Carbone<'a> {
30    pub fn new(config: &'a Config, api_token: Option<&'a ApiJsonToken>) -> Result<Self> {
31        let mut headers = header::HeaderMap::new();
32        headers.insert(
33            "carbone-version",
34            HeaderValue::from_str(config.api_version.as_str()).unwrap(),
35        );
36        if api_token != None 
37        {
38            let bearer = format!("Bearer {}", api_token.expect("REASON").as_str());
39
40            let mut auth_value = header::HeaderValue::from_str(bearer.as_str()).unwrap();
41            auth_value.set_sensitive(true);
42
43            headers.insert(header::AUTHORIZATION, auth_value);
44        }
45
46            let http_client = ClientBuilder::new()
47                .default_headers(headers)
48                .timeout(Duration::from_secs(config.api_timeout))
49                .build()?;
50
51        Ok(Self {
52            config,
53            http_client,
54        })
55    }
56
57    // Delete a template from the Carbone Service.
58    pub async fn delete_template(&self, template_id: TemplateId) -> Result<bool> {
59        let url = format!("{}/template/{}", self.config.api_url, template_id.as_str());
60
61        let response = self.http_client.delete(url).send().await?;
62
63        let json = response.json::<APIResponse>().await?;
64
65        if json.success {
66            Ok(true)
67        } else {
68            Err(CarboneError::Error(json.error.unwrap()))
69        }
70    }
71
72    // Download a template from the Carbone Service.
73    pub async fn download_template(&self, template_id: &TemplateId) -> Result<Bytes> {
74        let url = format!("{}/template/{}", self.config.api_url, template_id.as_str());
75
76        let response = self.http_client.get(url).send().await?;
77
78        if response.status() == StatusCode::OK {
79            Ok(response.bytes().await?)
80        } else {
81            let json = response.json::<APIResponse>().await?;
82            Err(CarboneError::Error(json.error.unwrap()))
83        }
84    }
85
86    /// Generate a report.
87    pub async fn generate_report(
88        &self,
89        template_name: String,
90        template_data: Vec<u8>,
91        json_data: JsonData,
92        payload: Option<&str>,
93        salt: Option<&str>
94    ) -> Result<Bytes> {
95
96        let template_id_generated = TemplateId::from_bytes(template_data.to_owned(), payload)?;
97        let mut template_id = template_id_generated;
98        let render_id;
99    
100        match self.render_data(template_id, json_data.clone()).await {
101            Ok(id) => {
102                render_id = Some(id);
103            }
104            Err(e) => match e {
105                CarboneError::HttpError { status_code, error_message } => {
106                    if status_code == reqwest::StatusCode::NOT_FOUND {
107                        template_id = self.upload_template(template_name.as_str(), template_data, salt).await?;
108                        render_id = Some(self.render_data(template_id, json_data).await?);
109                    } else {
110                        return Err(CarboneError::HttpError { status_code, error_message });
111                    }
112                },
113                CarboneError::Error(error_message) => {
114                    return Err(CarboneError::Error(error_message));
115                },
116                _ => {
117                    return Err(e);
118                }
119            }
120        };
121    
122        let report_content = self.get_report(&render_id.unwrap()).await?;
123    
124        Ok(report_content)
125    }
126
127
128    /// Get a new report.
129    pub async fn get_report(&self, render_id: &RenderId) -> Result<Bytes> {
130        let url = format!("{}/render/{}", self.config.api_url, render_id.as_str());
131
132        let response = self.http_client.get(url).send().await?;
133
134        // let mut report_name = None;
135
136        // if let Some(content_disposition) = response.headers().get("content-disposition") {
137        //     if let Ok(disposition) = content_disposition.to_str() {
138        //         let split_content_disposition: Vec<&str> = disposition.split('=').collect();
139
140        //         if split_content_disposition.len() == 2 {
141        //             let mut name = split_content_disposition[1].to_string();
142        //             if name.starts_with('"') && name.ends_with('"') {
143        //                 name = name[1..name.len() - 1].to_string();
144        //             }
145        //             report_name = Some(name);
146        //         }
147        //     }
148        // }
149
150        if response.status() == StatusCode::OK {
151            Ok(response.bytes().await?)
152        } else {
153            let json = response.json::<APIResponse>().await?;
154            Err(CarboneError::Error(json.error.unwrap()))
155        }
156    }
157
158    /// Generate a report with a template_id given.
159    pub async fn generate_report_with_template_id(
160        &self,
161        template_id: TemplateId,
162        json_data: JsonData,
163    ) -> Result<Bytes> {
164        let render_id = self.render_data(template_id, json_data).await?;
165        let report_content = self.get_report(&render_id).await?;
166
167        Ok(report_content)
168    }
169
170    /// Render data with a given template_id.
171    pub async fn render_data(
172        &self,
173        template_id: TemplateId,
174        json_data: JsonData,
175    ) -> Result<RenderId> {
176        let url = format!("{}/render/{}", self.config.api_url, template_id.as_str());
177
178        let response = self
179            .http_client
180            .post(url)
181            .header("Content-Type", "application/json")
182            .body(json_data.as_str().to_owned())
183            .send()
184            .await?;
185
186        if !response.status().is_success() {
187            let status_code = response.status();
188            let json = response.json::<APIResponse>().await?;
189            return Err(CarboneError::HttpError {
190                status_code,
191                error_message: json.error.unwrap_or_else(|| "Unknown error".to_string()),
192            });
193        }
194
195        let json = response.json::<APIResponse>().await?;
196
197        if json.success {
198            Ok(json.data.unwrap().render_id.unwrap())
199        } else {
200            Err(CarboneError::Error(json.error.unwrap()))
201        }
202    }
203
204    /// Upload a template to the Carbone Service.
205    pub async fn upload_template(
206        &self,
207        file_name: &str,
208        file_content: Vec<u8>,
209        salt: Option<&str>,
210    ) -> Result<TemplateId> {
211        let salt = match salt {
212            Some(s) => s.to_string(),
213            None => "".to_string(),
214        };
215
216        let file_path = Path::new(file_name);
217
218        let file_name = file_path
219            .file_name()
220            .map(|filename| filename.to_string_lossy().into_owned());
221
222        let file_name = match file_name {
223            Some(s) => s,
224            None => return Err(CarboneError::Error("Failed to fetch file name".to_string())),
225        };
226
227
228        let ext = file_path
229            .extension()
230            .and_then(|ext| ext.to_str()) .unwrap_or("");
231        let mime = mime_guess::from_ext(ext).first_or_octet_stream();
232
233        let part = multipart::Part::bytes(file_content)
234            .file_name(file_name)
235            .mime_str(mime.as_ref())?;
236
237        let form: multipart::Form = multipart::Form::new().text("", salt).part("template", part);
238
239        let url = format!("{}/template", self.config.api_url);
240
241
242        let response = self.http_client.post(url).multipart(form).send().await?;
243
244        let json = response.json::<APIResponse>().await?;
245
246        if json.success {
247            Ok(json.data.unwrap().template_id.unwrap())
248        } else {
249            Err(CarboneError::Error(json.error.unwrap()))
250        }
251    }
252
253
254    pub async fn get_status(&self) -> Result<String>
255    {
256        let url = format!("{}/status", self.config.api_url);
257
258        let response = self.http_client.get(url).send().await?;
259
260        if response.status() == StatusCode::OK {
261            let body = response.text().await?;
262            Ok(body)
263        } else {
264            let json = response.json::<APIResponse>().await?;
265            Err(CarboneError::Error(json.error.unwrap()))
266        }
267    }
268}