1use crate::{
4 models,
5 opts::{
6 ClearCacheOpts, ImageBuildOpts, ImageListOpts, ImagePruneOpts, ImagePushOpts,
7 ImageRemoveOpts, PullOpts, TagOpts,
8 },
9};
10
11use std::io::Read;
12
13use futures_util::{stream::Stream, TryFutureExt, TryStreamExt};
14
15use containers_api::{
16 conn::{Headers, Payload, AUTH_HEADER},
17 tarball,
18 url::{construct_ep, encoded_pair, encoded_pairs},
19};
20
21use crate::Result;
22
23impl_api_ty!(Image => name);
24
25impl Image {
26 impl_api_ep! {img: Image, resp
27 Inspect -> &format!("/images/{}/json", img.name), models::ImageInspect
28 }
29
30 api_doc! { Image => Delete
31 |
32 pub async fn remove(&self, opts: &ImageRemoveOpts) -> Result<Vec<models::ImageDeleteResponseItem>> {
36 let ep =
37 containers_api::url::construct_ep(format!("/images/{}", self.name), opts.serialize());
38 self.docker.delete_json(ep.as_ref()).await
39 }}
40
41 api_doc! { Image => Delete
42 |
43 pub async fn delete(&self) -> Result<Vec<models::ImageDeleteResponseItem>> {
47 self.docker
48 .delete_json(&format!("/images/{}", self.name))
49 .await
50 }}
51
52 api_doc! { Image => History
53 |
54 pub async fn history(&self) -> Result<models::ImageHistory200Response> {
56 self.docker
57 .get_json(&format!("/images/{}/history", self.name))
58 .await
59 }}
60
61 api_doc! { Image => Get
62 |
63 pub fn export(&self) -> impl Stream<Item = Result<Vec<u8>>> + Unpin + '_ {
65 Box::pin(
66 self.docker
67 .get_stream(format!("/images/{}/get", self.name))
68 .map_ok(|c| c.to_vec()),
69 )
70 }}
71
72 api_doc! { Image => Tag
73 |
74 pub async fn tag(&self, opts: &TagOpts) -> Result<()> {
76 let ep = construct_ep(format!("/images/{}/tag", self.name), opts.serialize());
77 self.docker
78 .post_string(&ep, Payload::empty(), Headers::none())
79 .await
80 .map(|_| ())
81 }}
82
83 api_doc! { Image => Push
84 |
85 pub async fn push(&self, opts: &ImagePushOpts) -> Result<()> {
87 let ep = construct_ep(format!("/images/{}/push", self.name), opts.serialize());
88
89 let headers = opts
90 .auth_header()
91 .map(|auth| Headers::single(AUTH_HEADER, auth))
92 .unwrap_or_else(Headers::default);
93
94 self.docker
95 .post_string(&ep, Payload::empty(), Some(headers))
96 .await
97 .map(|_| ())
98 }}
99
100 api_doc! { Distribution => Inspect
101 |
102 pub async fn distribution_inspect(&self) -> Result<models::DistributionInspect> {
104 self.docker
105 .post_json(
106 &format!("/distribution/{}/json", self.name),
107 Payload::empty(),
108 Headers::none(),
109 )
110 .await
111 }}
112}
113
114impl Images {
115 impl_api_ep! {img: Image, resp
116 List -> "/images/json", models::ImageSummary
117 Prune -> "/images/prune", models::ImagePrune200Response
118 }
119
120 api_doc! { Image => Build
121 |
122 pub fn build<'docker>(
126 &'docker self,
127 opts: &ImageBuildOpts,
128 ) -> impl Stream<Item = Result<models::ImageBuildChunk>> + Unpin + 'docker {
129 let ep = construct_ep("/build", opts.serialize());
130 let mut bytes = vec![];
131 let tar_result = tarball::dir(&mut bytes, &opts.path);
132
133 let docker = &self.docker;
134 Box::pin(
135 async move {
136 tar_result?;
137
138 let value_stream =
139 docker.post_into_stream(ep, Payload::Tar(bytes), Headers::none());
140
141 Ok(value_stream)
142 }
143 .try_flatten_stream(),
144 )
145 }}
146
147 api_doc! { Image => Build
148 |
149 #[cfg(feature = "par-compress")]
150 pub fn build_par<'docker>(
154 &'docker self,
155 opts: &ImageBuildOpts,
156 ) -> impl Stream<Item = Result<models::ImageBuildChunk>> + Unpin + 'docker {
157 let ep = construct_ep("/build", opts.serialize());
158
159 let tar_result = tarball::dir_par(&opts.path);
160
161 let docker = &self.docker;
162 Box::pin(
163 async move {
164 let bytes = tar_result?;
165
166 let value_stream =
167 docker.post_into_stream(ep, Payload::Tar(bytes), Headers::none());
168
169 Ok(value_stream)
170 }
171 .try_flatten_stream(),
172 )
173 }}
174
175 api_doc! { Image => Search
176 |
177 pub async fn search<T>(&self, term: T) -> Result<models::ImageSearch200Response>
179 where
180 T: AsRef<str>,
181 {
182 self.docker
183 .get_json(&construct_ep(
184 "/images/search",
185 Some(encoded_pair("term", term.as_ref())),
186 ))
187 .await
188 }}
189
190 api_doc! { Image => Pull
191 |
192 pub fn pull<'docker>(
194 &'docker self,
195 opts: &PullOpts,
196 ) -> impl Stream<Item = Result<models::ImageBuildChunk>> + Unpin + 'docker {
197 let headers = opts.auth_header().map(|a| Headers::single(AUTH_HEADER, a));
198
199 Box::pin(self.docker.post_into_stream(
200 construct_ep("/images/create", opts.serialize()),
201 Payload::empty(),
202 headers,
203 ))
204 }}
205
206 api_doc! { Image => GetAll
207 |
208 pub fn export<'docker>(
211 &'docker self,
212 names: Vec<&str>,
213 ) -> impl Stream<Item = Result<Vec<u8>>> + 'docker {
214 self.docker
215 .get_stream(format!(
216 "/images/get?{}",
217 encoded_pairs(names.iter().map(|n| ("names", *n)))
218 ))
219 .map_ok(|c| c.to_vec())
220 }}
221
222 api_doc! { Image => Load
223 |
224 pub fn import<'docker, R>(
227 &'docker self,
228 mut tarball: R,
229 ) -> impl Stream<Item = Result<models::ImageBuildChunk>> + Unpin + 'docker
230 where
231 R: Read + Send + 'docker,
232 {
233 Box::pin(
234 async move {
235 let mut bytes = Vec::default();
236
237 tarball.read_to_end(&mut bytes)?;
238
239 let value_stream = self.docker.post_into_stream(
240 "/images/load",
241 Payload::Tar(bytes),
242 Headers::none(),
243 );
244 Ok(value_stream)
245 }
246 .try_flatten_stream(),
247 )
248 }}
249
250 api_doc! { Image => Push
251 |
252 pub async fn push(&self, name: impl Into<crate::Id>, opts: &ImagePushOpts) -> Result<()> {
254 let image = Image::new(self.docker.clone(), name);
255 image.push(opts).await
256 }}
257
258 api_doc! { Build => Prune
259 |
260 pub async fn clear_cache(
262 &self,
263 opts: &ClearCacheOpts,
264 ) -> Result<models::BuildPrune200Response> {
265 self.docker
266 .post_json(
267 construct_ep("/build/prune", opts.serialize()),
268 Payload::empty(),
269 Headers::none(),
270 )
271 .await
272 }}
273}