Skip to main content

compose_lens/model/
sections.rs

1//! Field-level build and deploy section models.
2
3use super::{FieldReference, Located};
4use crate::source::SourceSpan;
5
6/// A Compose build declaration with short and long forms retained.
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub enum Build {
9    /// A scalar build context.
10    Context(Located<String>),
11    /// A mapping of independently classified build fields.
12    Definition(BuildDefinition),
13}
14
15/// A long-syntax build definition.
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct BuildDefinition {
18    span: SourceSpan,
19    fields: Vec<BuildField>,
20    extension_fields: Vec<FieldReference>,
21    unknown_fields: Vec<FieldReference>,
22}
23
24impl BuildDefinition {
25    pub(super) const fn new(span: SourceSpan) -> Self {
26        Self {
27            span,
28            fields: Vec::new(),
29            extension_fields: Vec::new(),
30            unknown_fields: Vec::new(),
31        }
32    }
33
34    pub(super) fn push_field(&mut self, field: BuildField) {
35        self.fields.push(field);
36    }
37
38    pub(super) fn push_extension(&mut self, field: FieldReference) {
39        self.extension_fields.push(field);
40    }
41
42    pub(super) fn push_unknown(&mut self, field: FieldReference) {
43        self.unknown_fields.push(field);
44    }
45
46    /// Returns the complete mapping span.
47    #[must_use]
48    pub const fn span(&self) -> SourceSpan {
49        self.span
50    }
51
52    /// Returns recognized build fields in authored order.
53    #[must_use]
54    pub fn fields(&self) -> &[BuildField] {
55        &self.fields
56    }
57
58    /// Finds the first recognized field of the requested kind.
59    #[must_use]
60    pub fn field(&self, kind: BuildFieldKind) -> Option<&BuildField> {
61        self.fields.iter().find(|field| field.kind == kind)
62    }
63
64    /// Returns retained `x-` fields.
65    #[must_use]
66    pub fn extension_fields(&self) -> &[FieldReference] {
67        &self.extension_fields
68    }
69
70    /// Returns fields not recognized by this release.
71    #[must_use]
72    pub fn unknown_fields(&self) -> &[FieldReference] {
73        &self.unknown_fields
74    }
75}
76
77/// One recognized build subfield and its source reference.
78#[derive(Debug, Clone, PartialEq, Eq)]
79pub struct BuildField {
80    kind: BuildFieldKind,
81    reference: FieldReference,
82}
83
84impl BuildField {
85    pub(super) const fn new(kind: BuildFieldKind, reference: FieldReference) -> Self {
86        Self { kind, reference }
87    }
88
89    /// Returns the field's specification-level identity.
90    #[must_use]
91    pub const fn kind(&self) -> BuildFieldKind {
92        self.kind
93    }
94
95    /// Returns source spans for reading or editing the retained value.
96    #[must_use]
97    pub const fn reference(&self) -> &FieldReference {
98        &self.reference
99    }
100}
101
102/// Recognized fields from the current Compose Build Specification.
103#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
104#[non_exhaustive]
105pub enum BuildFieldKind {
106    /// Additional named build contexts.
107    AdditionalContexts,
108    /// Dockerfile build arguments.
109    Args,
110    /// External cache sources.
111    CacheFrom,
112    /// External cache destinations.
113    CacheTo,
114    /// Build context.
115    Context,
116    /// Dockerfile path.
117    Dockerfile,
118    /// Inline Dockerfile content.
119    DockerfileInline,
120    /// Build entitlements.
121    Entitlements,
122    /// Build-time host mappings.
123    ExtraHosts,
124    /// Container isolation technology.
125    Isolation,
126    /// Build image labels.
127    Labels,
128    /// Build network mode.
129    Network,
130    /// Disable build cache.
131    NoCache,
132    /// Target platforms.
133    Platforms,
134    /// Privileged build mode.
135    Privileged,
136    /// Supply-chain provenance.
137    Provenance,
138    /// Pull referenced images.
139    Pull,
140    /// Software bill of materials.
141    Sbom,
142    /// Build-time secret grants.
143    Secrets,
144    /// SSH agent/socket grants.
145    Ssh,
146    /// Build shared-memory size.
147    ShmSize,
148    /// Additional output tags.
149    Tags,
150    /// Dockerfile target stage.
151    Target,
152    /// Build-container resource limits.
153    Ulimits,
154}
155
156impl BuildFieldKind {
157    pub(super) fn from_name(name: &str) -> Option<Self> {
158        Some(match name {
159            "additional_contexts" => Self::AdditionalContexts,
160            "args" => Self::Args,
161            "cache_from" => Self::CacheFrom,
162            "cache_to" => Self::CacheTo,
163            "context" => Self::Context,
164            "dockerfile" => Self::Dockerfile,
165            "dockerfile_inline" => Self::DockerfileInline,
166            "entitlements" => Self::Entitlements,
167            "extra_hosts" => Self::ExtraHosts,
168            "isolation" => Self::Isolation,
169            "labels" => Self::Labels,
170            "network" => Self::Network,
171            "no_cache" => Self::NoCache,
172            "platforms" => Self::Platforms,
173            "privileged" => Self::Privileged,
174            "provenance" => Self::Provenance,
175            "pull" => Self::Pull,
176            "sbom" => Self::Sbom,
177            "secrets" => Self::Secrets,
178            "ssh" => Self::Ssh,
179            "shm_size" => Self::ShmSize,
180            "tags" => Self::Tags,
181            "target" => Self::Target,
182            "ulimits" => Self::Ulimits,
183            _ => return None,
184        })
185    }
186}
187
188/// A deploy definition split into independently classifiable fields.
189#[derive(Debug, Clone, PartialEq, Eq)]
190pub struct DeployDefinition {
191    span: SourceSpan,
192    fields: Vec<DeployField>,
193    extension_fields: Vec<FieldReference>,
194    unknown_fields: Vec<FieldReference>,
195}
196
197impl DeployDefinition {
198    pub(super) const fn new(span: SourceSpan) -> Self {
199        Self {
200            span,
201            fields: Vec::new(),
202            extension_fields: Vec::new(),
203            unknown_fields: Vec::new(),
204        }
205    }
206
207    pub(super) fn push_field(&mut self, field: DeployField) {
208        self.fields.push(field);
209    }
210
211    pub(super) fn push_extension(&mut self, field: FieldReference) {
212        self.extension_fields.push(field);
213    }
214
215    pub(super) fn push_unknown(&mut self, field: FieldReference) {
216        self.unknown_fields.push(field);
217    }
218
219    /// Returns the complete deploy mapping span.
220    #[must_use]
221    pub const fn span(&self) -> SourceSpan {
222        self.span
223    }
224
225    /// Returns recognized deploy fields in authored order.
226    #[must_use]
227    pub fn fields(&self) -> &[DeployField] {
228        &self.fields
229    }
230
231    /// Finds the first recognized field of the requested kind.
232    #[must_use]
233    pub fn field(&self, kind: DeployFieldKind) -> Option<&DeployField> {
234        self.fields.iter().find(|field| field.kind == kind)
235    }
236
237    /// Returns retained `x-` fields.
238    #[must_use]
239    pub fn extension_fields(&self) -> &[FieldReference] {
240        &self.extension_fields
241    }
242
243    /// Returns fields not recognized by this release.
244    #[must_use]
245    pub fn unknown_fields(&self) -> &[FieldReference] {
246        &self.unknown_fields
247    }
248}
249
250/// One recognized deploy subfield and its source reference.
251#[derive(Debug, Clone, PartialEq, Eq)]
252pub struct DeployField {
253    kind: DeployFieldKind,
254    reference: FieldReference,
255}
256
257impl DeployField {
258    pub(super) const fn new(kind: DeployFieldKind, reference: FieldReference) -> Self {
259        Self { kind, reference }
260    }
261
262    /// Returns the field's specification-level identity.
263    #[must_use]
264    pub const fn kind(&self) -> DeployFieldKind {
265        self.kind
266    }
267
268    /// Returns source spans for reading or editing the retained value.
269    #[must_use]
270    pub const fn reference(&self) -> &FieldReference {
271        &self.reference
272    }
273}
274
275/// Recognized fields from the current Compose Deploy Specification.
276#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
277#[non_exhaustive]
278pub enum DeployFieldKind {
279    /// Service-discovery endpoint mode.
280    EndpointMode,
281    /// Platform-service labels.
282    Labels,
283    /// Replication or job mode.
284    Mode,
285    /// Node-placement rules.
286    Placement,
287    /// Desired replica count.
288    Replicas,
289    /// Resource limits and reservations.
290    Resources,
291    /// Deploy-level restart policy.
292    RestartPolicy,
293    /// Rollback behavior.
294    RollbackConfig,
295    /// Rolling-update behavior.
296    UpdateConfig,
297}
298
299impl DeployFieldKind {
300    pub(super) fn from_name(name: &str) -> Option<Self> {
301        Some(match name {
302            "endpoint_mode" => Self::EndpointMode,
303            "labels" => Self::Labels,
304            "mode" => Self::Mode,
305            "placement" => Self::Placement,
306            "replicas" => Self::Replicas,
307            "resources" => Self::Resources,
308            "restart_policy" => Self::RestartPolicy,
309            "rollback_config" => Self::RollbackConfig,
310            "update_config" => Self::UpdateConfig,
311            _ => return None,
312        })
313    }
314}