1use containers_api::opts::{Filter, FilterItem};
2use containers_api::{
3 impl_filter_func, impl_map_field, impl_opts_builder, impl_opts_required_builder,
4 impl_url_bool_field, impl_url_enum_field, impl_url_field, impl_url_str_field,
5 impl_url_vec_field,
6};
7use serde::Serialize;
8use base64::engine::Engine;
9use std::collections::HashMap;
10use std::fmt;
11
12impl_opts_required_builder!(url =>
13 ImageBuild,
15 path => "path"
19);
20
21#[derive(Debug, Clone, PartialEq, Eq)]
22#[derive(Default)]
24pub enum NetworkMode {
25 #[default]
27 Bridge,
28 Host,
30 None,
32 Container,
34 Custom(String),
36}
37
38impl AsRef<str> for NetworkMode {
39 fn as_ref(&self) -> &str {
40 match self {
41 NetworkMode::Bridge => "bridge",
42 NetworkMode::Host => "host",
43 NetworkMode::None => "none",
44 NetworkMode::Container => "container",
45 NetworkMode::Custom(custom) => custom,
46 }
47 }
48}
49
50impl fmt::Display for NetworkMode {
51 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52 write!(f, "{}", self.as_ref())
53 }
54}
55
56#[derive(Debug, Clone, PartialEq, Eq)]
57pub struct Platform {
59 os: String,
60 arch: Option<String>,
61 version: Option<String>,
62}
63
64impl Platform {
65 pub fn new(os: impl Into<String>) -> Self {
66 Self {
67 os: os.into(),
68 arch: None,
69 version: None,
70 }
71 }
72
73 pub fn arch(mut self, arch: impl Into<String>) -> Self {
74 self.arch = Some(arch.into());
75 self
76 }
77
78 pub fn version(mut self, version: impl Into<String>) -> Self {
79 self.version = Some(version.into());
80 self
81 }
82}
83
84impl fmt::Display for Platform {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 if let Some(arch) = &self.arch {
87 if let Some(vers) = &self.version {
88 write!(f, "{}/{}/{}", self.os, arch, vers)
89 } else {
90 write!(f, "{}/{}", self.os, arch)
91 }
92 } else {
93 write!(f, "{}", self.os)
94 }
95 }
96}
97
98impl ImageBuildOptsBuilder {
99 impl_url_bool_field!(
100 all_platforms => "allplatforms"
105 );
106
107 impl_map_field!(url
108 build_args => "buildargs"
110 );
111
112 pub fn cache_from<S>(mut self, images: impl IntoIterator<Item = S>) -> Self
114 where
115 S: Into<String>,
116 {
117 self.params.insert(
118 "cachefrom",
119 serde_json::to_string(&images.into_iter().map(|i| i.into()).collect::<Vec<_>>())
120 .unwrap_or_default(),
121 );
122 self
123 }
124
125 impl_url_field!(
126 cpu_period: isize => "cpuperiod"
128 );
129
130 impl_url_field!(
131 cpu_quota: isize => "cpuquota"
133 );
134
135 impl_url_field!(
136 cpu_set_cpus: isize => "cpusetcpus"
138 );
139
140 impl_url_field!(
141 cpu_shares: isize => "cpushares"
143 );
144
145 impl_url_str_field!(
146 dockerfile => "dockerfile"
149 );
150
151 impl_url_str_field!(
152 extra_hosts => "extrahosts"
154 );
155
156 impl_url_bool_field!(
157 force_rm => "forcerm"
159 );
160
161 impl_url_bool_field!(
162 http_proxy => "httpproxy"
164 );
165
166 impl_map_field!(url
167 labels => "labels"
169 );
170
171 impl_url_bool_field!(
172 layers => "layers"
174 );
175
176 impl_url_field!(
177 memory: usize => "memory"
180 );
181
182 impl_url_field!(
183 memswap: usize => "memswap"
185 );
186
187 impl_url_enum_field!(
188 network_mode: NetworkMode => "networkmode"
190 );
191
192 impl_url_bool_field!(
193 no_cache => "nocache"
195 );
196
197 impl_url_str_field!(
198 outputs => "outputs"
200 );
201
202 pub fn platform(mut self, platform: Platform) -> Self {
203 self.params.insert("platform", platform.to_string());
204 self
205 }
206
207 impl_url_bool_field!(
208 pull => "pull"
210 );
211
212 impl_url_bool_field!(
213 quiet => "q"
215 );
216
217 impl_url_str_field!(
218 remote => "remote"
226 );
227
228 impl_url_bool_field!(
229 remove => "rm"
231 );
232
233 impl_url_field!(
234 shared_mem_size: usize => "shmsize"
237 );
238
239 impl_url_bool_field!(
240 squash => "squash"
242 );
243
244 impl_url_str_field!(
245 tag => "t"
247 );
248
249 impl_url_str_field!(
250 target => "target"
252 );
253
254 impl_url_vec_field!(
255 unset_env => "unsetenv"
257 );
258}
259
260impl_opts_builder!(url =>
261 ImageList
263);
264
265#[derive(Debug, Clone)]
266pub enum ImageOpt {
268 Name(crate::Id),
269 Tag(crate::Id, String),
270 Digest(crate::Id, String),
271}
272
273impl fmt::Display for ImageOpt {
274 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
275 use ImageOpt::*;
276 match self {
277 Name(id) => write!(f, "{id}"),
278 Tag(id, tag) => write!(f, "{id}:{tag}"),
279 Digest(id, digest) => write!(f, "{id}@{digest}"),
280 }
281 }
282}
283
284#[derive(Debug)]
285pub enum ImageListFilter {
287 Before(ImageOpt),
288 Dangling(bool),
289 LabelKey(String),
291 LabelKeyVal(String, String),
293 NoLabelKey(String),
295 NoLabelKeyVal(String, String),
297 Reference(crate::Id, Option<String>),
299 Id(crate::Id),
300 Since(ImageOpt),
301}
302
303impl Filter for ImageListFilter {
304 fn query_item(&self) -> FilterItem {
305 use ImageListFilter::*;
306 match &self {
307 Before(image) => FilterItem::new("before", image.to_string()),
308 Dangling(dangling) => FilterItem::new("dangling", dangling.to_string()),
309 LabelKey(key) => FilterItem::new("label", key.clone()),
310 LabelKeyVal(key, val) => FilterItem::new("label", format!("{key}={val}")),
311 NoLabelKey(key) => FilterItem::new("label!", key.clone()),
312 NoLabelKeyVal(key, val) => FilterItem::new("label!", format!("{key}={val}")),
313 Reference(image, tag) => FilterItem::new(
314 "reference",
315 if let Some(tag) = tag {
316 format!("{image}:{tag}")
317 } else {
318 image.to_string()
319 },
320 ),
321 Id(id) => FilterItem::new("id", id.to_string()),
322 Since(image) => FilterItem::new("since", image.to_string()),
323 }
324 }
325}
326
327impl ImageListOptsBuilder {
328 impl_filter_func!(ImageListFilter);
329
330 impl_url_bool_field!(
331 all => "all"
333 );
334}
335
336impl_opts_builder!(url =>
337 ImageTag
339);
340
341impl ImageTagOptsBuilder {
342 impl_url_str_field!(
343 repo => "repo"
345 );
346
347 impl_url_str_field!(
348 tag => "tag"
350 );
351}
352
353#[derive(Clone, Serialize, Debug)]
354#[serde(untagged)]
355pub enum RegistryAuth {
356 Password {
357 username: String,
358 password: String,
359
360 #[serde(skip_serializing_if = "Option::is_none")]
361 email: Option<String>,
362
363 #[serde(rename = "serveraddress")]
364 #[serde(skip_serializing_if = "Option::is_none")]
365 server_address: Option<String>,
366 },
367 Token {
368 #[serde(rename = "identitytoken")]
369 identity_token: String,
370 },
371}
372
373impl RegistryAuth {
374 pub fn token(token: impl Into<String>) -> RegistryAuth {
376 RegistryAuth::Token {
377 identity_token: token.into(),
378 }
379 }
380
381 pub fn builder() -> RegistryAuthBuilder {
383 RegistryAuthBuilder::default()
384 }
385
386 pub fn serialize(&self) -> String {
388 serde_json::to_string(self)
389 .map(|c| base64::engine::general_purpose::URL_SAFE.encode(c))
390 .unwrap_or_default()
391 }
392}
393
394#[derive(Default)]
395pub struct RegistryAuthBuilder {
396 username: Option<String>,
397 password: Option<String>,
398 email: Option<String>,
399 server_address: Option<String>,
400}
401
402impl RegistryAuthBuilder {
403 pub fn username(&mut self, username: impl Into<String>) -> &mut Self {
405 self.username = Some(username.into());
406 self
407 }
408
409 pub fn password(&mut self, password: impl Into<String>) -> &mut Self {
411 self.password = Some(password.into());
412 self
413 }
414
415 pub fn email(&mut self, email: impl Into<String>) -> &mut Self {
417 self.email = Some(email.into());
418 self
419 }
420
421 pub fn server_address(&mut self, server_address: impl Into<String>) -> &mut Self {
424 self.server_address = Some(server_address.into());
425 self
426 }
427
428 pub fn build(&self) -> RegistryAuth {
430 RegistryAuth::Password {
431 username: self.username.clone().unwrap_or_default(),
432 password: self.password.clone().unwrap_or_default(),
433 email: self.email.clone(),
434 server_address: self.server_address.clone(),
435 }
436 }
437}
438
439#[derive(Default, Debug)]
440pub struct PullOpts {
441 auth: Option<RegistryAuth>,
442 params: HashMap<&'static str, String>,
443}
444
445impl PullOpts {
446 pub fn builder() -> PullOptsBuilder {
448 PullOptsBuilder::default()
449 }
450
451 pub fn serialize(&self) -> Option<String> {
453 if self.params.is_empty() {
454 None
455 } else {
456 Some(containers_api::url::encoded_pairs(
457 self.params.iter().map(|(k, v)| (k, v)),
458 ))
459 }
460 }
461
462 pub(crate) fn auth_header(&self) -> Option<String> {
463 self.auth.clone().map(|a| a.serialize())
464 }
465}
466
467#[derive(Debug, Clone, PartialEq, Eq)]
468#[derive(Default)]
470pub enum PullPolicy {
471 #[default]
472 Always,
473 Missing,
474 Newer,
475 Never,
476}
477
478impl AsRef<str> for PullPolicy {
479 fn as_ref(&self) -> &str {
480 match self {
481 PullPolicy::Always => "always",
482 PullPolicy::Missing => "missing",
483 PullPolicy::Newer => "newer",
484 PullPolicy::Never => "never",
485 }
486 }
487}
488
489impl fmt::Display for PullPolicy {
490 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
491 write!(f, "{}", self.as_ref())
492 }
493}
494
495#[derive(Debug, Default)]
496pub struct PullOptsBuilder {
497 auth: Option<RegistryAuth>,
498 params: HashMap<&'static str, String>,
499}
500
501impl PullOptsBuilder {
502 impl_url_bool_field!(
503 all_tags => "allTags"
505 );
506
507 impl_url_str_field!(
508 arch => "Arch"
510 );
511
512 impl_url_str_field!(
513 credentials => "credentials"
515 );
516
517 impl_url_str_field!(
518 os => "OS"
520 );
521
522 impl_url_enum_field!(
523 policy: PullPolicy => "policy"
525 );
526
527 impl_url_bool_field!(
528 quiet => "quiet"
530 );
531
532 impl_url_str_field!(
533 reference => "reference"
535 );
536
537 impl_url_bool_field!(
538 tls_verify => "tlsVerify"
540 );
541
542 impl_url_str_field!(
543 variant => "Variant"
545 );
546
547 pub fn auth(&mut self, auth: RegistryAuth) -> &mut Self {
548 self.auth = Some(auth);
549 self
550 }
551
552 pub fn build(&mut self) -> PullOpts {
553 PullOpts {
554 auth: self.auth.take(),
555 params: self.params.clone(),
556 }
557 }
558}
559
560impl_opts_builder!(url =>
561 ImageExport
563);
564
565impl ImageExportOptsBuilder {
566 impl_url_bool_field!(
567 compress => "compress"
569 );
570
571 impl_url_str_field!(
572 format => "format"
574 );
575}
576
577impl_opts_builder!(url =>
578 ImageImport
580);
581
582impl ImageImportOptsBuilder {
583 impl_url_vec_field!(
584 changes => "changes"
586 );
587
588 impl_url_str_field!(
589 message => "message"
591 );
592
593 impl_url_str_field!(
594 reference => "reference"
596 );
597
598 impl_url_str_field!(
599 url => "url"
601 );
602}
603
604impl_opts_builder!(url =>
605 ImageTree
607);
608
609impl ImageTreeOptsBuilder {
610 impl_url_bool_field!(
611 what_requires => "whatrequires"
613 );
614}
615
616impl_opts_builder!(url =>
617 ImagesRemove
619);
620
621impl ImagesRemoveOptsBuilder {
622 impl_url_bool_field!(
623 all => "all"
625 );
626
627 impl_url_bool_field!(
628 force => "force"
630 );
631
632 impl_url_bool_field!(
633 ignore => "ignore"
635 );
636
637 impl_url_vec_field!(
638 images => "images"
640 );
641
642 impl_url_bool_field!(
643 lookup_manifest => "lookupManifest"
645 );
646}
647
648#[derive(Default, Debug)]
649pub struct ImagePushOpts {
651 auth: Option<RegistryAuth>,
652 params: HashMap<&'static str, String>,
653}
654
655impl ImagePushOpts {
656 pub fn builder() -> ImagePushOptsBuilder {
658 ImagePushOptsBuilder::default()
659 }
660
661 pub fn serialize(&self) -> Option<String> {
663 if self.params.is_empty() {
664 None
665 } else {
666 Some(containers_api::url::encoded_pairs(
667 self.params.iter().map(|(k, v)| (k, v)),
668 ))
669 }
670 }
671
672 pub(crate) fn auth_header(&self) -> Option<String> {
673 self.auth.clone().map(|a| a.serialize())
674 }
675}
676
677#[derive(Debug, Default)]
678pub struct ImagePushOptsBuilder {
679 auth: Option<RegistryAuth>,
680 params: HashMap<&'static str, String>,
681}
682
683impl ImagePushOptsBuilder {
684 impl_url_str_field!(
685 destination => "destination"
687 );
688
689 impl_url_bool_field!(
690 quiet => "quiet"
692 );
693
694 impl_url_bool_field!(
695 tls_verify => "tlsVerify"
697 );
698
699 pub fn auth(mut self, auth: RegistryAuth) -> Self {
700 self.auth = Some(auth);
701 self
702 }
703
704 pub fn build(self) -> ImagePushOpts {
705 ImagePushOpts {
706 auth: self.auth,
707 params: self.params,
708 }
709 }
710}
711
712#[derive(Debug)]
713pub enum ImagePruneFilter {
715 Dangling(bool),
718 Until(String),
723 LabelKey(String),
725 LabelKeyVal(String, String),
727 NoLabelKey(String),
729 NoLabelKeyVal(String, String),
731}
732
733impl Filter for ImagePruneFilter {
734 fn query_item(&self) -> FilterItem {
735 use ImagePruneFilter::*;
736 match &self {
737 Dangling(dangling) => FilterItem::new("dangling", dangling.to_string()),
738 Until(until) => FilterItem::new("until", until.to_string()),
739 LabelKey(key) => FilterItem::new("label", key.clone()),
740 LabelKeyVal(key, val) => FilterItem::new("label", format!("{key}={val}")),
741 NoLabelKey(key) => FilterItem::new("label!", key.clone()),
742 NoLabelKeyVal(key, val) => FilterItem::new("label!", format!("{key}={val}")),
743 }
744 }
745}
746
747impl_opts_builder!(url =>
748 ImagePrune
750);
751
752impl ImagePruneOptsBuilder {
753 impl_filter_func!(
754 ImagePruneFilter
756 );
757
758 impl_url_bool_field!(
759 all => "all"
761 );
762
763 impl_url_bool_field!(
764 external => "external"
767 );
768}
769
770#[derive(Debug)]
771pub enum ImageSearchFilter {
773 IsAutomated(bool),
774 IsOfficial(bool),
775 Stars(usize),
777}
778
779impl Filter for ImageSearchFilter {
780 fn query_item(&self) -> FilterItem {
781 use ImageSearchFilter::*;
782 match &self {
783 IsAutomated(is_automated) => FilterItem::new("is-automated", is_automated.to_string()),
784 IsOfficial(is_official) => FilterItem::new("is-official", is_official.to_string()),
785 Stars(stars) => FilterItem::new("stars", stars.to_string()),
786 }
787 }
788}
789
790impl_opts_builder!(url =>
791 ImageSearch
793);
794
795impl ImageSearchOptsBuilder {
796 impl_filter_func!(
797 ImageSearchFilter
799 );
800
801 impl_url_field!(
802 limit: usize => "limit"
804 );
805
806 impl_url_bool_field!(
807 list_tags => "listTags"
809 );
810
811 impl_url_str_field!(
812 term => "term"
814 );
815
816 impl_url_bool_field!(
817 tls_verify => "tlsVerify"
819 );
820}
821
822impl_opts_builder!(url =>
823 ImagesExport
825);
826
827impl ImagesExportOptsBuilder {
828 impl_url_bool_field!(
829 compress => "compress"
831 );
832
833 impl_url_str_field!(
834 format => "format"
836 );
837
838 impl_url_bool_field!(
839 oci_accept_uncompressed_layers => "ociAcceptUncompressedLayers"
841 );
842
843 impl_url_vec_field!(
844 references => "references"
846 );
847}