1use owo_colors::{OwoColorize, Style};
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6use std::fmt; macro_rules! write_field {
13 ($f:expr, $name:literal, $value:expr) => {
15 writeln!($f, " {:<20} : {:?}", $name, $value)
16 };
17 ($f:expr, $name:literal, $collection:expr, collection) => {
19 writeln!($f, " {:<20} : {} items", $name, $collection.len())
20 };
21}
22
23#[derive(Clone, Serialize, Deserialize, Default)]
30pub struct DatabaseMetadata {
31 pub schemas: HashMap<String, SchemaMetadata>,
32}
33
34impl fmt::Display for DatabaseMetadata {
35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36 write!(f, "Database with {} schemas", self.schemas.len())
37 }
38}
39
40impl fmt::Debug for DatabaseMetadata {
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 writeln!(f, "DatabaseMetadata ({} schemas):", self.schemas.len())?;
43 for (name, schema) in &self.schemas {
44 writeln!(f, "{:#?}", schema)?;
45 }
46 Ok(())
47 }
48}
49
50#[derive(Clone, Serialize, Deserialize, Default)]
51pub struct SchemaMetadata {
52 pub name: String,
53 pub tables: HashMap<String, TableMetadata>,
54 pub views: HashMap<String, ViewMetadata>,
55 pub enums: HashMap<String, EnumMetadata>,
56 pub functions: HashMap<String, FunctionMetadata>,
57}
58
59impl fmt::Display for SchemaMetadata {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 write!(
62 f,
63 "Schema '{}' [{} tables, {} views, {} enums]",
64 self.name,
65 self.tables.len(),
66 self.views.len(),
67 self.enums.len()
68 )
69 }
70}
71
72impl fmt::Debug for SchemaMetadata {
73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74 writeln!(f, "Schema '{}':", self.name)?;
75 write_field!(f, "Tables", self.tables, collection)?;
76 write_field!(f, "Views", self.views, collection)?;
77 write_field!(f, "Enums", self.enums, collection)?;
78 write_field!(f, "Functions", self.functions, collection)?;
79 Ok(())
80 }
81}
82
83#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
86pub enum AxionDataType {
87 Text,
88 Integer(i32),
89 Float(i32),
90 Numeric,
91 Boolean,
92 Timestamp,
93 TimestampTz,
94 Date,
95 Time,
96 Bytes,
97 Uuid,
98 Json,
99 JsonB,
100 Inet,
101 Enum(String),
102 Array(Box<AxionDataType>),
103 Unsupported(String),
104}
105
106impl fmt::Display for AxionDataType {
108 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109 match self {
110 Self::Text => write!(f, "TEXT"),
111 Self::Integer(bits) => write!(f, "INT{}", bits),
112 Self::Float(bits) => write!(f, "FLOAT{}", bits),
113 Self::Numeric => write!(f, "NUMERIC"),
114 Self::Boolean => write!(f, "BOOL"),
115 Self::Timestamp => write!(f, "TIMESTAMP"),
116 Self::TimestampTz => write!(f, "TIMESTAMPTZ"),
117 Self::Date => write!(f, "DATE"),
118 Self::Time => write!(f, "TIME"),
119 Self::Bytes => write!(f, "BYTES"),
120 Self::Uuid => write!(f, "UUID"),
121 Self::Json => write!(f, "JSON"),
122 Self::JsonB => write!(f, "JSONB"),
123 Self::Inet => write!(f, "INET"),
124 Self::Enum(name) => write!(f, "{}", name),
125 Self::Array(inner) => write!(f, "{}[]", inner),
126 Self::Unsupported(name) => write!(f, "UNSUPPORTED({})", name),
127 }
128 }
129}
130impl fmt::Debug for AxionDataType {
132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133 match self {
134 Self::Enum(name) => f.debug_tuple("Enum").field(name).finish(),
135 Self::Array(inner) => f.debug_tuple("Array").field(inner).finish(),
136 Self::Unsupported(name) => f.debug_tuple("Unsupported").field(name).finish(),
137 _ => write!(f, "{}", self), }
139 }
140}
141
142#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
143pub struct ForeignKeyReference {
144 pub schema: String,
145 pub table: String,
146 pub column: String,
147}
148impl fmt::Display for ForeignKeyReference {
150 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151 write!(f, "{}.{}.{}", self.schema, self.table, self.column)
152 }
153}
154impl fmt::Debug for ForeignKeyReference {
155 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156 f.debug_struct("ForeignKey")
158 .field("schema", &self.schema)
159 .field("table", &self.table)
160 .field("column", &self.column)
161 .finish()
162 }
163}
164
165#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
168pub struct ColumnMetadata {
169 pub name: String,
170 pub sql_type_name: String,
171 pub axion_type: AxionDataType,
172 pub is_nullable: bool,
173 pub is_primary_key: bool,
174 pub default_value: Option<String>,
175 pub comment: Option<String>,
176 pub foreign_key: Option<ForeignKeyReference>,
177}
178impl fmt::Display for ColumnMetadata {
182 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183 let pk_style = Style::new().green().bold();
185 let fk_style = Style::new().cyan();
186 let enum_style = Style::new().yellow();
187 let type_style = Style::new().magenta();
188 let nullable_style = Style::new().red().bold();
189
190 let nullable_marker: Box<dyn fmt::Display> = if self.is_nullable {
194 Box::new(" ") } else {
196 Box::new("*".style(nullable_style))
197 };
198 write!(f, " {} {:<25}", nullable_marker, self.name.bold())?;
200
201 write!(f, "{:<20}", self.sql_type_name.dimmed())?;
203
204 let binding = self.axion_type.to_string();
206 let axion_type_display: Box<dyn fmt::Display> = match &self.axion_type {
207 AxionDataType::Enum(name) => Box::new(name.style(enum_style)),
208 _ => Box::new(binding.style(type_style)),
209 };
210
211 write!(f, "{:<20}", axion_type_display)?;
212
213 let mut constraints = Vec::new();
215 if self.is_primary_key {
216 constraints.push(format!("{}", "PK".style(pk_style)));
217 }
218 if let Some(fk) = &self.foreign_key {
219 constraints.push(format!("{} -> {}", "FK".style(fk_style), fk));
220 }
221 write!(f, "{}", constraints.join(" "))?;
222
223 Ok(())
224 }
225}
226impl fmt::Debug for ColumnMetadata {
227 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
228 writeln!(f, "Column '{}':", self.name)?;
229 write_field!(f, "SQL Type", &self.sql_type_name)?;
230 write_field!(f, "Axion Type", &self.axion_type)?;
231 write_field!(f, "Nullable", &self.is_nullable)?;
232 write_field!(f, "Primary Key", &self.is_primary_key)?;
233 write_field!(f, "Default", &self.default_value)?;
234 write_field!(f, "Foreign Key", &self.foreign_key)?;
235 write_field!(f, "Comment", &self.comment)
236 }
237}
238
239#[derive(Clone, Serialize, Deserialize, Default)]
240pub struct TableMetadata {
241 pub name: String,
242 pub schema: String,
243 pub columns: Vec<ColumnMetadata>,
244 pub primary_key_columns: Vec<String>,
245 pub comment: Option<String>,
246}
247impl fmt::Display for TableMetadata {
248 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249 writeln!(
251 f,
252 "{}",
253 format!("{}.{}", self.schema, self.name)
254 .bright_blue()
255 .bold()
256 )?;
257
258 for col in &self.columns {
260 writeln!(f, "{}", col)?;
261 }
262 Ok(())
263 }
264}
265impl fmt::Debug for TableMetadata {
266 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
267 writeln!(f, "Table '{}.{}':", self.schema, self.name)?;
268 write_field!(f, "Primary Keys", &self.primary_key_columns)?;
269 write_field!(f, "Comment", &self.comment)?;
270 writeln!(f, " Columns ({}):", self.columns.len())?;
271 for col in &self.columns {
272 writeln!(f, "{:#?}", col)?;
273 }
274 Ok(())
275 }
276}
277
278#[derive(Clone, Serialize, Deserialize, Default)]
279pub struct ViewMetadata {
280 pub name: String,
281 pub schema: String,
282 pub columns: Vec<ColumnMetadata>,
283 pub definition: Option<String>,
284 pub comment: Option<String>,
285}
286impl fmt::Display for ViewMetadata {
288 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
289 writeln!(
291 f,
292 "{}",
293 format!("{}.{}", self.schema, self.name)
294 .bright_green()
295 .bold()
296 )?;
297
298 for col in &self.columns {
299 writeln!(f, "{}", col)?;
300 }
301 Ok(())
302 }
303}
304impl fmt::Debug for ViewMetadata {
305 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
306 writeln!(f, "View '{}.{}':", self.schema, self.name)?;
307 write_field!(f, "Comment", &self.comment)?;
308 if let Some(def) = &self.definition {
309 writeln!(
310 f,
311 " Definition : {}...",
312 &def.chars().take(50).collect::<String>()
313 )?;
314 }
315 writeln!(f, " Columns ({}):", self.columns.len())?;
316 for col in &self.columns {
317 writeln!(f, "{:#?}", col)?;
318 }
319 Ok(())
320 }
321}
322
323#[derive(Clone, Serialize, Deserialize, Default)]
324pub struct EnumMetadata {
325 pub name: String,
326 pub schema: String,
327 pub values: Vec<String>,
328 pub comment: Option<String>,
329}
330impl fmt::Display for EnumMetadata {
331 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
332 write!(
333 f,
334 "{}.{} ({})",
335 self.schema,
336 self.name,
337 self.values.join(", ")
338 )
339 }
340}
341impl fmt::Debug for EnumMetadata {
342 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
343 writeln!(f, "Enum '{}.{}':", self.schema, self.name)?;
344 write_field!(f, "Values", &self.values)?;
345 write_field!(f, "Comment", &self.comment)
346 }
347}
348
349#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
353pub enum RoutineKind {
354 Function,
355 Procedure,
356 Aggregate,
357 Window,
358 Trigger,
359}
360
361#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
362pub enum ParameterMode {
363 In,
364 Out,
365 InOut,
366 Variadic,
367}
368
369#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
370pub struct ParameterMetadata {
371 pub name: String,
372 pub sql_type_name: String,
373 pub axion_type: AxionDataType,
374 pub mode: ParameterMode,
375 pub has_default: bool,
376}
377
378#[derive(Debug, Clone, Serialize, Deserialize, Default)]
379pub struct FunctionMetadata {
380 pub name: String,
381 pub schema: String,
382 pub kind: Option<RoutineKind>,
383 pub parameters: Vec<ParameterMetadata>,
384 pub return_type: Option<AxionDataType>,
385 pub return_table: Option<Vec<ColumnMetadata>>,
386 pub comment: Option<String>,
387}