1use core::fmt;
2use indexmap::{IndexMap, IndexSet};
3use merge_it::*;
4#[cfg(feature = "schemars")]
5use schemars::JsonSchema;
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8use std::cmp::Ordering;
9use std::collections::{BTreeMap, BTreeSet};
10
11type StringBTreeMap = BTreeMap<String, String>;
12
13mod service;
14pub use service::*;
15
16#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
17#[cfg_attr(feature = "schemars", derive(JsonSchema))]
18#[serde(untagged)]
19pub enum StringOrNum {
20 String(String),
21 Num(i64),
22}
23
24#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, PartialOrd)]
25#[cfg_attr(feature = "schemars", derive(JsonSchema))]
26#[serde(untagged)]
27pub enum SingleValue {
28 String(String),
29 Bool(bool),
30 Int(i64),
31 Float(f64),
32}
33
34impl fmt::Display for SingleValue {
35 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
36 match self {
37 Self::String(s) => f.write_str(s),
38 Self::Bool(b) => write!(f, "{b}"),
39 Self::Int(i) => write!(f, "{i}"),
40 Self::Float(fl) => write!(f, "{fl}"),
41 }
42 }
43}
44
45#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
46#[cfg_attr(feature = "schemars", derive(JsonSchema))]
47#[serde(untagged)]
48pub enum ListOrMap {
49 List(BTreeSet<String>),
50 Map(BTreeMap<String, String>),
51}
52
53impl ListOrMap {
54 pub fn contains(&self, key: &str) -> bool {
55 match self {
56 Self::List(list) => list.contains(key),
57 Self::Map(map) => map.contains_key(key),
58 }
59 }
60
61 pub fn get(&self, key: &str) -> Option<&String> {
62 match self {
63 Self::List(list) => list.get(key),
64 Self::Map(map) => map.get(key),
65 }
66 }
67
68 pub fn is_empty(&self) -> bool {
69 match self {
70 Self::List(btree_set) => btree_set.is_empty(),
71 Self::Map(btree_map) => btree_map.is_empty(),
72 }
73 }
74}
75
76impl Default for ListOrMap {
77 fn default() -> Self {
78 Self::List(Default::default())
79 }
80}
81
82impl Merge for ListOrMap {
83 fn merge(&mut self, other: Self) {
84 match self {
85 Self::List(left_list) => match other {
86 Self::List(other_list) => {
87 left_list.extend(other_list);
88 }
89 Self::Map(other_map) => {
90 if left_list.is_empty() || !other_map.is_empty() {
91 *self = Self::Map(other_map);
92 }
93 }
94 },
95 Self::Map(left_map) => match other {
96 Self::List(other_list) => {
97 if left_map.is_empty() || !other_list.is_empty() {
98 *self = Self::List(other_list);
99 }
100 }
101 Self::Map(other_map) => {
102 left_map.extend(other_map);
103 }
104 },
105 }
106 }
107}
108
109#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
110#[cfg_attr(feature = "schemars", derive(JsonSchema))]
111#[serde(untagged)]
112pub enum StringOrList {
113 String(String),
114 List(Vec<String>),
115}
116
117#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
118#[cfg_attr(feature = "schemars", derive(JsonSchema))]
119#[serde(untagged)]
120pub enum StringOrSortedList {
121 String(String),
122 List(BTreeSet<String>),
123}
124
125impl StringOrSortedList {
126 pub fn is_empty(&self) -> bool {
127 match self {
128 Self::String(str) => str.is_empty(),
129 Self::List(list) => list.is_empty(),
130 }
131 }
132}
133
134impl Default for StringOrSortedList {
135 fn default() -> Self {
136 Self::List(Default::default())
137 }
138}
139
140impl Merge for StringOrSortedList {
141 fn merge(&mut self, right: Self) {
142 match self {
143 Self::String(left_string) => {
144 match right {
145 Self::List(mut right_list) => {
146 let left_string = std::mem::take(left_string);
147
148 right_list.insert(left_string);
149
150 *self = Self::List(right_list);
151 }
152 Self::String(right_string) => {
153 if left_string.is_empty() || !right_string.is_empty() {
154 *left_string = right_string;
155 }
156 }
157 };
158 }
159 Self::List(left_list) => match right {
160 Self::String(right_string) => {
161 left_list.insert(right_string);
162 }
163 Self::List(right_list) => {
164 left_list.extend(right_list);
165 }
166 },
167 }
168 }
169}
170
171#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Default, Merge)]
173#[cfg_attr(feature = "schemars", derive(JsonSchema))]
174#[serde(default)]
175pub struct ComposeFile {
176 #[serde(skip_serializing_if = "Option::is_none")]
180 pub name: Option<String>,
181
182 #[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
188 pub include: BTreeSet<Include>,
189
190 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
194 #[cfg(feature = "presets")]
195 pub services: BTreeMap<String, ServicePresetRef>,
196 #[cfg(not(feature = "presets"))]
197 pub services: BTreeMap<String, Service>,
198
199 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
203 pub configs: BTreeMap<String, TopLevelConfig>,
204
205 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
209 pub models: BTreeMap<String, TopLevelModel>,
210
211 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
215 pub networks: BTreeMap<String, TopLevelNetwork>,
216
217 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
221 pub secrets: BTreeMap<String, TopLevelSecret>,
222
223 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
227 pub volumes: BTreeMap<String, TopLevelVolume>,
228
229 #[serde(flatten, skip_serializing_if = "BTreeMap::is_empty")]
230 pub extensions: BTreeMap<String, Value>,
231}
232
233impl ComposeFile {
234 pub fn new() -> Self {
235 Default::default()
236 }
237}
238
239#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default, Eq)]
243#[cfg_attr(feature = "schemars", derive(JsonSchema))]
244#[serde(deny_unknown_fields)]
245#[serde(default)]
246pub struct IncludeSettings {
247 #[serde(skip_serializing_if = "Option::is_none")]
249 pub path: Option<StringOrSortedList>,
250
251 #[serde(skip_serializing_if = "Option::is_none")]
253 pub project_directory: Option<String>,
254
255 #[serde(skip_serializing_if = "Option::is_none")]
259 pub env_file: Option<StringOrList>,
260}
261
262impl PartialOrd for IncludeSettings {
263 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
264 Some(self.cmp(other))
265 }
266}
267
268impl Ord for IncludeSettings {
269 fn cmp(&self, other: &Self) -> Ordering {
270 self.path.cmp(&other.path)
271 }
272}
273
274#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, PartialOrd, Ord, Eq)]
275#[cfg_attr(feature = "schemars", derive(JsonSchema))]
276#[serde(untagged)]
277pub enum Include {
278 Short(String),
279 Long(IncludeSettings),
280}
281
282impl Default for Include {
283 fn default() -> Self {
284 Self::Short(Default::default())
285 }
286}
287
288#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
289#[cfg_attr(feature = "schemars", derive(JsonSchema))]
290#[serde(untagged)]
291pub enum Ulimit {
292 Single(StringOrNum),
293 SoftHard {
294 soft: StringOrNum,
295 hard: StringOrNum,
296 },
297}
298
299#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
303#[cfg_attr(feature = "schemars", derive(JsonSchema))]
304#[serde(deny_unknown_fields)]
305pub struct TopLevelNetwork {
306 #[serde(skip_serializing_if = "Option::is_none")]
310 pub external: Option<bool>,
311 #[serde(skip_serializing_if = "Option::is_none")]
316 #[serde(default)]
317 pub name: Option<String>,
318
319 #[serde(skip_serializing_if = "Option::is_none")]
321 #[serde(default)]
322 pub internal: Option<bool>,
323
324 #[serde(skip_serializing_if = "Option::is_none")]
328 #[serde(default)]
329 pub driver: Option<String>,
330
331 #[serde(skip_serializing_if = "Option::is_none")]
333 #[serde(default)]
334 pub attachable: Option<bool>,
335
336 #[serde(skip_serializing_if = "Option::is_none")]
338 #[serde(default)]
339 enable_ipv4: Option<bool>,
340
341 #[serde(skip_serializing_if = "Option::is_none")]
343 #[serde(default)]
344 pub enable_ipv6: Option<bool>,
345
346 #[serde(skip_serializing_if = "Option::is_none")]
350 #[serde(default)]
351 pub ipam: Option<Ipam>,
352
353 #[serde(skip_serializing_if = "Option::is_none")]
357 #[serde(default)]
358 pub driver_opts: Option<BTreeMap<String, Option<SingleValue>>>,
359
360 #[serde(skip_serializing_if = "Option::is_none")]
361 #[serde(default)]
362 pub labels: Option<ListOrMap>,
363}
364
365#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Default)]
369#[cfg_attr(feature = "schemars", derive(JsonSchema))]
370#[serde(deny_unknown_fields)]
371#[serde(default)]
372pub struct Ipam {
373 #[serde(skip_serializing_if = "Option::is_none")]
375 pub driver: Option<String>,
376
377 #[serde(skip_serializing_if = "Option::is_none")]
379 pub config: Option<BTreeSet<IpamConfig>>,
380
381 #[serde(skip_serializing_if = "Option::is_none")]
383 pub options: Option<StringBTreeMap>,
384}
385
386#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Default)]
390#[cfg_attr(feature = "schemars", derive(JsonSchema))]
391#[serde(deny_unknown_fields)]
392#[serde(default)]
393pub struct IpamConfig {
394 #[serde(skip_serializing_if = "Option::is_none")]
396 pub subnet: Option<String>,
397
398 #[serde(skip_serializing_if = "Option::is_none")]
400 pub ip_range: Option<String>,
401
402 #[serde(skip_serializing_if = "Option::is_none")]
404 pub gateway: Option<String>,
405
406 #[serde(skip_serializing_if = "Option::is_none")]
408 pub aux_addresses: Option<StringBTreeMap>,
409}
410
411impl PartialOrd for IpamConfig {
412 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
413 Some(self.cmp(other))
414 }
415}
416
417impl Ord for IpamConfig {
418 fn cmp(&self, other: &Self) -> Ordering {
419 self.subnet.cmp(&other.subnet)
420 }
421}
422
423#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
425#[cfg_attr(feature = "schemars", derive(JsonSchema))]
426#[serde(rename_all = "lowercase")]
427pub enum EndpointMode {
428 Vip,
430
431 Dnsrr,
433}
434
435#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
437#[cfg_attr(feature = "schemars", derive(JsonSchema))]
438#[serde(rename_all = "kebab-case")]
439pub enum DeployMode {
440 Global,
442
443 Replicated,
445
446 ReplicatedJob,
450
451 GlobalJob,
454}
455
456#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Default)]
458#[cfg_attr(feature = "schemars", derive(JsonSchema))]
459#[serde(deny_unknown_fields)]
460#[serde(default)]
461pub struct Deploy {
462 #[serde(skip_serializing_if = "Option::is_none")]
464 pub endpoint_mode: Option<EndpointMode>,
465
466 #[serde(skip_serializing_if = "Option::is_none")]
468 pub mode: Option<DeployMode>,
469
470 #[serde(skip_serializing_if = "Option::is_none")]
472 pub replicas: Option<i64>,
473
474 #[serde(skip_serializing_if = "Option::is_none")]
476 pub labels: Option<ListOrMap>,
477
478 #[serde(skip_serializing_if = "Option::is_none")]
480 pub rollback_config: Option<RollbackConfig>,
481
482 #[serde(skip_serializing_if = "Option::is_none")]
484 pub update_config: Option<UpdateConfig>,
485
486 #[serde(skip_serializing_if = "Option::is_none")]
488 pub resources: Option<Resources>,
489
490 #[serde(skip_serializing_if = "Option::is_none")]
492 pub restart_policy: Option<RestartPolicy>,
493
494 #[serde(skip_serializing_if = "Option::is_none")]
496 pub placement: Option<Placement>,
497}
498
499#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Default)]
501#[cfg_attr(feature = "schemars", derive(JsonSchema))]
502#[serde(deny_unknown_fields)]
503#[serde(default)]
504pub struct Limits {
505 #[serde(skip_serializing_if = "Option::is_none")]
507 pub cpus: Option<StringOrNum>,
508
509 #[serde(skip_serializing_if = "Option::is_none")]
511 pub memory: Option<String>,
512
513 #[serde(skip_serializing_if = "Option::is_none")]
515 pub pids: Option<StringOrNum>,
516}
517
518#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Default)]
520#[cfg_attr(feature = "schemars", derive(JsonSchema))]
521#[serde(default)]
522#[serde(deny_unknown_fields)]
523pub struct Reservations {
524 #[serde(skip_serializing_if = "Option::is_none")]
526 pub cpus: Option<StringOrNum>,
527
528 #[serde(skip_serializing_if = "Option::is_none")]
530 pub memory: Option<String>,
531
532 #[serde(skip_serializing_if = "Option::is_none")]
534 pub generic_resources: Option<BTreeSet<GenericResource>>,
535
536 #[serde(skip_serializing_if = "Option::is_none")]
538 pub devices: Option<BTreeSet<Device>>,
539}
540
541#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Default, PartialOrd, Ord)]
543#[cfg_attr(feature = "schemars", derive(JsonSchema))]
544#[serde(default)]
545#[serde(deny_unknown_fields)]
546pub struct GenericResource {
547 #[serde(skip_serializing_if = "Option::is_none")]
549 pub discrete_resource_spec: Option<DiscreteResourceSpec>,
550}
551
552#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Default)]
554#[cfg_attr(feature = "schemars", derive(JsonSchema))]
555#[serde(default)]
556#[serde(deny_unknown_fields)]
557pub struct DiscreteResourceSpec {
558 #[serde(skip_serializing_if = "Option::is_none")]
560 pub kind: Option<String>,
561
562 #[serde(skip_serializing_if = "Option::is_none")]
564 pub value: Option<StringOrNum>,
565}
566
567impl PartialOrd for DiscreteResourceSpec {
568 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
569 Some(self.cmp(other))
570 }
571}
572
573impl Ord for DiscreteResourceSpec {
574 fn cmp(&self, other: &Self) -> Ordering {
575 self.kind.cmp(&other.kind)
576 }
577}
578
579#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
581#[cfg_attr(feature = "schemars", derive(JsonSchema))]
582#[serde(deny_unknown_fields)]
583pub struct Device {
584 #[serde(skip_serializing_if = "Option::is_none")]
586 #[serde(default)]
587 pub driver: Option<String>,
588
589 #[serde(skip_serializing_if = "Option::is_none")]
591 #[serde(default)]
592 pub count: Option<StringOrNum>,
593
594 #[serde(skip_serializing_if = "Option::is_none")]
596 #[serde(default)]
597 pub device_ids: Option<BTreeSet<String>>,
598
599 #[serde(skip_serializing_if = "BTreeSet::is_empty")]
601 pub capabilities: BTreeSet<String>,
602
603 #[serde(skip_serializing_if = "Option::is_none")]
605 #[serde(default)]
606 pub options: Option<ListOrMap>,
607}
608
609impl PartialOrd for Device {
610 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
611 Some(self.cmp(other))
612 }
613}
614
615impl Ord for Device {
616 fn cmp(&self, other: &Self) -> Ordering {
617 self.driver.cmp(&other.driver)
618 }
619}
620
621#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Default)]
623#[cfg_attr(feature = "schemars", derive(JsonSchema))]
624#[serde(default)]
625#[serde(deny_unknown_fields)]
626pub struct Placement {
627 #[serde(skip_serializing_if = "Option::is_none")]
629 pub constraints: Option<BTreeSet<String>>,
630
631 #[serde(skip_serializing_if = "Option::is_none")]
633 pub preferences: Option<BTreeSet<Preferences>>,
634}
635
636#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
638#[cfg_attr(feature = "schemars", derive(JsonSchema))]
639#[serde(deny_unknown_fields)]
640pub struct Preferences {
641 pub spread: String,
642}
643
644#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Default)]
648#[cfg_attr(feature = "schemars", derive(JsonSchema))]
649#[serde(deny_unknown_fields)]
650#[serde(default)]
651pub struct Resources {
652 #[serde(skip_serializing_if = "Option::is_none")]
654 pub limits: Option<Limits>,
655 #[serde(skip_serializing_if = "Option::is_none")]
657 pub reservations: Option<Reservations>,
658}
659
660#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
662#[cfg_attr(feature = "schemars", derive(JsonSchema))]
663#[serde(rename_all = "kebab-case")]
664pub enum RestartPolicyCondition {
665 None,
667
668 OnFailure,
670
671 Any,
673}
674
675#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Default)]
679#[cfg_attr(feature = "schemars", derive(JsonSchema))]
680#[serde(default)]
681#[serde(deny_unknown_fields)]
682pub struct RestartPolicy {
683 #[serde(skip_serializing_if = "Option::is_none")]
685 pub condition: Option<RestartPolicyCondition>,
686
687 #[serde(skip_serializing_if = "Option::is_none")]
689 pub delay: Option<String>,
690
691 #[serde(skip_serializing_if = "Option::is_none")]
693 pub max_attempts: Option<i64>,
694
695 #[serde(skip_serializing_if = "Option::is_none")]
697 pub window: Option<String>,
698}
699
700#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
702#[cfg_attr(feature = "schemars", derive(JsonSchema))]
703#[serde(rename_all = "kebab-case")]
704pub enum UpdateFailureAction {
705 Continue,
706 Rollback,
707 Pause,
708}
709
710#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
712#[cfg_attr(feature = "schemars", derive(JsonSchema))]
713#[serde(rename_all = "kebab-case")]
714pub enum RollbackFailureAction {
715 Continue,
716 Pause,
717}
718
719#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
721#[cfg_attr(feature = "schemars", derive(JsonSchema))]
722#[serde(rename_all = "kebab-case")]
723pub enum OperationsOrder {
724 StartFirst,
725 StopFirst,
726}
727
728#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Default)]
732#[cfg_attr(feature = "schemars", derive(JsonSchema))]
733#[serde(default)]
734#[serde(deny_unknown_fields)]
735pub struct RollbackConfig {
736 #[serde(skip_serializing_if = "Option::is_none")]
738 pub parallelism: Option<i64>,
739 #[serde(skip_serializing_if = "Option::is_none")]
741 pub delay: Option<String>,
742 #[serde(skip_serializing_if = "Option::is_none")]
744 pub failure_action: Option<RollbackFailureAction>,
745 #[serde(skip_serializing_if = "Option::is_none")]
747 pub monitor: Option<String>,
748 #[serde(skip_serializing_if = "Option::is_none")]
750 pub max_failure_ratio: Option<f64>,
751
752 #[serde(skip_serializing_if = "Option::is_none")]
754 pub order: Option<OperationsOrder>,
755}
756
757#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Default)]
761#[cfg_attr(feature = "schemars", derive(JsonSchema))]
762#[serde(deny_unknown_fields)]
763#[serde(default)]
764pub struct UpdateConfig {
765 #[serde(skip_serializing_if = "Option::is_none")]
767 pub parallelism: Option<i64>,
768 #[serde(skip_serializing_if = "Option::is_none")]
770 pub delay: Option<String>,
771 #[serde(skip_serializing_if = "Option::is_none")]
773 pub failure_action: Option<UpdateFailureAction>,
774 #[serde(skip_serializing_if = "Option::is_none")]
776 pub monitor: Option<String>,
777 #[serde(skip_serializing_if = "Option::is_none")]
779 pub max_failure_ratio: Option<f64>,
780
781 #[serde(skip_serializing_if = "Option::is_none")]
783 pub order: Option<OperationsOrder>,
784}
785
786#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
790#[cfg_attr(feature = "schemars", derive(JsonSchema))]
791#[serde(deny_unknown_fields, rename_all = "snake_case")]
792pub enum TopLevelSecret {
793 File(String),
795 Environment(String),
797 #[serde(untagged)]
798 External {
799 external: bool,
801 name: String,
803 },
804}
805
806#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
808#[cfg_attr(feature = "schemars", derive(JsonSchema))]
809#[serde(untagged)]
810pub enum ServiceConfigOrSecret {
811 String(String),
813 Advanced(ServiceConfigOrSecretSettings),
815}
816
817impl PartialOrd for ServiceConfigOrSecretSettings {
818 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
819 Some(self.cmp(other))
820 }
821}
822
823impl Ord for ServiceConfigOrSecretSettings {
824 fn cmp(&self, other: &Self) -> Ordering {
825 self.source.cmp(&other.source)
826 }
827}
828
829#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
831#[cfg_attr(feature = "schemars", derive(JsonSchema))]
832#[serde(deny_unknown_fields)]
833pub struct ServiceConfigOrSecretSettings {
834 pub source: String,
836
837 #[serde(skip_serializing_if = "Option::is_none")]
839 #[serde(default)]
840 pub target: Option<String>,
841
842 #[serde(skip_serializing_if = "Option::is_none")]
844 #[serde(default)]
845 pub uid: Option<String>,
846
847 #[serde(skip_serializing_if = "Option::is_none")]
849 #[serde(default)]
850 pub gid: Option<String>,
851
852 #[serde(skip_serializing_if = "Option::is_none")]
854 #[serde(default)]
855 pub mode: Option<StringOrNum>,
856}
857
858#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Default, Eq)]
862#[cfg_attr(feature = "schemars", derive(JsonSchema))]
863#[serde(default)]
864#[serde(deny_unknown_fields)]
865pub struct TopLevelConfig {
866 #[serde(skip_serializing_if = "Option::is_none")]
868 pub name: Option<String>,
869
870 #[serde(skip_serializing_if = "Option::is_none")]
872 pub external: Option<bool>,
873
874 #[serde(skip_serializing_if = "Option::is_none")]
876 pub content: Option<String>,
877
878 #[serde(skip_serializing_if = "Option::is_none")]
880 pub environment: Option<String>,
881
882 #[serde(skip_serializing_if = "Option::is_none")]
884 pub file: Option<String>,
885}
886
887#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
891#[cfg_attr(feature = "schemars", derive(JsonSchema))]
892#[serde(untagged)]
893pub enum ExtraHosts {
894 List(BTreeSet<String>),
896
897 Map(BTreeMap<String, StringOrSortedList>),
899}
900
901impl Merge for ExtraHosts {
902 fn merge(&mut self, other: Self) {
903 if let Self::List(left_list) = self
904 && let Self::List(right_list) = other
905 {
906 left_list.extend(right_list);
907 } else if let Self::Map(left_map) = self
908 && let Self::Map(right_map) = other
909 {
910 left_map.extend(right_map);
911 } else {
912 *self = other;
913 }
914 }
915}
916
917#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
921#[cfg_attr(feature = "schemars", derive(JsonSchema))]
922#[serde(deny_unknown_fields)]
923pub struct TopLevelModel {
924 pub model: String,
926
927 #[serde(skip_serializing_if = "Option::is_none")]
929 #[serde(default)]
930 pub name: Option<String>,
931
932 #[serde(skip_serializing_if = "Option::is_none")]
934 #[serde(default)]
935 pub context_size: Option<u64>,
936
937 #[serde(skip_serializing_if = "Option::is_none")]
939 #[serde(default)]
940 pub runtime_flags: Option<Vec<String>>,
941}
942
943impl PartialOrd for TopLevelModel {
944 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
945 Some(self.cmp(other))
946 }
947}
948
949impl Ord for TopLevelModel {
950 fn cmp(&self, other: &Self) -> Ordering {
951 self.name
952 .cmp(&other.name)
953 .then_with(|| self.model.cmp(&other.model))
954 }
955}
956
957#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
961#[cfg_attr(feature = "schemars", derive(JsonSchema))]
962#[serde(deny_unknown_fields)]
963pub struct TopLevelVolume {
964 #[serde(default, skip_serializing_if = "Option::is_none")]
968 pub external: Option<bool>,
969
970 #[serde(skip_serializing_if = "Option::is_none")]
974 #[serde(default)]
975 pub name: Option<String>,
976
977 #[serde(skip_serializing_if = "Option::is_none")]
979 #[serde(default)]
980 pub driver: Option<String>,
981
982 #[serde(skip_serializing_if = "BTreeMap::is_empty")]
984 #[serde(default)]
985 pub driver_opts: BTreeMap<String, SingleValue>,
986
987 #[serde(skip_serializing_if = "Option::is_none")]
993 #[serde(default)]
994 pub labels: Option<ListOrMap>,
995}