Skip to main content

safe_migrate/_internal/report/
violations.rs

1#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
2pub enum OperationKind {
3    DropColumn,
4    DropTable,
5    DropIndex,
6    DropView,
7    DropMaterializedView,
8    DropFunction,
9    DropProcedure,
10    DropSchema,
11    DropDatabase,
12    DropSequence,
13    DropDomain,
14    DropType,
15    DropPublication,
16    DropTrigger,
17    DropPolicy,
18    AddColumn,
19    AlterColumnType,
20    AddConstraint,
21    CreateIndex,
22    CreateTable,
23    CreateView,
24    CreateFunction,
25    CreateProcedure,
26    AlterFunction,
27    AlterProcedure,
28    RefreshMaterializedView,
29    AttachPartition,
30    DetachPartition,
31    VacuumFull,
32    Grant,
33    RevokeGrant,
34    AlterType,
35    CreateTrigger,
36    CreatePolicy,
37    DisableTrigger,
38    EnableTrigger,
39    RenameTable,
40    RenameColumn,
41    Rename,
42    OpaqueSql,
43    CreateSchema,
44    SetDefault,
45    CreateSequence,
46    CreateDomain,
47    AlterSchema,
48    Conflict,
49    Irreversible,
50    UnresolvedReference,
51    Other(String),
52}
53
54impl std::fmt::Display for OperationKind {
55    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56        match self {
57            OperationKind::DropColumn => write!(f, "drop_column"),
58            OperationKind::DropTable => write!(f, "drop_table"),
59            OperationKind::DropIndex => write!(f, "drop_index"),
60            OperationKind::DropView => write!(f, "drop_view"),
61            OperationKind::DropMaterializedView => write!(f, "drop_materialized_view"),
62            OperationKind::DropFunction => write!(f, "drop_function"),
63            OperationKind::DropProcedure => write!(f, "drop_procedure"),
64            OperationKind::DropSchema => write!(f, "drop_schema"),
65            OperationKind::DropDatabase => write!(f, "drop_database"),
66            OperationKind::DropSequence => write!(f, "drop_sequence"),
67            OperationKind::DropDomain => write!(f, "drop_domain"),
68            OperationKind::DropType => write!(f, "drop_type"),
69            OperationKind::DropPublication => write!(f, "drop_publication"),
70            OperationKind::DropTrigger => write!(f, "drop_trigger"),
71            OperationKind::DropPolicy => write!(f, "drop_policy"),
72            OperationKind::AddColumn => write!(f, "add_column"),
73            OperationKind::AlterColumnType => write!(f, "alter_column_type"),
74            OperationKind::AddConstraint => write!(f, "add_constraint"),
75            OperationKind::CreateIndex => write!(f, "create_index"),
76            OperationKind::CreateTable => write!(f, "create_table"),
77            OperationKind::CreateView => write!(f, "create_view"),
78            OperationKind::CreateFunction => write!(f, "create_function"),
79            OperationKind::CreateProcedure => write!(f, "create_procedure"),
80            OperationKind::AlterFunction => write!(f, "alter_function"),
81            OperationKind::AlterProcedure => write!(f, "alter_procedure"),
82            OperationKind::RefreshMaterializedView => write!(f, "refresh_materialized_view"),
83            OperationKind::AttachPartition => write!(f, "attach_partition"),
84            OperationKind::DetachPartition => write!(f, "detach_partition"),
85            OperationKind::VacuumFull => write!(f, "vacuum_full"),
86            OperationKind::Grant => write!(f, "grant"),
87            OperationKind::RevokeGrant => write!(f, "revoke_grant"),
88            OperationKind::AlterType => write!(f, "alter_type"),
89            OperationKind::CreateTrigger => write!(f, "create_trigger"),
90            OperationKind::CreatePolicy => write!(f, "create_policy"),
91            OperationKind::DisableTrigger => write!(f, "disable_trigger"),
92            OperationKind::EnableTrigger => write!(f, "enable_trigger"),
93            OperationKind::RenameTable => write!(f, "rename_table"),
94            OperationKind::RenameColumn => write!(f, "rename_column"),
95            OperationKind::Rename => write!(f, "rename"),
96            OperationKind::OpaqueSql => write!(f, "opaque_sql"),
97            OperationKind::CreateSchema => write!(f, "create_schema"),
98            OperationKind::SetDefault => write!(f, "set_default"),
99            OperationKind::CreateSequence => write!(f, "create_sequence"),
100            OperationKind::CreateDomain => write!(f, "create_domain"),
101            OperationKind::AlterSchema => write!(f, "alter_schema"),
102            OperationKind::Conflict => write!(f, "conflict"),
103            OperationKind::Irreversible => write!(f, "irreversible"),
104            OperationKind::UnresolvedReference => write!(f, "unresolved_reference"),
105            OperationKind::Other(s) => write!(f, "{}", s),
106        }
107    }
108}
109
110#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
111pub enum ObjectKind {
112    Table,
113    Index,
114    View,
115    MaterializedView,
116    Function,
117    Procedure,
118    Trigger,
119    Sequence,
120    Schema,
121    Role,
122    Publication,
123    Subscription,
124    Database,
125    Domain,
126    Policy,
127    Type,
128    Opaque,
129    Unknown,
130}
131
132impl std::fmt::Display for ObjectKind {
133    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
134        match self {
135            ObjectKind::Table => write!(f, "table"),
136            ObjectKind::Index => write!(f, "index"),
137            ObjectKind::View => write!(f, "view"),
138            ObjectKind::MaterializedView => write!(f, "materialized view"),
139            ObjectKind::Function => write!(f, "function"),
140            ObjectKind::Procedure => write!(f, "procedure"),
141            ObjectKind::Trigger => write!(f, "trigger"),
142            ObjectKind::Sequence => write!(f, "sequence"),
143            ObjectKind::Schema => write!(f, "schema"),
144            ObjectKind::Role => write!(f, "role"),
145            ObjectKind::Publication => write!(f, "publication"),
146            ObjectKind::Subscription => write!(f, "subscription"),
147            ObjectKind::Database => write!(f, "database"),
148            ObjectKind::Domain => write!(f, "domain"),
149            ObjectKind::Policy => write!(f, "policy"),
150            ObjectKind::Type => write!(f, "type"),
151            ObjectKind::Opaque => write!(f, "opaque"),
152            ObjectKind::Unknown => write!(f, "object"),
153        }
154    }
155}
156
157/// ViolationTier represents the severity of a finding.
158/// Tier1 is declared first so `derive(Ord)` sorts it before Tier2 and Tier3.
159#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, serde::Serialize)]
160pub enum ViolationTier {
161    Tier1, // HALT — Access Exclusive / data-destructive, sorts first
162    Tier2, // WARN — Share Row Exclusive / cautious
163    Tier3, // SAFE — informational / low risk, sorts last
164}
165
166#[derive(Debug, Clone, serde::Serialize)]
167pub struct Violation {
168    #[serde(skip)]
169    pub source_range: Option<rowan::TextRange>,
170    pub rule_id: &'static str,
171    pub operation_kind: OperationKind,
172    pub object_kind: ObjectKind,
173    pub object_name: String,
174    pub tier: ViolationTier,
175    pub reason: String,
176    pub recipe: &'static str,
177    pub dedup_key: Option<String>,
178    pub sql: Option<String>,
179    #[serde(default)]
180    pub fk_dependency_related: bool,
181}
182
183/// Stable source location attached at reporting time. Rules remain independent
184/// of file layout; the engine derives this from the parsed statement range.
185#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
186pub struct SourceLocation {
187    pub file: String,
188    pub line: usize,
189    pub column: usize,
190}
191
192/// A violation paired with the file and line that produced it. The flattened
193/// serialization keeps the JSON violation schema additive.
194#[derive(Debug, Clone, serde::Serialize)]
195pub struct ReportFinding {
196    #[serde(flatten)]
197    pub violation: Violation,
198    #[serde(skip_serializing_if = "Option::is_none")]
199    pub location: Option<SourceLocation>,
200    /// One-based statement position within the source file.
201    #[serde(skip_serializing_if = "Option::is_none")]
202    pub statement_index: Option<usize>,
203}