1use serde::{Deserialize, Serialize};
2
3use crate::{ContractProvenance, Guard, GuardDnf, ResourceRef, ValueKind, YamlPath};
4
5#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
9pub struct SplitSegmentUse {
10 pub separator: String,
12 pub last: bool,
14}
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
18pub enum MergeLayerTransform {
19 Identity,
21 NilScrubbed,
23 ParsedMap,
25}
26
27#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
32pub struct MergeLayersUse {
33 pub layers: Vec<String>,
35 pub position: usize,
37 pub transforms: Vec<MergeLayerTransform>,
39 pub via_binding: bool,
47}
48
49impl MergeLayersUse {
50 #[must_use]
52 pub fn shadowed_by(&self) -> &[String] {
53 self.layers
54 .get(..self.position.min(self.layers.len()))
55 .unwrap_or_default()
56 }
57
58 #[must_use]
60 pub fn own_transform(&self) -> MergeLayerTransform {
61 self.transforms
62 .get(self.position)
63 .copied()
64 .unwrap_or(MergeLayerTransform::Identity)
65 }
66}
67
68#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
70pub struct ContractUse {
71 pub source_expr: String,
73 pub path: YamlPath,
75 pub kind: ValueKind,
77 pub condition: GuardDnf,
79 pub resource: Option<ResourceRef>,
81 #[serde(default, skip_serializing_if = "Vec::is_empty")]
83 pub provenance: Vec<ContractProvenance>,
84 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
87 pub stringified: bool,
88 #[serde(default, skip_serializing_if = "std::collections::BTreeSet::is_empty")]
93 pub template_supplied_member_keys: std::collections::BTreeSet<String>,
94 #[serde(default, skip_serializing_if = "Option::is_none")]
97 pub split_segment: Option<SplitSegmentUse>,
98 #[serde(default, skip_serializing_if = "Option::is_none")]
100 pub merge_layers: Option<MergeLayersUse>,
101 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
104 pub range_key: bool,
105 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
110 pub nil_omitting: bool,
111 #[serde(default, skip_serializing_if = "std::collections::BTreeMap::is_empty")]
117 pub omitted_members: std::collections::BTreeMap<String, Vec<Guard>>,
118 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
124 pub digest: bool,
125 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
130 pub merge_operand: bool,
131}
132
133impl<'de> Deserialize<'de> for ContractUse {
134 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
135 where
136 D: serde::Deserializer<'de>,
137 {
138 #[derive(Deserialize)]
139 struct WireContractUse {
140 source_expr: String,
141 path: YamlPath,
142 kind: ValueKind,
143 condition: GuardDnf,
144 resource: Option<ResourceRef>,
145 #[serde(default)]
146 provenance: Vec<ContractProvenance>,
147 #[serde(default)]
148 stringified: bool,
149 #[serde(default)]
150 template_supplied_member_keys: std::collections::BTreeSet<String>,
151 #[serde(default)]
152 split_segment: Option<SplitSegmentUse>,
153 #[serde(default)]
154 merge_layers: Option<MergeLayersUse>,
155 #[serde(default)]
156 range_key: bool,
157 #[serde(default)]
158 nil_omitting: bool,
159 #[serde(default)]
160 omitted_members: std::collections::BTreeMap<String, Vec<Guard>>,
161 #[serde(default)]
162 digest: bool,
163 #[serde(default)]
164 merge_operand: bool,
165 }
166
167 let wire = WireContractUse::deserialize(deserializer)?;
168 Ok(Self {
169 source_expr: wire.source_expr,
170 path: wire.path,
171 kind: wire.kind,
172 condition: wire.condition,
173 resource: wire.resource,
174 provenance: wire.provenance,
175 stringified: wire.stringified,
176 template_supplied_member_keys: wire.template_supplied_member_keys,
177 split_segment: wire.split_segment,
178 merge_layers: wire.merge_layers,
179 range_key: wire.range_key,
180 nil_omitting: wire.nil_omitting,
181 omitted_members: wire.omitted_members,
182 digest: wire.digest,
183 merge_operand: wire.merge_operand,
184 })
185 }
186}
187
188impl ContractUse {
189 #[must_use]
191 pub fn new(
192 source_expr: String,
193 path: YamlPath,
194 kind: ValueKind,
195 guards: Vec<Guard>,
196 resource: Option<ResourceRef>,
197 ) -> Self {
198 Self::with_provenances(source_expr, path, kind, guards, resource, None)
199 }
200
201 pub fn with_provenances(
203 source_expr: String,
204 path: YamlPath,
205 kind: ValueKind,
206 guards: Vec<Guard>,
207 resource: Option<ResourceRef>,
208 provenance: impl IntoIterator<Item = ContractProvenance>,
209 ) -> Self {
210 let condition = GuardDnf::from_guards(guards);
211 Self::with_condition_and_provenances(
212 source_expr,
213 path,
214 kind,
215 condition,
216 resource,
217 provenance,
218 )
219 }
220
221 pub fn with_condition_and_provenances(
223 source_expr: String,
224 path: YamlPath,
225 kind: ValueKind,
226 condition: GuardDnf,
227 resource: Option<ResourceRef>,
228 provenance: impl IntoIterator<Item = ContractProvenance>,
229 ) -> Self {
230 Self {
231 source_expr,
232 path,
233 kind,
234 condition,
235 resource,
236 provenance: provenance.into_iter().collect(),
237 stringified: false,
238 template_supplied_member_keys: std::collections::BTreeSet::new(),
239 split_segment: None,
240 merge_layers: None,
241 range_key: false,
242 nil_omitting: false,
243 omitted_members: std::collections::BTreeMap::new(),
244 digest: false,
245 merge_operand: false,
246 }
247 }
248
249 pub fn canonicalize(&mut self) {
251 self.provenance.sort();
252 self.provenance.dedup();
253 }
254
255 #[must_use]
257 pub fn single_guard_conjunction(&self) -> Vec<Guard> {
258 self.condition
259 .single_guard_conjunction()
260 .unwrap_or_default()
261 }
262
263 pub fn map_value_paths<F>(&mut self, map: &mut F)
265 where
266 F: FnMut(&str) -> String,
267 {
268 self.source_expr = map(&self.source_expr);
269 self.condition.map_value_paths(map);
270 if let Some(merge) = &mut self.merge_layers {
271 for layer in &mut merge.layers {
272 *layer = map(layer);
273 }
274 }
275 for retain_guards in self.omitted_members.values_mut() {
276 for guard in retain_guards {
277 *guard = guard.clone().map_value_paths(map);
278 }
279 }
280 }
281}