tmf_client/lib.rs
1//! # TMF Client Library
2//!
3//! ## Description
4//! Interact with TMF compliant APIs using an SDK
5//!
6//! ## Supported TMF APIs
7//!
8//! Currently supports:
9//!
10//! - TMF620 Product Catalog Management
11//! - TMF622 Product Order Management
12//! - TMF629 Customer Management
13//! - TMF632 Party Management
14//! - TMF633 Service Catalog Management
15//! - TMF637 Product Inventory Management
16//! - TMF638 Service Inventory Management
17//! - TMF639 Resource Inventory Management
18//! - TMF645 Service Qualification Management
19//! - TMF648 Quote Management
20//! - TMF663 Shopping Cart Management
21//! - TMF674 Geographic Site Management
22//!
23//! ## Features
24//! All TMF APIs can be conditionally compiled. Deafult includes all APIs using V4 specifications.
25
26#![warn(missing_docs)]
27
28pub mod common;
29pub mod tmf;
30
31use common::tmf_error::TMFError;
32#[cfg(feature = "tmf620")]
33use tmf::tmf620::TMF620;
34#[cfg(feature = "tmf622")]
35use tmf::tmf622::TMF622;
36#[cfg(feature = "tmf629")]
37use tmf::tmf629::TMF629;
38#[cfg(feature = "tmf632")]
39use tmf::tmf632::TMF632;
40#[cfg(feature = "tmf633")]
41use tmf::tmf633::TMF633;
42#[cfg(feature = "tmf637")]
43use tmf::tmf637::TMF637;
44#[cfg(feature = "tmf638")]
45use tmf::tmf638::TMF638;
46#[cfg(feature = "tmf639")]
47use tmf::tmf639::TMF639;
48#[cfg(feature = "tmf645")]
49use tmf::tmf645::TMF645;
50#[cfg(feature = "tmf648")]
51use tmf::tmf648::TMF648;
52#[cfg(feature = "tmf663")]
53use tmf::tmf663::TMF663;
54#[cfg(feature = "tmf674")]
55use tmf::tmf674::TMF674;
56
57use tmflib::{HasId, Uri};
58
59#[cfg(feature = "insecure")]
60const INSECURE: bool = true;
61#[cfg(not(feature = "insecure"))]
62const INSECURE: bool = false;
63
64/// Default port for the TMF API
65pub const DEFAULT_PORT: u16 = 8001;
66
67/// Configuration for the TMF Client
68#[derive(Clone, Debug, Default)]
69pub struct Config {
70 /// The host URI for the TMF API
71 pub host: Uri,
72 /// The port to use for the TMF API
73 ///
74 pub port: u16,
75 /// The base path for the TMF API
76 pub insecure: bool,
77}
78
79impl Config {
80 /// Create a new configuration for the TMF Client
81 /// ```
82 /// # use tmf_client::Config;
83 /// let config = Config::new("http://localhost", 8000);
84 /// ```
85 pub fn new(host: impl Into<String>, port: u16) -> Config {
86 Config {
87 host: Uri::from(host.into()),
88 port,
89 insecure: INSECURE,
90 }
91 }
92}
93
94/// Fields for filtering output
95#[derive(Clone, Default, Debug)]
96pub struct QueryOptions {
97 /// Specific set of fields delimited by comma
98 pub fields: Option<String>,
99 /// Limit the number of results returned
100 pub limit: Option<u16>,
101 /// Offset the results returned
102 pub offset: Option<u16>,
103 /// Filter on name
104 pub name: Option<String>,
105}
106
107impl QueryOptions {
108 /// Setting the field on an existing QueryOptions
109 /// ```
110 /// # use tmf_client::QueryOptions;
111 /// let opt = QueryOptions::default()
112 /// .fields("id,name,description".to_string());
113 /// ```
114 pub fn fields(mut self, fields: String) -> QueryOptions {
115 self.fields = Some(fields);
116 self
117 }
118 /// Set the limit on the number of results returned
119 /// ```
120 /// # use tmf_client::QueryOptions;
121 /// let opt = QueryOptions::default()
122 /// .limit(10);
123 /// ```
124 pub fn limit(mut self, limit: u16) -> QueryOptions {
125 self.limit = Some(limit);
126 self
127 }
128
129 /// Set the offset on the number of results returned
130 /// ```
131 /// # use tmf_client::QueryOptions;
132 /// let opt = QueryOptions::default()
133 /// .offset(5);
134 /// ```
135 pub fn offset(mut self, offset: u16) -> QueryOptions {
136 self.offset = Some(offset);
137 self
138 }
139
140 /// Set the name to filter on
141 /// ```
142 /// # use tmf_client::QueryOptions;
143 /// let opt = QueryOptions::default()
144 /// .name("MyService".to_string());
145 /// ```
146 /// This will filter the results to only include those with the specified name.
147 /// If the name is not set, it will not filter on name.
148 ///
149 pub fn name(mut self, name: impl Into<String>) -> QueryOptions {
150 self.name = Some(name.into());
151 self
152 }
153}
154
155impl From<QueryOptions> for String {
156 fn from(val: QueryOptions) -> Self {
157 let limit = match val.limit {
158 Some(l) => format!("limit={l}"),
159 None => String::default(),
160 };
161 let offset = match val.offset {
162 Some(o) => format!("offset={o}"),
163 None => String::default(),
164 };
165 let name = match val.name {
166 Some(n) => format!("name={n}"),
167 None => String::default(),
168 };
169 format!("{limit}&{offset}&{name}")
170 }
171}
172
173/// Standard set of operations for all TMF objects
174#[cfg(feature = "blocking")]
175pub trait BlockingOperations {
176 /// The TMF object type that this trait operates on
177 type TMF: HasId;
178
179 /// Get a specific TMF object by Id
180 /// ```
181 /// # use tmf_client::{TMFClient,BlockingOperations};
182 /// let categories = TMFClient::new("http://localhost",Some(8000))
183 /// .tmf620()
184 /// .category()
185 /// .get("ID123");
186 /// ```
187 fn get(&self, id: impl Into<String>) -> Result<Vec<Self::TMF>, TMFError>;
188 /// Get a list of tmf objects applying optional filter
189 /// ```
190 /// # use tmf_client::{TMFClient,QueryOptions,BlockingOperations};
191 /// let filter = QueryOptions::default()
192 /// .limit(15)
193 /// .offset(10);
194 /// let categories = TMFClient::new("http://localhost",Some(8000))
195 /// .tmf620()
196 /// .category()
197 /// .list(Some(filter));
198 /// ```
199 fn list(&self, filter: Option<QueryOptions>) -> Result<Vec<Self::TMF>, TMFError>;
200 /// Create a new instance of a TMF object
201 fn create(&self, item: Self::TMF) -> Result<Self::TMF, TMFError>;
202 /// Update an existing TMF Object using the provided patch object
203 fn update(&self, id: impl Into<String>, patch: Self::TMF) -> Result<Self::TMF, TMFError>;
204 /// Delete a specific tmf object by Id
205 /// ```
206 /// # use tmf_client::{TMFClient,BlockingOperations};
207 /// let categories = TMFClient::new("http://localhost",None)
208 /// .tmf620()
209 /// .category()
210 /// .delete("ID123");
211 /// ```
212 fn delete(&self, id: impl Into<String>) -> Result<Self::TMF, TMFError>;
213}
214
215/// Standard set of operations for all TMF objects
216#[cfg(not(feature = "blocking"))]
217pub trait AsyncOperations {
218 /// The TMF object type that this trait operates on
219 type TMF: HasId;
220
221 /// Get a specific TMF object by Id
222 fn get(
223 &self,
224 id: impl Into<String>,
225 ) -> impl std::future::Future<Output = Result<Vec<Self::TMF>, TMFError>>;
226 /// Get a list of tmf objects applying optional filter
227 fn list(
228 &self,
229 filter: Option<QueryOptions>,
230 ) -> impl std::future::Future<Output = Result<Vec<Self::TMF>, TMFError>>;
231 /// Create a new instance of a TMF object
232 fn create(
233 &self,
234 item: Self::TMF,
235 ) -> impl std::future::Future<Output = Result<Self::TMF, TMFError>>;
236 /// Update an existing TMF Object using the provided patch object
237 fn update(
238 &self,
239 id: impl Into<String>,
240 patch: Self::TMF,
241 ) -> impl std::future::Future<Output = Result<Self::TMF, TMFError>>;
242 /// Delete a specific tmf object by Id
243 fn delete(
244 &self,
245 id: impl Into<String>,
246 ) -> impl std::future::Future<Output = Result<Self::TMF, TMFError>>;
247}
248
249/// Trait to create a new instance of a TMF object
250#[allow(clippy::new_ret_no_self)]
251pub trait HasNew<T: Clone> {
252 /// Create a new instance of the TMF object passin in the destination host Uri
253 fn new(config: Config) -> T;
254}
255
256/// TMF Client
257pub struct TMFClient {
258 config: Config,
259 #[cfg(feature = "tmf620")]
260 tmf620: Option<TMF620>,
261 #[cfg(feature = "tmf622")]
262 tmf622: Option<TMF622>,
263 #[cfg(feature = "tmf629")]
264 tmf629: Option<TMF629>,
265 #[cfg(feature = "tmf632")]
266 tmf632: Option<TMF632>,
267 #[cfg(feature = "tmf633")]
268 tmf633: Option<TMF633>,
269 #[cfg(feature = "tmf637")]
270 tmf637: Option<TMF637>,
271 #[cfg(feature = "tmf638")]
272 tmf638: Option<TMF638>,
273 #[cfg(feature = "tmf639")]
274 tmf639: Option<TMF639>,
275 #[cfg(feature = "tmf645")]
276 tmf645: Option<TMF645>,
277 #[cfg(feature = "tmf648")]
278 tmf648: Option<TMF648>,
279 #[cfg(feature = "tmf663")]
280 tmf663: Option<TMF663>,
281 #[cfg(feature = "tmf674")]
282 tmf674: Option<TMF674>,
283}
284
285// Create a new instance
286fn instantiate<T: Clone + HasNew<T>>(api: &mut Option<T>, config: Config) -> T {
287 match api {
288 Some(instance) => instance.clone(),
289 None => {
290 let new_api = T::new(config);
291 api.replace(new_api.clone());
292 new_api
293 }
294 }
295}
296
297impl TMFClient {
298 /// Create a new TMFClient instance
299 /// ```
300 /// # use tmf_client::TMFClient;
301 /// let client = TMFClient::new("http://localhost",Some(8000));
302 /// ```
303 pub fn new(host: impl Into<String>, port: Option<u16>) -> TMFClient {
304 TMFClient {
305 config: Config::new(host, port.unwrap_or(DEFAULT_PORT)),
306 #[cfg(feature = "tmf620")]
307 tmf620: None,
308 #[cfg(feature = "tmf622")]
309 tmf622: None,
310 #[cfg(feature = "tmf629")]
311 tmf629: None,
312 #[cfg(feature = "tmf632")]
313 tmf632: None,
314 #[cfg(feature = "tmf633")]
315 tmf633: None,
316 #[cfg(feature = "tmf637")]
317 tmf637: None,
318 #[cfg(feature = "tmf638")]
319 tmf638: None,
320 #[cfg(feature = "tmf639")]
321 tmf639: None,
322 #[cfg(feature = "tmf645")]
323 tmf645: None,
324 #[cfg(feature = "tmf648")]
325 tmf648: None,
326 #[cfg(feature = "tmf663")]
327 tmf663: None,
328 #[cfg(feature = "tmf674")]
329 tmf674: None,
330 }
331 }
332
333 /// Create access to TMF620 API
334 /// ```
335 /// # use tmf_client::TMFClient;
336 /// let tmf620 = TMFClient::new("http://localhost",None)
337 /// .tmf620();
338 /// ```
339 #[cfg(feature = "tmf620")]
340 pub fn tmf620(&mut self) -> TMF620 {
341 // // let instance =
342 let config = self.config.clone();
343 let instance: TMF620 = instantiate(&mut self.tmf620, config);
344 self.tmf620.replace(instance.clone());
345 instance
346 }
347
348 /// Create access to TMF622 API
349 /// ```
350 /// # use tmf_client::TMFClient;
351 /// let tmf620 = TMFClient::new("http://localhost",None)
352 /// .tmf622();
353 /// ```
354 #[cfg(feature = "tmf622")]
355 pub fn tmf622(&mut self) -> TMF622 {
356 instantiate(&mut self.tmf622, self.config.clone())
357 }
358
359 /// Create access to TMF632 API
360 /// ```
361 /// # use tmf_client::TMFClient;
362 /// let tmf632 = TMFClient::new("http://localhost",None)
363 /// .tmf629();
364 /// ```
365 #[cfg(feature = "tmf629")]
366 pub fn tmf629(&mut self) -> TMF629 {
367 instantiate(&mut self.tmf629, self.config.clone())
368 }
369
370 /// Create access to TMF632 API
371 /// ```
372 /// # use tmf_client::TMFClient;
373 /// let tmf632 = TMFClient::new("http://localhost",None)
374 /// .tmf632();
375 /// ```
376 #[cfg(feature = "tmf632")]
377 pub fn tmf632(&mut self) -> TMF632 {
378 instantiate(&mut self.tmf632, self.config.clone())
379 }
380
381 /// Create access to TMF633 API
382 /// ```
383 /// # use tmf_client::TMFClient;
384 /// let tmf633 = TMFClient::new("http://localhost",None)
385 /// .tmf633();
386 /// ```
387 #[cfg(feature = "tmf633")]
388 pub fn tmf633(&mut self) -> TMF633 {
389 instantiate(&mut self.tmf633, self.config.clone())
390 }
391
392 /// Create access to TMF637 API
393 /// ```
394 /// # use tmf_client::TMFClient;
395 /// let tmf637 = TMFClient::new("http://localhost",None)
396 /// .tmf637();
397 /// ```
398 #[cfg(feature = "tmf637")]
399 pub fn tmf637(&mut self) -> TMF637 {
400 instantiate(&mut self.tmf637, self.config.clone())
401 }
402
403 /// Create access to TMF638 API
404 /// ```
405 /// # use tmf_client::TMFClient;
406 /// let tmf638 = TMFClient::new("http://localhost",None)
407 /// .tmf638();
408 /// ```
409 #[cfg(feature = "tmf638")]
410 pub fn tmf638(&mut self) -> TMF638 {
411 instantiate(&mut self.tmf638, self.config.clone())
412 }
413
414 /// Create access to TMF639 API
415 /// ```
416 /// # use tmf_client::TMFClient;
417 /// let tmf639 = TMFClient::new("http://localhost",None)
418 /// .tmf639();
419 /// ```
420 #[cfg(feature = "tmf639")]
421 pub fn tmf639(&mut self) -> TMF639 {
422 instantiate(&mut self.tmf639, self.config.clone())
423 }
424
425 /// Create access to TMF645 API
426 /// ```
427 /// # use tmf_client::TMFClient;
428 /// let tmf645 = TMFClient::new("http://localhost",None)
429 /// .tmf645();
430 /// ```
431 #[cfg(feature = "tmf645")]
432 pub fn tmf645(&mut self) -> TMF645 {
433 instantiate(&mut self.tmf645, self.config.clone())
434 }
435
436 /// Create access to TMF648 API
437 /// ```
438 /// # use tmf_client::TMFClient;
439 /// let tmf648 = TMFClient::new("http://localhost",None)
440 /// .tmf648();
441 /// ```
442 #[cfg(feature = "tmf648")]
443 pub fn tmf648(&mut self) -> TMF648 {
444 instantiate(&mut self.tmf648, self.config.clone())
445 }
446
447 /// Create access to TMF663 API
448 /// ```
449 /// # use tmf_client::TMFClient;
450 /// let tmf663 = TMFClient::new("http://localhost",None)
451 /// .tmf663();
452 /// ```
453 #[cfg(feature = "tmf663")]
454 pub fn tmf663(&mut self) -> TMF663 {
455 instantiate(&mut self.tmf663, self.config.clone())
456 }
457
458 /// Create access to TMF674 API
459 /// ```
460 /// # use tmf_client::TMFClient;
461 /// let tmf674 = TMFClient::new("http://localhost",None)
462 /// .tmf674();
463 /// ```
464 #[cfg(feature = "tmf674")]
465 pub fn tmf674(&mut self) -> TMF674 {
466 instantiate(&mut self.tmf674, self.config.clone())
467 }
468}
469
470#[cfg(test)]
471mod tests {
472 use super::*;
473 #[test]
474 fn test_filter_limit() {
475 let filter = QueryOptions::default().limit(111);
476
477 assert_eq!(filter.limit, Some(111));
478 }
479
480 #[test]
481 fn test_filter_offset() {
482 let filter = QueryOptions::default().offset(222);
483
484 assert_eq!(filter.offset, Some(222));
485 }
486}