docker_api/
builder.rs

1/// Necessary to work around https://github.com/rust-lang/rust/issues/52607.
2macro_rules! calculated_doc {
3    (
4        $(
5            #[doc = $doc:expr]
6            $thing:item
7        )*
8    ) => {
9        $(
10            #[doc = $doc]
11            $thing
12        )*
13    };
14}
15
16macro_rules! impl_api_ty {
17    ($(#[doc = $docs:expr])* $name:ident => $name_field:ident) => {
18        paste::item! {
19
20            calculated_doc!{
21            #[doc = concat!("Interface for accessing and manipulating Docker ", stringify!($name), ".\n", $($docs,)* "\n", api_url!($name))]
22            #[derive(Debug)]
23            pub struct [< $name >] {
24                docker: crate::Docker,
25                $name_field: crate::Id,
26            }
27            }
28            impl [< $name >] {
29                // TODO: this is possible on nightly, figure out what to do
30                calculated_doc!{
31                #[doc = concat!("Exports an interface exposing operations against a ", stringify!($name), " instance.")]
32                pub fn new(docker: crate::Docker, $name_field: impl Into<crate::Id>) -> Self
33                {
34                    [< $name >] {
35                        docker,
36                        $name_field: $name_field.into(),
37                    }
38                }
39                }
40
41                calculated_doc!{
42                #[doc = concat!("A getter for ", stringify!($name), " ", stringify!($name_field))]
43                pub fn $name_field(&self) -> &crate::Id {
44                    &self.$name_field
45                }
46                }
47
48
49            }
50
51
52            calculated_doc!{
53            #[doc = concat!("Interface for Docker ", stringify!($name), "s.", stringify!($name), ">")]
54            #[derive(Debug)]
55            pub struct [< $name s >] {
56                docker: crate::Docker,
57            }
58            }
59
60            impl [< $name s >] {
61                calculated_doc!{
62                #[doc = concat!("Exports an interface for interacting with Docker ", stringify!($name), "s.")]
63                pub fn new(docker: crate::Docker) -> Self {
64                    [< $name s >] { docker }
65                }
66                }
67
68                calculated_doc!{
69                #[doc = concat!("Returns a reference to a set of operations available to a specific ", stringify!($name), ".")]
70                pub fn get(&self, $name_field: impl Into<crate::Id>) -> [< $name >]
71                {
72                    [< $name >]::new(self.docker.clone(), $name_field)
73                }
74                }
75            }
76
77        }
78    }
79}
80
81macro_rules! api_url {
82    () => {
83        concat!("https://docs.docker.com/engine/api/", version!())
84    };
85    (operation $ep:expr) => {
86        concat!("\n[Api Reference](", api_url!(), "/#operation/", $ep, ")")
87    };
88    (tag $ep:expr) => {
89        concat!("\n[Api Reference](", api_url!(), "/#tag/", $ep, ")")
90    };
91    ($base:ident) => {
92        api_url!(tag stringify!($base))
93    };
94    ($base:ident => $op:ident) => {
95        api_url!(operation concat!(stringify!($base), stringify!($op)))
96    };
97}
98
99macro_rules! api_doc {
100    (
101        $base:ident => $op:ident
102        $(#[doc = $doc:expr])*
103        |
104        $it:item
105    ) => {
106        calculated_doc!{
107            #[doc = concat!(api_url!($base => $op))]
108            #[doc = "\n"]
109            $(
110                #[doc = $doc]
111            )*
112            $it
113        }
114    };
115    (
116        $base:ident
117        $(#[doc = $doc:expr])*
118        |
119        $it:item
120    ) => {
121        calculated_doc!{
122            #[doc = concat!(api_url!($base))]
123            #[doc = "\n"]
124            $(
125                #[doc = $doc]
126            )*
127            $it
128        }
129    };
130}
131
132macro_rules! impl_api_ep {
133    (
134        $it:ident: $base:ident, $resp:ident
135        $(
136            $op:ident -> $ep:expr, $ret:expr $(,$extra:expr)*
137        )*
138    ) => {
139        $(
140        impl_api_ep! {$op $it: $base -> $resp $ep, $ret $(,$extra)* }
141        )*
142    };
143    (
144        Inspect $it:ident: $base:ident -> $resp:ident $ep:expr, $ret:expr $(,$extra:expr)*
145    ) => {
146        paste::item! {
147        api_doc! { $base => Inspect
148        |
149        #[doc = concat!("Inspect this ", stringify!($base), ".")]
150        pub async fn inspect(&self) -> Result<$ret> {
151            let $it = self;
152            self.docker.get_json($ep).await
153        }}
154        }
155    };
156    (
157        ForceDelete $it:ident: $base:ident -> $resp:ident $ep:expr, $ret:expr $(,$extra:expr)*
158    ) => {
159
160        paste::item! {
161        async fn _delete(&self, force: bool) -> Result<$ret> {
162            let query = if force {
163                Some(containers_api::url::encoded_pair("force", force))
164            } else {
165                None
166            };
167
168            let $it = self;
169            let ep = containers_api::url::construct_ep($ep, query);
170
171            self.docker
172                .delete_json(ep.as_ref())
173                .await
174        }
175        }
176        paste::item! {
177        api_doc! { $base => Delete
178        |
179        #[doc = concat!("Delete this ", stringify!($base), ".")]
180        pub async fn force_delete(&self) -> Result<$ret> {
181            self._delete(true).await
182        }}
183        }
184        paste::item! {
185        api_doc! { $base => Delete
186        |
187        #[doc = concat!("Delete this ", stringify!($base), ".")]
188        pub async fn delete(&self) -> Result<$ret> {
189            self._delete(false).await
190        }}
191        }
192    };
193    (
194        Delete $it:ident: $base:ident -> $resp:ident $ep:expr, $ret:expr $(,$extra:expr)*
195    ) => {
196        paste::item! {
197        api_doc! { $base => Delete
198        |
199        #[doc = concat!("Delete this ", stringify!($base), ".")]
200        pub async fn delete(&self) -> Result<()> {
201            let $it = self;
202            self.docker.delete($ep).await.map(|_| ())
203        }}
204        }
205    };
206    (
207        DeleteWithOpts $it:ident: $base:ident -> $resp:ident $ep:expr, $ret:expr $(,$extra:expr)*
208    ) => {
209        impl_api_ep! { DeleteWithOpts $it: $base -> $resp $ep, $ret => $($extra)* }
210    };
211    (
212        DeleteWithOpts $it:ident: $base:ident -> $resp:ident $ep:expr, $ret:expr => $fn:expr
213    ) => {
214        paste::item! {
215        api_doc! { $base => Delete
216        |
217        #[doc = concat!("Delete this ", stringify!($base), ".")]
218        #[doc = concat!("Use [`delete`](", stringify!($base), "::delete) to delete without options.")]
219        pub async fn remove(&self, opts: &[< $base RemoveOpts >]) -> Result<$ret> {
220            let $it = self;
221            let ep = containers_api::url::construct_ep($ep, opts.serialize());
222            self.docker.$fn(ep.as_ref()).await
223        }}
224        }
225        paste::item! {
226        api_doc! { $base => Delete
227        |
228        #[doc = concat!("Delete this ", stringify!($base), ".")]
229        #[doc = concat!("Use [`remove`](", stringify!($base), "::remove) to customize options.")]
230        pub async fn delete(&self) -> Result<[< $ret >]> {
231            let $it = self;
232            self.docker.$fn($ep).await
233        }}
234        }
235    };
236    (
237        List $it:ident: $base:ident -> $resp:ident $ep:expr, $ret:expr $(, $extra:expr)*
238    ) => {
239        paste::item! {
240        api_doc! { $base => List
241        |
242        #[doc = concat!("List available ", stringify!($base), "s.")]
243        pub async fn list(&self, opts: &[< $base ListOpts >]) -> Result<Vec<$ret>> {
244            let ep = containers_api::url::construct_ep($ep, opts.serialize());
245            self.docker.get_json(&ep).await
246        }}
247        }
248    };
249    (
250        Create $it:ident: $base:ident -> $resp:ident $ep:expr, $ret:expr $(, $extra:expr)*
251    ) => {
252        paste::item! {
253        api_doc! { $base => Create
254        |
255        #[doc = concat!("Create a new ", stringify!($base), ".")]
256        pub async fn create(&self, opts: &[< $base CreateOpts >]) -> Result<[< $base >]> {
257            self.docker.post_json(&$ep, Payload::Json(opts.serialize_vec()?), Headers::none()).await
258            .map(|$resp: [< $ret >]| [< $base >]::new(self.docker.clone(), $($extra)*))
259        }}
260        }
261    };
262    (
263        Prune $it:ident: $base:ident -> $resp:ident $ep:expr, $ret:expr  $(, $extra:expr)*
264    ) => {
265        paste::item! {
266        api_doc! { $base => Prune
267        |
268        #[doc = concat!("Delete stopped/unused ", stringify!($base), "s.")]
269        pub async fn prune(&self, opts: &[< $base PruneOpts >]) -> Result<$ret> {
270            self.docker
271                .post_json(
272                    &containers_api::url::construct_ep($ep, opts.serialize()),
273                    crate::conn::Payload::empty(),
274                    crate::conn::Headers::none(),
275                ).await
276        }}
277        }
278    };
279    (
280        Logs $it:ident: $base:ident -> $resp:ident $ep:expr, $ret:expr $(, $extra:expr)*
281    ) => {
282        paste::item! {
283        api_doc! { $base => Logs
284        |
285        #[doc = concat!("Returns a stream of logs from a ", stringify!($base), ".")]
286        pub fn logs<'docker>(
287            &'docker self,
288            opts: &crate::opts::LogsOpts
289        ) -> impl futures_util::Stream<Item = crate::Result<containers_api::conn::TtyChunk>> + Unpin + 'docker {
290            use containers_api::conn::tty;
291            use futures_util::TryStreamExt;
292            let $it = self;
293            let ep = containers_api::url::construct_ep($ep, opts.serialize());
294
295            let stream = Box::pin(self.docker.get_stream(ep).map_err(|e| containers_api::conn::Error::Any(Box::new(e))));
296
297            Box::pin(tty::decode(stream).map_err(crate::Error::Error))
298        }
299        }}
300    };
301}