dhl_tracking/
lib.rs

1use std::error::Error;
2
3static BASE_URL_PRODUCTION: &str =
4    "https://cig.dhl.de/services/production/rest/sendungsverfolgung?xml=";
5static BASE_URL_SANDBOX: &str = "https://cig.dhl.de/services/sandbox/rest/sendungsverfolgung?xml=";
6
7static SANDBOX_XML_PARAM: &str = r#"<?xml version="1.0" encoding="UTF-8" standalone="no"?> <data appname="zt12345" language-code="{language_code}" password="geheim" piece-code="00340434161094022115" request="d-get-piece-detail"/>"#;
8static PRODUCTION_XML_PARAM: &str = r#"<?xml version="1.0" encoding="UTF-8" standalone="no"?> <data appname="{zt_kennung}" language-code="{language_code}" password="{passwd_zt_kennung}" piece-code="{sendungsnummer}" request="d-get-piece-detail"/>"#;
9/// Builds Sendungsverfolgung Struct for Sandbox
10/// ```
11/// use dhl_tracking::SendungsverfolgungBuilder;
12///
13/// fn main() {
14///    let sv = SendungsverfolgungBuilder::new()
15///        .sandbox(true)
16///        .passwd_entwicklerportal("your login-password entwicklerportal".to_string())
17///        .entwickler_id("EntwicklerID from Konto".to_owned())
18///        .build()
19///        .unwrap();
20///    println!("{:?}", sv.get_piece_detail("00340434161094022115").unwrap());
21/// }    
22/// ```
23///
24/// Builds Sendungsverfolgung Struct for Production
25/// ```
26/// use dhl_tracking::SendungsverfolgungBuilder;
27///
28/// let sendungsverfolgung = SendungsverfolgungBuilder::new()
29/// .zt_kennung("ztxxxxx".to_owned())
30/// .passwd_zt_kennung("your password".to_owned())
31/// .app_token("your token".to_owned())
32/// .app_id("your app id".to_owned())
33/// .sandbox(false)
34/// .build()
35/// .unwrap();
36///
37/// let delivery_data = sendungsverfolgung
38/// .get_piece_detail("00300000000000000000")
39/// .unwrap();
40/// ```
41pub struct SendungsverfolgungBuilder {
42    zt_kennung: Option<String>,
43    passwd_zt_kennung: Option<String>,
44    entwickler_id: Option<String>,
45    passwd_entwicklerportal: Option<String>,
46    language_code: Option<String>,
47    sandbox: Option<bool>,
48    app_id: Option<String>,
49    app_token: Option<String>,
50}
51
52impl Default for SendungsverfolgungBuilder {
53    fn default() -> Self {
54        Self::new()
55    }
56}
57impl SendungsverfolgungBuilder {
58    pub fn new() -> SendungsverfolgungBuilder {
59        SendungsverfolgungBuilder {
60            zt_kennung: None,
61            passwd_zt_kennung: None,
62            entwickler_id: None,
63            passwd_entwicklerportal: None,
64            language_code: Some("de".to_string()),
65            sandbox: None,
66            app_id: None,
67            app_token: None,
68        }
69    }
70
71    /// Zu finden im Entwicklerportal in den Kontoeinstellungen.
72    pub fn entwickler_id(mut self, entwickler_id: String) -> SendungsverfolgungBuilder {
73        self.entwickler_id = Some(entwickler_id);
74        self
75    }
76    /// Passwort mit dem man sich im Entwicklerportal einloggt.
77    pub fn passwd_entwicklerportal(
78        mut self,
79        passwd_entwicklerportal: String,
80    ) -> SendungsverfolgungBuilder {
81        self.passwd_entwicklerportal = Some(passwd_entwicklerportal);
82        self
83    }
84    /// Erhält man von seinem Vertriebsmitarbeiter
85    pub fn zt_kennung(mut self, zt_kennung: String) -> SendungsverfolgungBuilder {
86        self.zt_kennung = Some(zt_kennung);
87        self
88    }
89    /// Das Passwort das man zur ZT-Kennung erhalten hat.
90    pub fn passwd_zt_kennung(mut self, passwd_zt_kennung: String) -> SendungsverfolgungBuilder {
91        self.passwd_zt_kennung = Some(passwd_zt_kennung);
92        self
93    }
94    /// Sandbox Modus aktivieren.
95    pub fn sandbox(mut self, sandbox: bool) -> SendungsverfolgungBuilder {
96        self.sandbox = Some(sandbox);
97        self
98    }
99    /// Sprache in der die Antwort zurückgegeben werden soll.
100    /// Mögliche Werte: de und en.
101    pub fn language_code(mut self, language_code: String) -> SendungsverfolgungBuilder {
102        self.language_code = Some(language_code);
103        self
104    }
105    /// App Token zu App ID. Findet sich im Entwicklerportal unter Paket APIs -> Freigabe und Betrieb -> Details -> Token anzeigen.
106    pub fn app_token(mut self, app_token: String) -> SendungsverfolgungBuilder {
107        self.app_token = Some(app_token);
108        self
109    }
110    /// App ID. Findet sich im Entwicklerportal unter Paket APIs -> Freigabe und Betrieb.
111    pub fn app_id(mut self, app_id: String) -> SendungsverfolgungBuilder {
112        self.app_id = Some(app_id);
113        self
114    }
115
116    /// Ezeugt neue Instanz von Sendungsverfolgung oder gibt Fehler zurück.
117    pub fn build(self) -> Result<Sendungsverfolgung, Box<dyn Error>> {
118        let sandbox = self.sandbox.unwrap_or(true); //If not set, use sandbox
119        let entwickler_id = match self.entwickler_id {
120            Some(entwickler_id) => Some(entwickler_id),
121            None => {
122                if sandbox {
123                    //Sandbox needs entwickler_id
124                    return Err("Entwickler ID not set. Needet in sandbox mode.".into());
125                } else {
126                    None
127                }
128            }
129        };
130        let zt_kennung = match self.zt_kennung {
131            Some(u) => Some(u),
132            None => {
133                if !sandbox {
134                    //Production needs zt_kennung
135                    Err("Can't build Sendungsverfolgung, zt_kennung not set. Needet in production mode.")?;
136                }
137                None
138            }
139        };
140        let passwd_zt_kennung = match self.passwd_zt_kennung {
141            Some(p) => Some(p),
142            None => {
143                if !sandbox {
144                    return Err("Can't build Sendungsverfolgung, passwd_zt_kennung not set. Needet in production mode.".into());
145                }
146                None
147            }
148        };
149        let passwd_entwicklerportal = match self.passwd_entwicklerportal {
150            Some(p) => Some(p),
151            None => {
152                if sandbox {
153                    return Err(
154                        "Can't build Sendungsverfolgung, passwd_entwicklerportal not set. Needet in sandbox mode".into(),
155                    );
156                }
157                None
158            }
159        };
160        let app_token = match self.app_token {
161            Some(l) => Some(l),
162            None => {
163                if !sandbox {
164                    return Err("Can't build Sendungsverfolgung, app_token not set. Needet in production mode.".into());
165                }
166                None
167            }
168        };
169        let app_id = match self.app_id {
170            Some(l) => Some(l),
171            None => {
172                if !sandbox {
173                    return Err("Can't build Sendungsverfolgung, app_id not set. Needet in production mode.".into());
174                }
175                None
176            }
177        };
178        let language_code = match self.language_code {
179            Some(l) => l,
180            None => "de".to_string(),
181        };
182        Ok(Sendungsverfolgung {
183            zt_kennung,
184            passwd_zt_kennung,
185            entwickler_id,
186            passwd_entwicklerportal,
187            language_code,
188            sandbox,
189            app_id,
190            app_token,
191        })
192    }
193}
194
195/// Builds Sendungsverfolgung Struct for Sandbox
196/// ```
197/// use dhl_tracking::SendungsverfolgungBuilder;
198///
199/// fn main() {
200///    let sv = SendungsverfolgungBuilder::new()
201///        .sandbox(true)
202///        .passwd_entwicklerportal("your login-password entwicklerportal".to_string())
203///        .entwickler_id("EntwicklerID from Konto".to_owned())
204///        .build()
205///        .unwrap();
206///    println!("{:?}", sv.get_piece_detail("00340434161094022115").unwrap());
207/// }    
208/// ```
209///
210/// Builds Sendungsverfolgung Struct for Production
211/// ```
212/// use dhl_tracking::SendungsverfolgungBuilder;
213///
214/// let sendungsverfolgung = SendungsverfolgungBuilder::new()
215/// .zt_kennung("ztxxxxx".to_owned())
216/// .passwd_zt_kennung("your password".to_owned())
217/// .app_token("your token".to_owned())
218/// .app_id("your app id".to_owned())
219/// .sandbox(false)
220/// .build()
221/// .unwrap();
222///
223/// let delivery_data = sendungsverfolgung
224/// .get_piece_detail("00300000000000000000")
225/// .unwrap();
226/// ```
227#[derive(Debug)]
228pub struct Sendungsverfolgung {
229    zt_kennung: Option<String>,
230    passwd_zt_kennung: Option<String>,
231    entwickler_id: Option<String>,
232    passwd_entwicklerportal: Option<String>,
233    language_code: String,
234    sandbox: bool,
235    app_id: Option<String>,
236    app_token: Option<String>,
237}
238impl Sendungsverfolgung {
239    /// Retrieves shipment number data from DHL and returns either the body of the response or an HTTP error.
240    /// Ruft Daten zur Sendungsnummer von DHL ab und gibt entweder den Body der Antwort zurück oder einen HTTP Fehler.
241    pub fn get_piece_detail(
242        &self,
243        sendungsnummer: &str,
244    ) -> Result<std::string::String, Box<dyn Error>> {
245        let mut xml_param = {
246            if self.sandbox {
247                SANDBOX_XML_PARAM.to_string()
248            } else {
249                PRODUCTION_XML_PARAM.to_string()
250            }
251        };
252        xml_param = xml_param.replace("{language_code}", &self.language_code);
253
254        if !self.sandbox {
255            xml_param = xml_param.replace("{sendungsnummer}", sendungsnummer);
256            xml_param = xml_param.replace(
257                "{zt_kennung}",
258                self.zt_kennung
259                    .as_ref()
260                    .ok_or("zt_kennung not set in production mode.")?,
261            );
262            xml_param = xml_param.replace(
263                "{passwd_zt_kennung}",
264                self.passwd_zt_kennung
265                    .as_ref()
266                    .ok_or("passwd_zt_kennung not set in production mode.")?,
267            );
268        }
269        let url = {
270            if self.sandbox {
271                BASE_URL_SANDBOX.to_string() + &xml_param
272            } else {
273                BASE_URL_PRODUCTION.to_string() + &xml_param
274            }
275        };
276
277        let url = url::Url::parse(&url)?;
278        let auth_user = {
279            if self.sandbox {
280                self.entwickler_id
281                    .as_ref()
282                    .ok_or("entwickler_id not set in sandbox mode.")?
283            } else {
284                self.app_id
285                    .as_ref()
286                    .ok_or("app_id not set in production mode.")?
287            }
288        };
289        let auth_password = {
290            if self.sandbox {
291                self.passwd_entwicklerportal
292                    .as_ref()
293                    .ok_or("passwd_entwicklerportal not set sandbox mode.")?
294            } else {
295                self.app_token
296                    .as_ref()
297                    .ok_or("app_token not set in production mode.")?
298            }
299        };
300
301        let client = reqwest::blocking::Client::new();
302        let responce = client
303            .get(url)
304            .basic_auth(auth_user, Some(auth_password))
305            .body("")
306            .send()?;
307        Ok(responce.text()?)
308    }
309}