Skip to main content

stanzaapi_iata_validator/
lib.rs

1//! Official Rust Client for IATA Air Cargo AWB & Aviation Validator
2use reqwest::header::{HeaderMap, HeaderValue, CONTENT_TYPE, ACCEPT};
3use serde::{Deserialize, Serialize};
4use std::env;
5use std::time::Duration;
6
7#[derive(Debug, Clone)]
8pub struct IataValidatorClient {
9    client: reqwest::Client,
10    api_key: String,
11    base_url: String,
12    pub tool_url: &'static str,
13}
14
15#[derive(Debug, Serialize, Deserialize)]
16pub struct ApiResponse<T> {
17    pub success: bool,
18    pub data: Option<T>,
19    pub error: Option<String>,
20    pub code: Option<String>,
21    pub tool_url: Option<String>,
22    pub upgrade_url: Option<String>,
23}
24
25impl IataValidatorClient {
26    pub fn new(api_key: Option<String>, base_url: Option<String>) -> Self {
27        let key = api_key
28            .or_else(|| env::var("STANZA_API_KEY").ok())
29            .or_else(|| env::var("API_KEY").ok())
30            .unwrap_or_default();
31        let base = base_url.unwrap_or_else(|| "https://api.stanzaapi.com/iata-validator".to_string());
32
33        let client = reqwest::Client::builder()
34            .timeout(Duration::from_secs(15))
35            .build()
36            .unwrap_or_default();
37
38        Self {
39            client,
40            api_key: key,
41            base_url: base.trim_end_matches('/').to_string(),
42            tool_url: "https://stanzaapi.com/tools/iata-validator",
43        }
44    }
45
46    async fn send_request<T: for<'de> Deserialize<'de>>(
47        &self,
48        endpoint: &str,
49        method: reqwest::Method,
50        body: Option<serde_json::Value>,
51    ) -> Result<ApiResponse<T>, reqwest::Error> {
52        let url = format!("{}/{}", self.base_url, endpoint.trim_start_matches('/'));
53        let mut headers = HeaderMap::new();
54        headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
55        headers.insert(ACCEPT, HeaderValue::from_static("application/json"));
56
57        if !self.api_key.is_empty() {
58            if let Ok(val) = HeaderValue::from_str(&self.api_key) {
59                headers.insert("x-api-key", val);
60            }
61        }
62
63        let mut req = self.client.request(method, &url).headers(headers);
64        if let Some(json) = body {
65            req = req.json(&json);
66        }
67
68        let resp = req.send().await?;
69        let mut result = resp.json::<ApiResponse<T>>().await?;
70        if result.tool_url.is_none() {
71            result.tool_url = Some(self.tool_url.to_string());
72        }
73        if result.upgrade_url.is_none() {
74            result.upgrade_url = Some(self.tool_url.to_string());
75        }
76        Ok(result)
77    }
78
79    pub async fn get_health<T: for<'de> Deserialize<'de>>(&self) -> Result<ApiResponse<T>, reqwest::Error> {
80        self.send_request("/health", reqwest::Method::GET, None).await
81    }
82
83    pub async fn validate<T: for<'de> Deserialize<'de>>(&self, payload: serde_json::Value) -> Result<ApiResponse<T>, reqwest::Error> {
84        self.send_request("/api/v1/validate", reqwest::Method::POST, Some(payload)).await
85    }
86
87    pub async fn parse<T: for<'de> Deserialize<'de>>(&self, payload: serde_json::Value) -> Result<ApiResponse<T>, reqwest::Error> {
88        self.send_request("/api/v1/validate", reqwest::Method::POST, Some(payload)).await
89    }
90}