1use std::fmt::Write;
2use std::marker::PhantomData;
3
4use cratestack_core::ModelEventKind;
5use cratestack_policy::ReadPolicy;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub struct ModelColumn {
9 pub rust_name: &'static str,
10 pub sql_name: &'static str,
11}
12
13#[derive(Debug, Clone, Copy)]
14pub struct ModelDescriptor<M, PK> {
15 pub schema_name: &'static str,
16 pub table_name: &'static str,
17 pub columns: &'static [ModelColumn],
18 pub primary_key: &'static str,
19 pub allowed_fields: &'static [&'static str],
20 pub allowed_includes: &'static [&'static str],
21 pub allowed_sorts: &'static [&'static str],
22 pub read_allow_policies: &'static [ReadPolicy],
23 pub read_deny_policies: &'static [ReadPolicy],
24 pub detail_allow_policies: &'static [ReadPolicy],
25 pub detail_deny_policies: &'static [ReadPolicy],
26 pub create_allow_policies: &'static [ReadPolicy],
27 pub create_deny_policies: &'static [ReadPolicy],
28 pub update_allow_policies: &'static [ReadPolicy],
29 pub update_deny_policies: &'static [ReadPolicy],
30 pub delete_allow_policies: &'static [ReadPolicy],
31 pub delete_deny_policies: &'static [ReadPolicy],
32 pub create_defaults: &'static [CreateDefault],
33 pub emitted_events: &'static [ModelEventKind],
34 pub version_column: Option<&'static str>,
38 pub audit_enabled: bool,
42 pub pii_columns: &'static [&'static str],
47 pub sensitive_columns: &'static [&'static str],
50 pub soft_delete_column: Option<&'static str>,
56 pub retention_days: Option<u32>,
61 _marker: PhantomData<fn() -> (M, PK)>,
62}
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
65pub enum CreateDefaultType {
66 Bool,
67 Int,
68 String,
69}
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq)]
72pub struct CreateDefault {
73 pub column: &'static str,
74 pub auth_field: &'static str,
75 pub ty: CreateDefaultType,
76 pub nullable: bool,
77}
78
79impl<M, PK> ModelDescriptor<M, PK> {
80 pub const fn new(
81 schema_name: &'static str,
82 table_name: &'static str,
83 columns: &'static [ModelColumn],
84 primary_key: &'static str,
85 allowed_fields: &'static [&'static str],
86 allowed_includes: &'static [&'static str],
87 allowed_sorts: &'static [&'static str],
88 read_allow_policies: &'static [ReadPolicy],
89 read_deny_policies: &'static [ReadPolicy],
90 detail_allow_policies: &'static [ReadPolicy],
91 detail_deny_policies: &'static [ReadPolicy],
92 create_allow_policies: &'static [ReadPolicy],
93 create_deny_policies: &'static [ReadPolicy],
94 update_allow_policies: &'static [ReadPolicy],
95 update_deny_policies: &'static [ReadPolicy],
96 delete_allow_policies: &'static [ReadPolicy],
97 delete_deny_policies: &'static [ReadPolicy],
98 create_defaults: &'static [CreateDefault],
99 emitted_events: &'static [ModelEventKind],
100 version_column: Option<&'static str>,
101 audit_enabled: bool,
102 pii_columns: &'static [&'static str],
103 sensitive_columns: &'static [&'static str],
104 soft_delete_column: Option<&'static str>,
105 retention_days: Option<u32>,
106 ) -> Self {
107 Self {
108 schema_name,
109 table_name,
110 columns,
111 primary_key,
112 allowed_fields,
113 allowed_includes,
114 allowed_sorts,
115 read_allow_policies,
116 read_deny_policies,
117 detail_allow_policies,
118 detail_deny_policies,
119 create_allow_policies,
120 create_deny_policies,
121 update_allow_policies,
122 update_deny_policies,
123 delete_allow_policies,
124 delete_deny_policies,
125 create_defaults,
126 emitted_events,
127 version_column,
128 audit_enabled,
129 pii_columns,
130 sensitive_columns,
131 soft_delete_column,
132 retention_days,
133 _marker: PhantomData,
134 }
135 }
136
137 pub fn emits(&self, operation: ModelEventKind) -> bool {
138 self.emitted_events.contains(&operation)
139 }
140
141 pub fn select_projection(&self) -> String {
142 let mut sql = String::new();
143 for (index, column) in self.columns.iter().enumerate() {
144 if index > 0 {
145 sql.push_str(", ");
146 }
147 let _ = write!(sql, "{} AS \"{}\"", column.sql_name, column.rust_name);
148 }
149 sql
150 }
151}