Skip to main content

diagnostics/
output.rs

1use crate::diagnostic::{
2    Entity, PerformanceMetrics, RelatedResource, ReportMetadata, ReportStatus, ViolationContext,
3    serialize_sorted_optional_map,
4};
5use crate::phase::Phase;
6use rules::{RuleOrigin, Severity};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9
10/// A single validation finding in the public report shape: the targeted entity is
11/// carried as a nested `entity` struct and the source location is flattened into
12/// individual line/column fields. The enrichment fields - `documentation_url`,
13/// `rule_description`, `phase`, and `context` - are carried only when a report is
14/// projected at the `DETAILED` detail level; the `STANDARD` detail level leaves them
15/// `None`, so they are omitted from serialization.
16#[derive(Debug, Clone, Serialize, Deserialize)]
17#[cfg_attr(feature = "wasm-bindings", derive(tsify::Tsify))]
18#[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Record))]
19#[serde(rename_all = "camelCase")]
20pub struct Diagnostic {
21    /// Identifier of the rule that produced this finding; its leading letter encodes the severity.
22    pub rule_id: String,
23    pub severity: Severity,
24    pub message: String,
25    /// Where the rule came from, such as a provider schema, the built-in engine, or a user-supplied rule.
26    pub source: RuleOrigin,
27    /// The named template entity this finding targets - a resource, parameter, output, mapping, condition, or template rule - if any.
28    #[serde(default, skip_serializing_if = "Option::is_none")]
29    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
30    pub entity: Option<Entity>,
31    /// Path to the offending property within the resource, such as 'Properties.Name'.
32    #[serde(default, skip_serializing_if = "Option::is_none")]
33    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
34    pub property_path: Option<String>,
35    #[serde(default, skip_serializing_if = "Option::is_none")]
36    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
37    pub suggested_fix: Option<String>,
38    #[serde(default, skip_serializing_if = "Option::is_none")]
39    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
40    pub category: Option<String>,
41    /// Line in the source template where the finding begins (1-based).
42    #[serde(default, skip_serializing_if = "Option::is_none")]
43    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
44    pub start_line: Option<u32>,
45    #[serde(default, skip_serializing_if = "Option::is_none")]
46    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
47    pub start_column: Option<u32>,
48    #[serde(default, skip_serializing_if = "Option::is_none")]
49    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
50    pub end_line: Option<u32>,
51    #[serde(default, skip_serializing_if = "Option::is_none")]
52    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
53    pub end_column: Option<u32>,
54    #[serde(default, skip_serializing_if = "Option::is_none")]
55    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
56    pub related_resources: Option<Vec<RelatedResource>>,
57    /// Condition name to boolean assignment under which this finding applies, when it depends on template conditions.
58    #[serde(default, skip_serializing_if = "Option::is_none", serialize_with = "serialize_sorted_optional_map")]
59    #[cfg_attr(feature = "wasm-bindings", tsify(type = "Record<string, boolean>"))]
60    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
61    pub condition_scenario: Option<HashMap<String, bool>>,
62    #[serde(default, skip_serializing_if = "Option::is_none")]
63    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
64    pub documentation_url: Option<String>,
65    #[serde(default, skip_serializing_if = "Option::is_none")]
66    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
67    pub rule_description: Option<String>,
68    #[serde(default, skip_serializing_if = "Option::is_none")]
69    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
70    pub phase: Option<Phase>,
71    #[serde(default, skip_serializing_if = "Option::is_none")]
72    #[cfg_attr(feature = "uniffi-bindings", uniffi(default))]
73    pub context: Option<ViolationContext>,
74}
75
76/// The serializable validation result: report metadata, performance metrics, and
77/// the flattened diagnostics. The per-diagnostic enrichment fields are present only
78/// when the report is projected at the `DETAILED` detail level.
79#[derive(Debug, Clone, Serialize)]
80#[cfg_attr(feature = "wasm-bindings", derive(tsify::Tsify))]
81#[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Record))]
82#[serde(rename_all = "camelCase")]
83pub struct ValidationReport {
84    pub file_path: String,
85    pub status: ReportStatus,
86    pub version: String,
87    pub metadata: ReportMetadata,
88    pub performance: PerformanceMetrics,
89    pub diagnostics: Vec<Diagnostic>,
90}