nextcloud_passwords_client/
utils.rs

1#[doc(hidden)]
2#[macro_export]
3macro_rules! create_details {
4    (
5        pub struct $struct:ident {
6        $(
7            pub $name:ident : bool
8        ),*
9        $(,)?
10    }) => {
11        /// Amount of optional details requested
12        #[derive(Default, Debug)]
13        pub struct $struct {
14            $(
15                pub $name: bool,
16            )*
17        }
18
19        impl $struct {
20            pub(crate) fn to_string(&self) -> String {
21                let mut s = "model".into();
22                $(
23                    if self.$name {
24                        s += concat!("+", stringify!($name));
25                    }
26                )*
27                s
28            }
29
30            pub fn new() -> Self {
31                Default::default()
32            }
33
34            $(
35                pub fn $name(self) -> Self {
36                    Self {
37                        $name: true,
38                        ..self
39                    }
40                }
41            )*
42
43        }
44    };
45}
46
47// Tags: versioned, create(optional | required), update(optional | required), search
48//
49
50#[derive(Debug)]
51pub struct SearchQuery<T: serde::Serialize> {
52    value: T,
53    query: QueryKind,
54}
55
56#[derive(Debug)]
57pub enum QueryKind {
58    Exact,
59    Equals,
60    NotEqual,
61    LessThan,
62    GreaterThan,
63    LessOrEqual,
64    GreaterOrEqual,
65}
66
67#[derive(serde::Serialize)]
68#[serde(untagged)]
69pub(crate) enum Criteria {
70    Value(serde_json::Value),
71    Search(&'static str, serde_json::Value),
72}
73
74impl<T: serde::Serialize> SearchQuery<T> {
75    pub(crate) fn to_criteria(&self) -> Result<Criteria, serde_json::Error> {
76        let value = serde_json::to_value(&self.value)?;
77        Ok(match self.query {
78            QueryKind::Exact => Criteria::Value(value),
79            QueryKind::Equals => Criteria::Search("eq", value),
80            QueryKind::NotEqual => Criteria::Search("ne", value),
81            QueryKind::LessThan => Criteria::Search("lt", value),
82            QueryKind::GreaterThan => Criteria::Search("gt", value),
83            QueryKind::LessOrEqual => Criteria::Search("le", value),
84            QueryKind::GreaterOrEqual => Criteria::Search("ge", value),
85        })
86    }
87}
88/*
89
90    TEMPLATE UTILISATION
91
92create_calls! {
93    PasswordApi where 
94        Endpoint = "1.0/password",
95        Details: Details,
96        Type: Password,
97        Create: CreatePassword,
98        Update: UpdatePassword,
99        Error: Error,
100        Identifier: PasswordIdentifier,
101        Trashed: TrashedIdentifier,
102        Criteria: PasswordSearch 
103    {
104        pub async fn list(&self, details: Option<Details>) -> Result<Vec<Type>, Error>;
105
106        pub async fn get(&self, details: Option<Details>, id: uuid::Uuid) -> Result<Type, Error>;
107
108        pub async fn find(&self, criteria: Criteria, details: Option<Details>) -> Result<Vec<Type>, Error>;
109
110        pub async fn create(&self, value: Create) -> Result<Identifier, Error>;
111
112        pub async fn update(&self, value: Update) -> Result<Identifier, Error>;
113
114        pub async fn delete(&self, id: uuid::Uuid, revision: Option<uuid::Uuid>) -> Result<Trashed, Error>;
115
116        pub async fn restore(&self, id: uuid::Uuid, revision: Option<uuid::Uuid>) -> Result<Identifier, Error>;
117    }
118}
119
120 */
121
122#[doc(hidden)]
123#[macro_export]
124macro_rules! create_calls {
125    (
126        $base:ident where
127            Endpoint = $endpoint:expr,
128            Details: $details:ty,
129            Type: $ty:ty,
130            Create: $create:ty,
131            Update: $update:ty,
132            Error: $err:ty,
133            Identifier: $ident:ty,
134            Trashed: $trashed:ty,
135            Criteria: $criteria:ty $(,)?
136        {
137            $(
138            List;
139            $(#[$meta_list:meta])*
140            pub async fn list(&self, details: Option<Details>) -> Result<Vec<Type>, Error>;
141            )?
142
143            $(
144            Get;
145            $(#[$meta_get:meta])*
146            pub async fn get(&self, details: Option<Details>, id: uuid::Uuid) -> Result<Type, Error>;
147            )?
148
149            $(
150            Find;
151            $(#[$meta_find:meta])*
152            pub async fn find(&self, criteria: Criteria, details: Option<Details>) -> Result<Vec<Type>, Error>;
153            )?
154
155            $(
156            Create;
157            $(#[$meta_create:meta])*
158            pub async fn create(&self, value: Create) -> Result<Identifier, Error>;
159            )?
160
161            $(
162            Update;
163            $(#[$meta_update:meta])*
164            pub async fn update(&self, value: Update) -> Result<Identifier, Error>;
165            )?
166
167            $(
168            Delete;
169            $(#[$meta_delete:meta])*
170            pub async fn delete(&self, id: uuid::Uuid, revision: Option<uuid::Uuid>) -> Result<Trashed, Error>;
171            )?
172
173            $(
174            Restore;
175            $(#[$meta_restore:meta])*
176            pub async fn restore(&self, id: uuid::Uuid, revision: Option<uuid::Uuid>) -> Result<Identifier, Error>;
177            )?
178        }
179    ) => {
180        ::doc_comment::doc_comment! { concat!("Actions on the ", stringify!($base), " API"),
181        pub struct $base<'a> {
182            pub(crate) api: &'a crate::AuthenticatedApi,
183        }}
184
185        impl<'a> $base<'a> {
186            $(
187            $(#[$meta_list])*
188            pub async fn list(&self, details: Option<$details>) -> Result<Vec<$ty>, $err> {
189                #[derive(serde::Serialize, serde::Deserialize)]
190                struct DetailsStr {
191                    #[serde(skip_serializing_if = "Option::is_none")]
192                    details: Option<String>,
193                }
194                self.api
195                    .passwords_post(
196                        concat!($endpoint, "/list"),
197                        DetailsStr {
198                            details: details.map(|d| d.to_string()),
199                        },
200                    )
201                    .await
202            }
203            )?
204
205            $(
206            $(#[$meta_get])*
207            pub async fn get(&self, details: Option<$details>, id: uuid::Uuid) -> Result<$ty, $err> {
208                #[derive(Serialize, Deserialize)]
209                struct Show {
210                    id: uuid::Uuid,
211                    #[serde(skip_serializing_if = "Option::is_none")]
212                    details: Option<String>,
213                }
214                let request = Show {
215                    id,
216                    details: details.map(|d| d.to_string()),
217                };
218                self.api
219                    .passwords_post(concat!($endpoint, "/show"), request)
220                    .await
221            }
222            )?
223
224            $(
225            $(#[$meta_find])*
226            pub async fn find(
227                &self,
228                criteria: $criteria,
229                details: Option<$details>,
230            ) -> Result<Vec<$ty>, $err> {
231                #[derive(Serialize)]
232                struct Request {
233                    criteria: $criteria,
234                    #[serde(skip_serializing_if = "Option::is_none")]
235                    details: Option<String>,
236                }
237                let request = Request {
238                    criteria,
239                    details: details.map(|d| d.to_string()),
240                };
241                self.api
242                    .passwords_post(concat!($endpoint, "/find"), request)
243                    .await
244            }
245            )?
246
247            $(
248            $(#[$meta_create])*
249            pub async fn create(&self, value: $create) -> Result<$ident, $err> {
250                self.api
251                    .passwords_post(concat!($endpoint, "/create"), value)
252                    .await
253            }
254            )?
255
256            $(
257            $(#[$meta_update])*
258            pub async fn update(&self, folder: $update) -> Result<$ident, $err> {
259                self.api
260                    .passwords_patch(concat!($endpoint, "/update"), folder)
261                    .await
262            }
263            )?
264
265            $(
266            $(#[$meta_delete])*
267            pub async fn delete(&self, id: uuid::Uuid, revision: Option<uuid::Uuid>) -> Result<$trashed, $err> {
268                #[derive(Serialize)]
269                struct Request {
270                    id: uuid::Uuid,
271                    #[serde(skip_serializing_if = "Option::is_none")]
272                    revision: Option<uuid::Uuid>,
273                }
274                self.api
275                    .passwords_delete(concat!($endpoint, "/delete"), Request { id, revision })
276                    .await
277            }
278            )?
279
280            $(
281            $(#[$meta_restore])*
282            pub async fn restore(
283                &self,
284                id: uuid::Uuid,
285                revision: Option<uuid::Uuid>,
286            ) -> Result<$ident, $err> {
287                #[derive(Serialize)]
288                struct Request {
289                    id: uuid::Uuid,
290                    #[serde(skip_serializing_if = "Option::is_none")]
291                    revision: Option<uuid::Uuid>,
292                }
293                self.api
294                    .passwords_patch(concat!($endpoint, "/restore"), Request { id, revision })
295                    .await
296            }
297            )?
298        }
299    };
300}
301
302#[doc(hidden)]
303#[macro_export]
304macro_rules! create_binding {
305    (
306        $(#[$s_attr:meta])*
307        pub
308        struct $name:ident {
309            $(
310                $(#[$f_attr:meta])*
311                pub
312                $field:ident : $type:ty [$($tags:tt)*]
313            ),* $(,)?
314        }
315    ) => (
316        create_binding! {
317            @name $name
318            @meta ($($s_attr)*)
319            @create_new ()
320            @create ()
321            @update_new ()
322            @update ()
323            @search ()
324            @versioned ()
325            @not_versioned ()
326            $(
327                (
328                    $(#[$f_attr])*
329                    $field : $type
330                ) [$($tags)*]
331            )*
332        }
333    );
334
335    (
336        @name $name:ident
337        @meta (
338            $($s_attr:tt)*
339        )
340        @create_new (
341            $(
342            $(
343                (
344                    $(#[$cn_attr:tt])*
345                    $cn_field:ident : $cn_type:ty
346                )
347            )+
348            )?
349        )
350        @create (
351            $(
352            $(
353                (
354                    $(#[$c_attr:tt])*
355                    $c_field:ident : $c_type:ty
356                )
357            )+
358            )?
359        )
360        @update_new (
361            $(
362                (
363                    $(#[$un_attr:tt])*
364                    $un_field:ident : $un_type:ty
365                )
366            )*
367        )
368        @update (
369            $(
370                (
371                    $(#[$u_attr:tt])*
372                    $u_field:ident : $u_type:ty
373                )
374            )*
375        )
376        @search (
377            $(
378            $(
379                (
380                    $(#[$se_attr:tt])*
381                    $se_field:ident : $se_type:ty
382                )
383            )+)?
384        )
385        @versioned (
386            $(
387                (
388                    $(#[$v_attr:tt])*
389                    $v_field:ident : $v_type:ty
390                )
391            )*
392        )
393        @not_versioned (
394            $(
395                (
396                    $(#[$n_attr:tt])*
397                    $n_field:ident : $n_type:ty
398                )
399            )*
400        )
401        // nothing left to parse
402    ) => (
403        ::paste::item! {
404            $(#[$s_attr])*
405            pub
406            struct $name {
407                $(
408                    $(#[$n_attr])*
409                    pub
410                    $n_field : $n_type,
411                )*
412                #[serde(flatten)]
413                pub
414                versioned : [<Versioned $name>],
415            }
416            ::doc_comment::doc_comment! { concat!("versioned properties of [", stringify!($name), "]"),
417            $(#[$s_attr])*
418            pub
419            struct [<Versioned $name>] {
420                $(
421                    $(#[$v_attr])*
422                    pub
423                    $v_field : $v_type,
424                )*
425            }
426            }
427
428            $(
429            ::doc_comment::doc_comment!{ concat!("Builder to create [", stringify!($name) , "], the values in the builder are optional values"),
430            #[derive(serde::Serialize, serde::Deserialize, Debug)]
431            pub
432            struct [<Create $name>] {
433                $(
434                    $(#[$cn_attr])*
435                    $cn_field: $cn_type,
436                )+
437                $(
438                    $(#[$c_attr])*
439                    pub
440                    $c_field : Option<$c_type>,
441                )+
442            }
443            }
444
445            impl [<Create $name>] {
446                pub
447                fn new ($($cn_field: $cn_type,)*)
448                  -> Self
449                {
450                    Self {
451                        $(
452                            $cn_field,
453                        )*
454                        $(
455                            $c_field: None,
456                        )*
457                    }
458                }
459
460                $(
461                    pub
462                    fn $c_field (self: Self, $c_field: $c_type)
463                      -> Self
464                    {
465                        Self { $c_field: Some($c_field), ..self }
466                    }
467                )*
468            }
469            )?
470
471            $(
472            ::doc_comment::doc_comment! {
473                "Builder to add search criterias (see the `find` method)",
474            #[derive(serde::Serialize, Default)]
475            pub struct [<$name Search>] {
476                $(
477                    #[serde(skip_serializing_if = "Option::is_none")]
478                    $(#[$se_attr])?
479                    $se_field: Option<crate::utils::Criteria>,
480                )+
481            }
482            }
483            impl [<$name Search>] {
484                pub fn new() -> Self {
485                    Default::default()
486                }
487                $(
488                    pub fn [<and_ $se_field>](self, query: crate::utils::SearchQuery<$se_type>) -> Result<Self, crate::Error> {
489                        Ok(Self {
490                            $se_field: Some(query.to_criteria()?),
491                            ..self
492                        })
493                    }
494                )+
495            }
496            )?
497
498            ::doc_comment::doc_comment! {
499                concat!("Builder to update [", stringify!($name), "], the values in the builder are optional values"),
500            $(#[$s_attr])*
501            pub
502            struct [<Update $name>] {
503                $(
504                    $(#[$un_attr])*
505                    $un_field: $un_type,
506                )*
507                $(
508                    #[serde(skip_serializing_if = "Option::is_none")]
509                    $(#[$u_attr])*
510                    pub
511                    $u_field : Option<$u_type>,
512                )*
513            }
514            }
515
516            impl [<Update $name>] {
517                pub
518                fn new ($($un_field: $un_type,)*)
519                  -> Self
520                {
521                    Self {
522                        $(
523                            $un_field,
524                        )*
525                        $(
526                            $u_field: None,
527                        )*
528                    }
529                }
530
531                $(
532                    pub
533                    fn $u_field (self: Self, $u_field: $u_type)
534                      -> Self
535                    {
536                        Self { $u_field: Some($u_field), ..self }
537                    }
538                )*
539            }
540        }
541    );
542
543    // Create new
544    (
545        @name $name:ident
546        @meta $meta:tt
547        @create_new ($($create_new:tt)*)
548        @create $create:tt
549        @update_new $update_new:tt
550        @update $update:tt
551        @search $search:tt
552        @versioned $versioned:tt
553        @not_versioned $not_versioned:tt
554            $current:tt [create(required) $($($tags:tt)+)?]
555            $($rest:tt)*
556    ) => (
557        create_binding! {
558            @name $name
559            @meta $meta
560            @create_new ( $($create_new)* $current )
561            @create $create
562            @update_new $update_new
563            @update $update
564            @search $search
565            @versioned $versioned
566            @not_versioned $not_versioned
567                $($current [$($tags)+])?
568                $($rest)*
569        }
570    );
571
572    // Create
573    (
574        @name $name:ident
575        @meta $meta:tt
576        @create_new $create_new:tt
577        @create ($($create:tt)*)
578        @update_new $update_new:tt
579        @update $update:tt
580        @search $search:tt
581        @versioned $versioned:tt
582        @not_versioned $not_versioned:tt
583            $current:tt [create(optional) $($($tags:tt)+)?]
584            $($rest:tt)*
585    ) => (
586        create_binding! {
587            @name $name
588            @meta $meta
589            @create_new $create_new
590            @create ($($create)* $current)
591            @update_new $update_new
592            @update $update
593            @search $search
594            @versioned $versioned
595            @not_versioned $not_versioned
596                $($current [$($tags)+])?
597                $($rest)*
598        }
599    );
600
601    // Update new
602    (
603        @name $name:ident
604        @meta $meta:tt
605        @create_new $create_new:tt
606        @create $create:tt
607        @update_new ($($update_new:tt)*)
608        @update $update:tt
609        @search $search:tt
610        @versioned $versioned:tt
611        @not_versioned $not_versioned:tt
612            $current:tt [update(required) $($($tags:tt)+)?]
613            $($rest:tt)*
614    ) => (
615        create_binding! {
616            @name $name
617            @meta $meta
618            @create_new $create_new
619            @create $create
620            @update_new ($($update_new)* $current)
621            @update $update
622            @search $search
623            @versioned $versioned
624            @not_versioned $not_versioned
625                $($current [$($tags)+])?
626                $($rest)*
627        }
628    );
629
630    // Update
631    (
632        @name $name:ident
633        @meta $meta:tt
634        @create_new $create_new:tt
635        @create $create:tt
636        @update_new $update_new:tt
637        @update ($($update:tt)*)
638        @search $search:tt
639        @versioned $versioned:tt
640        @not_versioned $not_versioned:tt
641            $current:tt [update(optional) $($($tags:tt)+)?]
642            $($rest:tt)*
643    ) => (
644        create_binding! {
645            @name $name
646            @meta $meta
647            @create_new $create_new
648            @create $create
649            @update_new $update_new
650            @update ($($update)* $current)
651            @search $search
652            @versioned $versioned
653            @not_versioned $not_versioned
654                $($current [$($tags)+])?
655                $($rest)*
656        }
657    );
658
659    // Search
660    (
661        @name $name:ident
662        @meta $meta:tt
663        @create_new $create_new:tt
664        @create $create:tt
665        @update_new $update_new:tt
666        @update $update:tt
667        @search ($($search:tt)*)
668        @versioned $versioned:tt
669        @not_versioned $not_versioned:tt
670            $current:tt [search $($($tags:tt)+)?]
671            $($rest:tt)*
672    ) => (
673        create_binding! {
674            @name $name
675            @meta $meta
676            @create_new $create_new
677            @create $create
678            @update_new $update_new
679            @update $update
680            @search ($($search)* $current)
681            @versioned $versioned
682            @not_versioned $not_versioned
683                $($current [$($tags)+])?
684                $($rest)*
685        }
686    );
687
688    // Versioned
689    (
690        @name $name:ident
691        @meta $meta:tt
692        @create_new $create_new:tt
693        @create $create:tt
694        @update_new $update_new:tt
695        @update $update:tt
696        @search $search:tt
697        @versioned ( $($versioned:tt)* )
698        @not_versioned $not_versioned:tt
699            $current:tt [versioned(true) $($($tags:tt)+)?]
700            $($rest:tt)*
701    ) => (
702        create_binding! {
703            @name $name
704            @meta $meta
705            @create_new $create_new
706            @create $create
707            @update_new $update_new
708            @update $update
709            @search $search
710            @versioned ( $($versioned)* $current )
711            @not_versioned $not_versioned
712                $($current [$($tags)+])?
713                $($rest)*
714        }
715    );
716    // Not Versioned
717    (
718        @name $name:ident
719        @meta $meta:tt
720        @create_new $create_new:tt
721        @create $create:tt
722        @update_new $update_new:tt
723        @update $update:tt
724        @search $search:tt
725        @versioned $versioned:tt
726        @not_versioned ( $($not_versioned:tt)* )
727            $current:tt [versioned(false) $($($tags:tt)+)?]
728            $($rest:tt)*
729    ) => (
730        create_binding! {
731            @name $name
732            @meta $meta
733            @create_new $create_new
734            @create $create
735            @update_new $update_new
736            @update $update
737            @search $search
738            @versioned $versioned
739            @not_versioned ( $($not_versioned)* $current)
740                $($current [$($tags)+])?
741                $($rest)*
742        }
743    );
744
745
746    // Nothing
747    (
748        @name $name:ident
749        @meta $meta:tt
750        @create_new $create_new:tt
751        @create $create:tt
752        @update_new $update_new:tt
753        @update $update:tt
754        @search $search:tt
755        @versioned $versioned:tt
756        @not_versioned $not_versioned:tt
757            $current:tt []
758            $($rest:tt)*
759    ) => (
760        create_binding! {
761            @name $name
762            @meta $meta
763            @create_new $create_new
764            @create $create
765            @update_new $update_new
766            @update $update
767            @search $search
768            @versioned $versioned
769            @not_versioned $not_versioned
770                $($rest)*
771        }
772    );
773}