Skip to main content

template_model/
diagnostic.rs

1use crate::json_value::JsonValue;
2use serde::Serialize;
3use std::collections::HashMap;
4
5/// Advanced introspection view of a parsed CloudFormation template: its reference graph,
6/// conditions, and resolved resources and outputs.
7#[derive(Debug, Clone, Serialize)]
8#[cfg_attr(feature = "wasm-bindings", derive(tsify::Tsify))]
9#[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Record))]
10#[serde(rename_all = "camelCase")]
11pub struct DiagnosticModel {
12    pub template: DiagnosticTemplate,
13    #[cfg_attr(feature = "wasm-bindings", tsify(type = "Record<string, JsonValue>"))]
14    pub parameters: HashMap<String, JsonValue>,
15    #[cfg_attr(feature = "wasm-bindings", tsify(type = "Record<string, DiagnosticCondition>"))]
16    pub conditions: HashMap<String, DiagnosticCondition>,
17    /// Parameter logical IDs referenced by the template's condition expressions.
18    pub condition_param_refs: Vec<String>,
19    /// Logical implications between conditions: if the antecedent condition holds, the consequent condition must also hold.
20    pub condition_implications: Vec<DiagnosticImplication>,
21    /// Groups of conditions that are mutually exclusive because they test the same parameter against different values.
22    pub condition_mutex_groups: Vec<DiagnosticMutexGroup>,
23    /// Pairs of condition names that can never both be true at the same time.
24    pub condition_exclusions: Vec<Vec<String>>,
25    /// Maps each resource's logical ID to the name of the condition that gates whether it is created.
26    #[cfg_attr(feature = "wasm-bindings", tsify(type = "Record<string, string>"))]
27    pub resource_condition_map: HashMap<String, String>,
28    pub mappings: JsonValue,
29    #[cfg_attr(feature = "wasm-bindings", tsify(type = "Record<string, DiagnosticResource>"))]
30    pub resources: HashMap<String, DiagnosticResource>,
31    #[cfg_attr(feature = "wasm-bindings", tsify(type = "Record<string, DiagnosticOutput>"))]
32    pub outputs: HashMap<String, DiagnosticOutput>,
33    pub edges: Vec<ReferenceEdge>,
34    /// Reference cycles in the template, each listed as the resource logical IDs forming the cycle.
35    pub cycles: Vec<Vec<String>>,
36    /// Output names whose value is an Fn::Join with an empty delimiter.
37    pub output_empty_joins: Vec<String>,
38    /// Logical IDs of resources the SAM transform generates implicitly (such as function execution roles and an implicit REST API).
39    pub sam_implicit_resources: Vec<String>,
40    /// Parameter logical IDs referenced from the SAM Globals section.
41    pub globals_param_refs: Vec<String>,
42    /// True when the template appears to be generated by the AWS CDK.
43    pub is_cdk: bool,
44    /// Names of conditions referenced by Fn::If expressions in the template.
45    pub fn_if_conditions: Vec<String>,
46    /// Mapping names referenced by Fn::FindInMap expressions.
47    pub find_in_map_names: Vec<String>,
48    /// Parameter logical IDs referenced from within other parameters' own definitions.
49    pub params_referenced_in_definitions: Vec<String>,
50    /// True when a Fn::FindInMap uses a computed mapping name rather than a literal string.
51    pub has_dynamic_findinmap_name: bool,
52    /// True when the template could not be fully parsed and the model is therefore incomplete.
53    pub has_parse_errors: bool,
54    /// The template's Rules section, each with its optional condition and assertions.
55    pub parsed_rules: Vec<DiagnosticRule>,
56    /// For resolved resource properties, records where each value came from (such as a parameter default, allowed values, an override, or an intrinsic).
57    pub resolution_sources: Vec<ResolutionSource>,
58}
59
60/// Top-level template metadata.
61#[derive(Debug, Clone, Serialize)]
62#[cfg_attr(feature = "wasm-bindings", derive(tsify::Tsify))]
63#[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Record))]
64#[serde(rename_all = "camelCase")]
65pub struct DiagnosticTemplate {
66    #[cfg_attr(feature = "wasm-bindings", tsify(optional))]
67    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
68    pub format_version: Option<String>,
69    #[cfg_attr(feature = "wasm-bindings", tsify(optional))]
70    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
71    pub description: Option<String>,
72    pub transforms: Vec<String>,
73    /// The top-level section names present in the template as written.
74    pub raw_top_level_keys: Vec<String>,
75}
76
77/// A single Conditions entry with its expression and relationships to other conditions.
78#[derive(Debug, Clone, Serialize)]
79#[cfg_attr(feature = "wasm-bindings", derive(tsify::Tsify))]
80#[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Record))]
81#[serde(rename_all = "camelCase")]
82pub struct DiagnosticCondition {
83    /// The condition expression rendered as a readable string.
84    #[serde(skip_serializing_if = "Option::is_none")]
85    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
86    pub expression: Option<String>,
87    /// Names of other conditions this condition depends on.
88    #[serde(skip_serializing_if = "Option::is_none")]
89    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
90    pub deps: Option<Vec<String>>,
91    /// Names of conditions that are mutually exclusive with this one.
92    #[serde(skip_serializing_if = "Option::is_none")]
93    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
94    pub mutex_with: Option<Vec<String>>,
95}
96
97/// An implication between two conditions: whenever the antecedent holds, the consequent also holds.
98#[derive(Debug, Clone, Serialize)]
99#[cfg_attr(feature = "wasm-bindings", derive(tsify::Tsify))]
100#[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Record))]
101#[serde(rename_all = "camelCase")]
102pub struct DiagnosticImplication {
103    pub antecedent: String,
104    pub consequent: String,
105}
106
107/// A set of conditions that are mutually exclusive because they test the same parameter against different values.
108#[derive(Debug, Clone, Serialize)]
109#[cfg_attr(feature = "wasm-bindings", derive(tsify::Tsify))]
110#[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Record))]
111#[serde(rename_all = "camelCase")]
112pub struct DiagnosticMutexGroup {
113    pub conditions: Vec<String>,
114    /// The parameter all conditions in the group test.
115    pub parameter: String,
116    /// The distinct parameter values the conditions compare against, one per condition.
117    pub values: Vec<String>,
118}
119
120/// A single edge in the template's reference graph, from a referencing resource to its target.
121#[derive(Debug, Clone, Serialize)]
122#[cfg_attr(feature = "wasm-bindings", derive(tsify::Tsify))]
123#[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Record))]
124#[serde(rename_all = "camelCase")]
125pub struct ReferenceEdge {
126    /// Logical ID of the resource that makes the reference.
127    pub source: String,
128    /// Property path within the source where the reference occurs.
129    pub source_path: String,
130    /// Logical ID of the referenced resource or parameter.
131    pub target: String,
132    /// How the reference is made, such as Ref, GetAtt, Sub, or DependsOn.
133    pub kind: String,
134    /// For a GetAtt or Sub reference, the attribute or variable name being referenced.
135    #[serde(skip_serializing_if = "Option::is_none")]
136    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
137    pub attr: Option<String>,
138    /// Name of the condition under which this reference is active, if it is inside a conditional branch.
139    #[serde(skip_serializing_if = "Option::is_none")]
140    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
141    pub condition_context: Option<String>,
142}
143
144/// A reference a resource makes to another resource or parameter.
145#[derive(Debug, Clone, Serialize)]
146#[cfg_attr(feature = "wasm-bindings", derive(tsify::Tsify))]
147#[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Record))]
148#[serde(rename_all = "camelCase")]
149pub struct OutgoingRef {
150    /// Property path within the resource where the reference occurs.
151    pub source_path: String,
152    /// Logical ID of the referenced resource or parameter.
153    pub target: String,
154    /// How the reference is made, such as Ref, GetAtt, Sub, or DependsOn.
155    pub kind: String,
156    /// For a GetAtt or Sub reference, the attribute or variable name being referenced.
157    #[serde(skip_serializing_if = "Option::is_none")]
158    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
159    pub attr: Option<String>,
160    /// Name of the condition under which this reference is active, if it is inside a conditional branch.
161    #[serde(skip_serializing_if = "Option::is_none")]
162    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
163    pub condition_context: Option<String>,
164}
165
166/// A reference made to this resource by another resource.
167#[derive(Debug, Clone, Serialize)]
168#[cfg_attr(feature = "wasm-bindings", derive(tsify::Tsify))]
169#[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Record))]
170#[serde(rename_all = "camelCase")]
171pub struct IncomingRef {
172    /// Logical ID of the resource that makes the reference.
173    pub source: String,
174    /// Property path within the source where the reference occurs.
175    pub source_path: String,
176    /// How the reference is made, such as Ref, GetAtt, Sub, or DependsOn.
177    pub kind: String,
178    /// For a GetAtt or Sub reference, the attribute or variable name being referenced.
179    #[serde(skip_serializing_if = "Option::is_none")]
180    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
181    pub attr: Option<String>,
182}
183
184/// A resource after intrinsic resolution, with its properties, references, and detected findings.
185#[derive(Debug, Clone, Serialize)]
186#[cfg_attr(feature = "wasm-bindings", derive(tsify::Tsify))]
187#[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Record))]
188#[serde(rename_all = "camelCase")]
189pub struct DiagnosticResource {
190    pub resource_type: String,
191    /// Name of the condition gating whether this resource is created, if any.
192    #[cfg_attr(feature = "wasm-bindings", tsify(optional))]
193    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
194    pub condition: Option<String>,
195    pub depends_on: Vec<String>,
196    #[cfg_attr(feature = "wasm-bindings", tsify(optional))]
197    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
198    pub deletion_policy: Option<JsonValue>,
199    #[cfg_attr(feature = "wasm-bindings", tsify(optional))]
200    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
201    pub update_replace_policy: Option<JsonValue>,
202    #[cfg_attr(feature = "wasm-bindings", tsify(optional))]
203    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
204    pub creation_policy: Option<JsonValue>,
205    #[cfg_attr(feature = "wasm-bindings", tsify(optional))]
206    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
207    pub update_policy: Option<JsonValue>,
208    /// The resource's properties with intrinsics resolved where possible.
209    #[cfg_attr(feature = "wasm-bindings", tsify(type = "Record<string, JsonValue>"))]
210    pub properties: HashMap<String, JsonValue>,
211    pub outgoing_refs: Vec<OutgoingRef>,
212    pub incoming_refs: Vec<IncomingRef>,
213    /// Mapping names referenced by Fn::FindInMap within this resource.
214    pub find_in_map_refs: Vec<String>,
215    /// Fn::Sub uses whose template is a single variable with no surrounding text, which could be written as a plain reference.
216    pub simple_subs: Vec<PathVariable>,
217    /// Property paths where Fn::Sub has no variables to substitute.
218    pub redundant_subs: Vec<String>,
219    /// Property paths where Fn::Join uses an empty delimiter, concatenating its elements directly.
220    pub empty_joins: Vec<String>,
221    /// Property paths where an ARN hardcodes the aws partition instead of using AWS::Partition.
222    pub hardcoded_partition_arns: Vec<String>,
223    /// Properties that resolve to null in one branch of a condition.
224    pub conditionally_null_props: Vec<ConditionalNull>,
225    /// Names of conditions referenced within this resource's properties.
226    pub condition_refs: Vec<String>,
227    /// Fn::ForEach loops in this resource, each with its iteration variable and collection source.
228    pub for_each_expansions: Vec<DiagnosticForEachExpansion>,
229    /// Property paths containing ${...} text that is not an Fn::Sub variable and is left as a literal.
230    pub unsubstituted_variables: Vec<PathVariable>,
231    /// Fn::Sub map keys not referenced in the template string, each with its path and the unused key name.
232    pub unused_sub_keys: Vec<PathVariable>,
233    /// Property values that are a raw pseudo-parameter string instead of using Ref, each with its path and the pseudo-parameter name.
234    pub raw_pseudo_params: Vec<PathVariable>,
235    /// Property paths containing a {{resolve:secretsmanager:...}} dynamic reference.
236    pub secretsmanager_ref_paths: Vec<String>,
237    /// References whose target does not resolve to any resource or parameter, each with its path and unresolved target.
238    pub invalid_refs: Vec<PathTarget>,
239}
240
241/// A property path paired with a variable name referenced there.
242#[derive(Debug, Clone, Serialize)]
243#[cfg_attr(feature = "wasm-bindings", derive(tsify::Tsify))]
244#[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Record))]
245pub struct PathVariable {
246    pub path: String,
247    pub variable: String,
248}
249
250/// A property that resolves to null in one branch of a condition.
251#[derive(Debug, Clone, Serialize)]
252#[cfg_attr(feature = "wasm-bindings", derive(tsify::Tsify))]
253#[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Record))]
254#[serde(rename_all = "camelCase")]
255pub struct ConditionalNull {
256    pub path: String,
257    pub condition: String,
258    /// True if the property is null in the condition's true branch; false if null in the false branch.
259    pub null_in_true: bool,
260}
261
262/// An Fn::ForEach loop within a resource.
263#[derive(Debug, Clone, Serialize)]
264#[cfg_attr(feature = "wasm-bindings", derive(tsify::Tsify))]
265#[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Record))]
266pub struct DiagnosticForEachExpansion {
267    /// Property path where the Fn::ForEach occurs.
268    pub path: String,
269    /// The loop's iteration variable name.
270    pub identifier: String,
271    /// A description of the collection the loop iterates over, such as a reference to a parameter, an inline list, or a dynamic value.
272    pub collection: String,
273}
274
275/// A property path paired with the reference target found there.
276#[derive(Debug, Clone, Serialize)]
277#[cfg_attr(feature = "wasm-bindings", derive(tsify::Tsify))]
278#[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Record))]
279pub struct PathTarget {
280    pub path: String,
281    pub target: String,
282}
283
284/// An Fn::GetAtt reference to a resource attribute.
285#[derive(Debug, Clone, Serialize)]
286#[cfg_attr(feature = "wasm-bindings", derive(tsify::Tsify))]
287#[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Record))]
288#[serde(rename_all = "camelCase")]
289pub struct GetAttRef {
290    /// Logical ID of the resource whose attribute is referenced.
291    pub resource: String,
292    pub attribute: String,
293}
294
295/// A template output after intrinsic resolution, with its references.
296#[derive(Debug, Clone, Serialize)]
297#[cfg_attr(feature = "wasm-bindings", derive(tsify::Tsify))]
298#[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Record))]
299#[serde(rename_all = "camelCase")]
300pub struct DiagnosticOutput {
301    /// The output value with intrinsics resolved where possible.
302    pub value: JsonValue,
303    #[cfg_attr(feature = "wasm-bindings", tsify(optional))]
304    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
305    pub description: Option<String>,
306    /// Name of the condition gating whether this output is emitted, if any.
307    #[cfg_attr(feature = "wasm-bindings", tsify(optional))]
308    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
309    pub condition: Option<String>,
310    #[cfg_attr(feature = "wasm-bindings", tsify(optional))]
311    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
312    pub export_name: Option<JsonValue>,
313    /// Fn::GetAtt references appearing in the output value.
314    pub getatt_refs: Vec<GetAttRef>,
315    /// Names of conditions referenced within the output value.
316    pub condition_refs: Vec<String>,
317}
318
319/// An entry from the template's Rules section.
320#[derive(Debug, Clone, Serialize)]
321#[cfg_attr(feature = "wasm-bindings", derive(tsify::Tsify))]
322#[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Record))]
323#[serde(rename_all = "camelCase")]
324pub struct DiagnosticRule {
325    pub name: String,
326    /// The rule's RuleCondition expression, if present, that gates when the assertions apply.
327    #[cfg_attr(feature = "wasm-bindings", tsify(optional))]
328    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
329    pub condition: Option<JsonValue>,
330    pub assertions: Vec<DiagnosticRuleAssertion>,
331}
332
333/// A single assertion within a template rule.
334#[derive(Debug, Clone, Serialize)]
335#[cfg_attr(feature = "wasm-bindings", derive(tsify::Tsify))]
336#[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Record))]
337#[serde(rename_all = "camelCase")]
338pub struct DiagnosticRuleAssertion {
339    /// The assertion expression that must evaluate to true.
340    pub assert_expr: JsonValue,
341    #[serde(skip_serializing_if = "Option::is_none")]
342    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
343    pub assert_description: Option<String>,
344}
345
346/// Records where a resolved resource property value came from.
347#[derive(Debug, Clone, Serialize)]
348#[cfg_attr(feature = "wasm-bindings", derive(tsify::Tsify))]
349#[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Record))]
350#[serde(rename_all = "camelCase")]
351pub struct ResolutionSource {
352    pub resource_id: String,
353    pub property_path: String,
354    /// Origin of the resolved value, such as a parameter default, allowed values, an override, or an intrinsic.
355    pub source: String,
356}