siderite_cli/report/
build.rs1use super::{CheckSummary, Diagnostic, Envelope, Severity, redact_text};
5use super::{FieldReport, ModelReport, ModelsData};
6use super::{MigrationReport, MigrationStatusData, UrlSource};
7use super::{RouteReport, RoutesData};
8use crate::check::{CheckIssue, CheckLevel, check, check_build};
9use crate::error::CliError;
10use crate::settings::CliSettings;
11use siderite_core::App;
12use siderite_orm::ModelMeta;
13use std::path::Path;
14
15pub fn route_reports(app: &App) -> Result<Vec<RouteReport>, CliError> {
22 let document = app
23 .openapi()
24 .map_err(|err| CliError::OpenApi(err.to_string()))?;
25 let mut rows = Vec::new();
26 for (path, item) in &document.paths {
27 for (method, operation) in &item.0 {
28 let mut tags = operation.tags.clone();
29 tags.sort();
30 tags.dedup();
31 rows.push(RouteReport {
32 method: method.to_ascii_uppercase(),
33 path: path.clone(),
34 operation_id: operation.operation_id.clone(),
35 tags,
36 summary: operation.summary.as_deref().map(redact_text),
37 });
38 }
39 }
40 rows.sort_by(|a, b| (&a.path, &a.method).cmp(&(&b.path, &b.method)));
41 Ok(rows)
42}
43
44pub fn routes(app: &App) -> Result<Envelope<RoutesData>, CliError> {
49 let routes = route_reports(app)?;
50 Ok(Envelope::new("routes", RoutesData { routes }, Vec::new()))
51}
52
53pub fn model_reports(models: &[&'static ModelMeta]) -> Vec<ModelReport> {
56 let mut reports: Vec<ModelReport> = models
57 .iter()
58 .map(|model| {
59 let mut fields: Vec<FieldReport> = model
60 .fields
61 .iter()
62 .map(|f| FieldReport {
63 name: f.name.to_owned(),
64 column: f.column.to_owned(),
65 sql_type: format!("{:?}", f.sql_type),
66 nullable: f.nullable,
67 pk: f.primary_key,
68 })
69 .collect();
70 fields.sort_by_key(|f| (f.name.clone(), f.column.clone()));
71 ModelReport {
72 name: model.name.to_owned(),
73 table: model.table.to_owned(),
74 managed: model.managed,
75 fields,
76 }
77 })
78 .collect();
79 reports.sort_by(|a, b| (&a.name, &a.table).cmp(&(&b.name, &b.table)));
80 reports
81}
82
83pub fn models(models: &[&'static ModelMeta]) -> Envelope<ModelsData> {
85 let models = model_reports(models);
86 Envelope::new("models", ModelsData { models }, Vec::new())
87}
88
89pub fn diagnostic(issue: &CheckIssue) -> Diagnostic {
91 let severity = match issue.level {
92 CheckLevel::Warning => Severity::Warning,
93 CheckLevel::Error => Severity::Error,
94 };
95 Diagnostic::new(issue.id, severity, &issue.message)
96}
97
98pub fn check_report(
105 factory: &dyn Fn() -> App,
106 models: &[&'static ModelMeta],
107 settings: &CliSettings,
108 migrations_dir: Option<&Path>,
109) -> Envelope<CheckSummary> {
110 let mut issues = check(&factory(), models, settings, migrations_dir);
111 if !issues.iter().any(|i| i.id.starts_with("openapi.")) {
114 issues.extend(check_build(factory()));
115 }
116 let errors = issues
117 .iter()
118 .filter(|i| i.level == CheckLevel::Error)
119 .count();
120 let summary = CheckSummary {
121 errors,
122 warnings: issues.len() - errors,
123 };
124 let diagnostics = issues.iter().map(diagnostic).collect();
125 Envelope::new("check", summary, diagnostics)
126}
127
128pub fn migration_status(
131 alias: Option<&str>,
132 source: UrlSource,
133 rows: Vec<(String, bool)>,
134) -> Envelope<MigrationStatusData> {
135 let applied = rows.iter().filter(|(_, applied)| *applied).count();
136 let pending = rows.len() - applied;
137 let migrations = rows
138 .into_iter()
139 .map(|(id, applied)| MigrationReport { id, applied })
140 .collect();
141 let data = MigrationStatusData {
142 alias: alias.map(str::to_owned),
143 source,
144 migrations,
145 applied,
146 pending,
147 };
148 Envelope::new("showmigrations", data, Vec::new())
149}