diff --git a/src/config/db.rs b/src/config/db.rs deleted file mode 100644 index a650faa..0000000 --- a/src/config/db.rs +++ /dev/null @@ -1,8 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Deserialize, Serialize, Debug, Clone)] -pub struct DbConfig { - pub database_schema: Option, - pub max_connections: u32, - pub acquire_timeout: u64, -} diff --git a/src/config/mod.rs b/src/config/mod.rs deleted file mode 100644 index 05b784a..0000000 --- a/src/config/mod.rs +++ /dev/null @@ -1,86 +0,0 @@ -pub mod db; -pub mod output; -pub mod sea_orm_config; -pub mod template; - -use std::path::PathBuf; - -use db::DbConfig; -use output::{OutputCommentConfig, OutputConfig, OutputModelConfig}; -use sea_orm_config::{ - DateTimeCrate, EntityFormat, Prelude, SeaOrmConfig, SeaOrmEntityConfig, - SeaOrmExtraAttributesConfig, SeaOrmExtraDerivesConfig, SeaOrmSerdeConfig, SeaOrmTableConfig, - SerdeEnable, -}; -use serde::{Deserialize, Serialize}; -use serde_yaml::Mapping; - -#[derive(Deserialize, Serialize, Debug, Clone)] -pub struct Config { - pub db: DbConfig, - pub sea_orm: SeaOrmConfig, - pub output: OutputConfig, - pub templates: Option, - pub templates_dir: Option, -} - -impl Default for Config { - fn default() -> Self { - Self { - db: DbConfig { - database_schema: None, - max_connections: 10, - acquire_timeout: 5, - }, - sea_orm: SeaOrmConfig { - prelude: Prelude::Enabled, - serde: SeaOrmSerdeConfig { - enable: SerdeEnable::None, - skip_deserializing_primary_key: false, - skip_hidden_column: false, - }, - entity: SeaOrmEntityConfig { - format: EntityFormat::Compact, - tables: SeaOrmTableConfig { - include_hidden: false, - skip_seaql_migrations: true, - table_config: None, - }, - extra_derives: SeaOrmExtraDerivesConfig { - model: Vec::new(), - eenum: Vec::new(), - }, - extra_attributes: SeaOrmExtraAttributesConfig { - model: Vec::new(), - eenum: Vec::new(), - }, - date_time_crate: DateTimeCrate::Chrono, - with_copy_enums: false, - }, - }, - output: OutputConfig { - path: PathBuf::from("./src/"), - models: OutputModelConfig { - comment: OutputCommentConfig { - max_width: None, - table_name: true, - column_name: true, - column_db_type: true, - column_rust_type: true, - column_attributes: true, - column_exclude_attributes: Vec::new(), - enable: true, - column_info: true, - ignore_errors: false, - }, - enable: true, - entities: String::from("_entities"), - prelude: true, - path: PathBuf::from("./models"), - }, - }, - templates: None, - templates_dir: None, - } - } -} diff --git a/src/config/output.rs b/src/config/output.rs deleted file mode 100644 index eed6e04..0000000 --- a/src/config/output.rs +++ /dev/null @@ -1,32 +0,0 @@ -use std::path::PathBuf; - -use serde::{Deserialize, Serialize}; - -#[derive(Deserialize, Serialize, Debug, Clone)] -pub struct OutputConfig { - pub path: PathBuf, - pub models: OutputModelConfig, -} - -#[derive(Deserialize, Serialize, Debug, Clone)] -pub struct OutputModelConfig { - pub prelude: bool, - pub enable: bool, - pub path: PathBuf, - pub comment: OutputCommentConfig, - pub entities: String, -} - -#[derive(Deserialize, Serialize, Debug, Clone)] -pub struct OutputCommentConfig { - pub enable: bool, - pub max_width: Option, - pub table_name: bool, - pub column_info: bool, - pub column_name: bool, - pub column_db_type: bool, - pub column_rust_type: bool, - pub column_attributes: bool, - pub column_exclude_attributes: Vec, - pub ignore_errors: bool, -} diff --git a/src/config/sea_orm_config.rs b/src/config/sea_orm_config.rs deleted file mode 100644 index f2da53c..0000000 --- a/src/config/sea_orm_config.rs +++ /dev/null @@ -1,233 +0,0 @@ -use serde::{Deserialize, Deserializer, Serialize}; -use serde_yaml::Value; - -use sea_orm_codegen::{ - DateTimeCrate as CodegenDateTimeCrate, EntityWriterContext, WithPrelude, WithSerde, -}; - -use super::Config; -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "lowercase")] -pub enum EntityFormat { - Expanded, - Compact, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -#[serde(untagged)] -pub enum TableConfig { - Specific { specific: Vec }, - Exclude { exclude: Vec }, -} - -#[derive(Debug, Clone)] -pub enum SerdeEnable { - Both, - Serialize, - Deserialize, - None, -} - -#[derive(Debug, Clone)] -pub enum Prelude { - Enabled, - Disabled, - AllowUnusedImports, -} - -impl<'de> Deserialize<'de> for SerdeEnable { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let value = Value::deserialize(deserializer)?; - - match value { - Value::String(s) if s == "serialize" => Ok(SerdeEnable::Serialize), - Value::String(s) if s == "deserialize" => Ok(SerdeEnable::Deserialize), - Value::Bool(true) => Ok(SerdeEnable::Both), - Value::Bool(false) => Ok(SerdeEnable::None), - _ => Err(serde::de::Error::custom( - "expected 'serialize', 'deserialize', 'true' or 'false'", - )), - } - } -} -impl Serialize for SerdeEnable { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - match self { - SerdeEnable::Both => serializer.serialize_bool(true), - SerdeEnable::Serialize => serializer.serialize_str("serialize"), - SerdeEnable::Deserialize => serializer.serialize_str("deserialize"), - SerdeEnable::None => serializer.serialize_bool(false), - } - } -} -impl<'de> Deserialize<'de> for Prelude { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let value = Value::deserialize(deserializer)?; - - match value { - Value::Bool(true) => Ok(Prelude::Enabled), - Value::Bool(false) => Ok(Prelude::Disabled), - Value::String(s) if s == "allow_unused_imports" => Ok(Prelude::AllowUnusedImports), - _ => Err(serde::de::Error::custom( - "expected 'true', 'false', or 'allow_unused_imports'", - )), - } - } -} -impl Serialize for Prelude { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - match self { - Prelude::Enabled => serializer.serialize_bool(true), - Prelude::Disabled => serializer.serialize_bool(false), - Prelude::AllowUnusedImports => serializer.serialize_str("allow_unused_imports"), - } - } -} -#[derive(Deserialize, Serialize, Debug, Clone)] -pub struct SeaOrmConfig { - pub prelude: Prelude, - pub serde: SeaOrmSerdeConfig, - pub entity: SeaOrmEntityConfig, -} - -#[derive(Deserialize, Serialize, Debug, Clone)] -pub struct SeaOrmSerdeConfig { - pub enable: SerdeEnable, - pub skip_deserializing_primary_key: bool, - pub skip_hidden_column: bool, -} - -#[derive(Deserialize, Serialize, Debug, Clone)] -pub struct SeaOrmEntityConfig { - pub format: EntityFormat, - pub tables: SeaOrmTableConfig, - pub extra_derives: SeaOrmExtraDerivesConfig, - pub extra_attributes: SeaOrmExtraAttributesConfig, - pub date_time_crate: DateTimeCrate, - pub with_copy_enums: bool, -} - -#[derive(Deserialize, Serialize, Debug, Clone)] -pub struct SeaOrmTableConfig { - pub include_hidden: bool, - pub skip_seaql_migrations: bool, - #[serde(flatten)] - pub table_config: Option, -} -#[derive(Deserialize, Serialize, Debug, Clone)] -pub struct SeaOrmExtraDerivesConfig { - pub model: Vec, - #[serde(rename = "enum")] - pub eenum: Vec, -} -#[derive(Deserialize, Serialize, Debug, Clone)] -pub struct SeaOrmExtraAttributesConfig { - pub model: Vec, - #[serde(rename = "enum")] - pub eenum: Vec, -} -#[derive(Deserialize, Serialize, Debug, Clone)] -#[serde(rename_all = "lowercase")] -pub enum DateTimeCrate { - Time, - Chrono, -} - -impl From for CodegenDateTimeCrate { - fn from(date_time_crate: DateTimeCrate) -> CodegenDateTimeCrate { - match date_time_crate { - DateTimeCrate::Chrono => CodegenDateTimeCrate::Chrono, - DateTimeCrate::Time => CodegenDateTimeCrate::Time, - } - } -} - -impl SeaOrmTableConfig { - pub fn get_filter(&self) -> Box bool> { - let include_hidden = self.include_hidden; - if let Some(table) = &self.table_config { - match table { - TableConfig::Specific { specific } => { - let specific = specific.clone(); - Box::new(move |table: &String| { - (include_hidden || !table.starts_with('_')) && specific.contains(table) - }) - } - TableConfig::Exclude { exclude } => { - let exclude = exclude.clone(); - Box::new(move |table: &String| { - (include_hidden || !table.starts_with('_')) && !exclude.contains(table) - }) - } - } - } else if self.skip_seaql_migrations { - Box::new(move |table: &String| { - (include_hidden || !table.starts_with('_')) - && !table.starts_with("seaql_migrations") - }) - } else { - Box::new(move |table: &String| (include_hidden || !table.starts_with('_'))) - } - } -} - -impl EntityFormat { - pub fn is_expanded(&self) -> bool { - matches!(self, EntityFormat::Expanded) - } -} -impl From for WithPrelude { - fn from(val: Prelude) -> Self { - match val { - Prelude::Enabled => WithPrelude::All, - - Prelude::Disabled => WithPrelude::None, - Prelude::AllowUnusedImports => WithPrelude::AllAllowUnusedImports, - } - } -} -impl From for WithSerde { - fn from(val: SerdeEnable) -> Self { - match val { - SerdeEnable::Both => WithSerde::Both, - SerdeEnable::Serialize => WithSerde::Serialize, - SerdeEnable::Deserialize => WithSerde::Deserialize, - SerdeEnable::None => WithSerde::None, - } - } -} - -impl From for EntityWriterContext { - fn from(val: Config) -> Self { - EntityWriterContext::new( - val.sea_orm.entity.format.is_expanded(), - val.sea_orm.prelude.into(), - val.sea_orm.serde.enable.into(), - val.sea_orm.entity.with_copy_enums, - val.sea_orm.entity.date_time_crate.into(), - val.db.database_schema, - false, - val.sea_orm.serde.skip_deserializing_primary_key, - val.sea_orm.serde.skip_hidden_column, - val.sea_orm.entity.extra_derives.model, - val.sea_orm.entity.extra_attributes.model, - val.sea_orm.entity.extra_derives.eenum, - val.sea_orm.entity.extra_attributes.eenum, - false, - false, - ) - } -} diff --git a/src/config/template.rs b/src/config/template.rs deleted file mode 100644 index 8b13789..0000000 --- a/src/config/template.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/generator/modules/annotate/comment.rs b/src/generator/modules/annotate/comment.rs new file mode 100644 index 0000000..cbcede2 --- /dev/null +++ b/src/generator/modules/annotate/comment.rs @@ -0,0 +1,101 @@ +use color_eyre::Result; +use minijinja::Environment; + +use crate::generator::modules::{ + discovery::{db::DbType, table::Table}, + sea_orm::config::DateTimeCrate, +}; +use comfy_table::{Cell, ContentArrangement, Table as CTable}; + +use super::{AnnotateCommentConfig, COMMENTBODY, COMMENTHEAD, COMMENTTAIL}; +pub fn generate_comment( + table: &Table, + config: &AnnotateCommentConfig, + environment: &Environment<'static>, + db_type: &DbType, + date_time_crate: &DateTimeCrate, +) -> Result { + let mut column_info_table = CTable::new(); + let mut header = Vec::new(); + if config.column_name.unwrap() { + header.push("Name"); + } + if config.column_db_type.unwrap() { + header.push("DbType"); + } + if config.column_rust_type.unwrap() { + header.push("RsType"); + } + if config.column_attributes.unwrap() { + header.push("Attrs"); + } + column_info_table + .load_preset(" -+=++ + ++") + .set_content_arrangement(ContentArrangement::Dynamic) + .set_header(header); + if let Some(width) = config.max_wdith { + column_info_table.set_width(width); + } + for column in &table.columns { + let mut row = Vec::new(); + if config.column_name.unwrap() { + row.push(Cell::new(column.name.clone())) + } + if config.column_db_type.unwrap() { + let column_type = column.get_db_type(db_type); + row.push(Cell::new(column_type)); + } + if config.column_rust_type.unwrap() { + let column_type = column.get_rust_type(date_time_crate); + row.push(Cell::new(column_type)); + } + if config.column_attributes.unwrap() { + let attrs_string = column.attrs_to_string(); + row.push(Cell::new(attrs_string)); + } + column_info_table.add_row(row); + } + // column_info_table.to_string() + // let config_part = match parsed_settings { + // Some(settings) => { + // let settings_str = serde_yaml::to_string(&settings)?; + // let settings_str = settings_str + // .lines() + // .map(|line| format!(" {}", line)) + // .collect::>() + // .join("\n"); + // format!( + // "{SETTINGSDELIMITER}\n{}\n{SETTINGSDELIMITER}\n\n", + // settings_str + // ) + // } + // None => String::new(), + // }; + + // let table_name = &table.name; + // let table_name_str = if config.table_name { + // format!("Table: {}\n", table_name) + // } else { + // String::new() + // }; + // let string = format!("{HEADER}\n{config_part}{table_name_str}\n{column_info_table}"); + + // let padded_string = Self::pad_comment(&string); + Ok(String::new()) +} + +pub fn pad_comment(s: &str) -> String { + let parts = s.split('\n').collect::>(); + let mut padded = String::new(); + for (index, part) in parts.iter().enumerate() { + let first = index == 0; + let comment = match first { + true => COMMENTHEAD.to_string(), + false => COMMENTBODY.to_string(), + }; + let padded_part = format!("{} {}\n", comment, part); + padded.push_str(&padded_part); + } + padded.push_str(COMMENTTAIL); + padded +} diff --git a/src/generator/modules/annotate/mod.rs b/src/generator/modules/annotate/mod.rs index 6a261f7..70a5b32 100644 --- a/src/generator/modules/annotate/mod.rs +++ b/src/generator/modules/annotate/mod.rs @@ -1,21 +1,55 @@ -use super::{discovery::DiscoveredSchema, models::ModelsConfig, Module, ModulesContext}; +pub mod comment; + +use super::{ + discovery::DiscoveredSchema, models::ModelsConfig, sea_orm::SeaOrmConfig, Module, + ModulesContext, +}; use color_eyre::Result; use minijinja::Environment; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; const HEADER: &str = r#"== Schema Information"#; const COMMENTHEAD: &str = r#"/*"#; const COMMENTBODY: &str = r#" *"#; const COMMENTTAIL: &str = r#"*/"#; const SETTINGSDELIMITER: &str = r#"```"#; + #[derive(Debug, Clone, Deserialize)] #[serde(default)] +#[derive(Default)] pub struct AnnotateConfig { pub enable: bool, + pub comment: AnnotateCommentConfig, } -impl Default for AnnotateConfig { +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct AnnotateCommentConfig { + #[serde(skip_serializing_if = "Option::is_none")] + pub max_wdith: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub table_name: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub column_name: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub column_db_type: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub column_rust_type: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub column_attributes: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub column_exclude_attributes: Option>, +} + +impl Default for AnnotateCommentConfig { fn default() -> Self { - Self { enable: false } + Self { + max_wdith: Some(80), + table_name: Some(true), + column_name: Some(true), + column_db_type: Some(true), + column_rust_type: Some(true), + column_attributes: Some(true), + column_exclude_attributes: Some(vec![]), + } } } @@ -39,12 +73,35 @@ impl Module for AnnotateModule { } async fn execute(&mut self, ctx: &mut ModulesContext) -> Result<()> { let map = ctx.get_anymap(); - + let file_manager = ctx.get_file_manager(); if let (Some(config), Some(environment), Some(schema)) = ( map.get::(), map.get::>(), map.get::(), ) { + if let (Some(models_config), Some(sea_orm_config)) = + (map.get::(), map.get::()) + { + if models_config.enable { + let path = models_config.path.clone().unwrap(); + for table in &schema.tables { + let path = path.join(format!("{}.rs", table.name)); + let content = file_manager.get(&path); + if content.is_some() { + // generate default comment and insert + let comment = comment::generate_comment( + table, + &config.comment, + environment, + &schema.database_type, + &sea_orm_config.entity.date_time_crate, + ); + } else { + // file must already exist therefor read and process it + } + } + } + } Ok(()) } else { Ok(()) diff --git a/src/generator/modules/discovery/column.rs b/src/generator/modules/discovery/column.rs index f5c80b3..81e7daf 100644 --- a/src/generator/modules/discovery/column.rs +++ b/src/generator/modules/discovery/column.rs @@ -174,7 +174,7 @@ impl Column { } write_db_type(&self.col_type, db_type) } - pub fn get_rs_type(&self, date_time_crate: &DateTimeCrate) -> String { + pub fn get_rust_type(&self, date_time_crate: &DateTimeCrate) -> String { fn write_rs_type(col_type: &ColumnType, date_time_crate: &DateTimeCrate) -> String { #[allow(unreachable_patterns)] match col_type { diff --git a/src/generator/modules/mod.rs b/src/generator/modules/mod.rs index abd2347..9b6bcb8 100644 --- a/src/generator/modules/mod.rs +++ b/src/generator/modules/mod.rs @@ -83,7 +83,10 @@ impl ModulesContext { pub fn get_anymap_mut(&mut self) -> &mut AnyCloneMap { &mut self.anymap } - pub fn get_file_manager(&mut self) -> &mut FileManager { + pub fn get_file_manager(&self) -> &FileManager { + &self.file_manager + } + pub fn get_file_manager_mut(&mut self) -> &mut FileManager { &mut self.file_manager } } diff --git a/src/generator/modules/models/mod.rs b/src/generator/modules/models/mod.rs index 1d600c0..2684a6d 100644 --- a/src/generator/modules/models/mod.rs +++ b/src/generator/modules/models/mod.rs @@ -169,7 +169,7 @@ impl Module for ModelsModule { // One or both keys are missing } tracing::info!(?files, "Generated model files"); - let file_manager = ctx.get_file_manager(); + let file_manager = ctx.get_file_manager_mut(); for (output_path, content) in files { file_manager.insert(&output_path, &content, None)?; } diff --git a/src/generator/modules/sea_orm/mod.rs b/src/generator/modules/sea_orm/mod.rs index 1d76f1c..cf5993f 100644 --- a/src/generator/modules/sea_orm/mod.rs +++ b/src/generator/modules/sea_orm/mod.rs @@ -98,7 +98,7 @@ impl Module for SeaOrmModule { })); } - let file_manager = ctx.get_file_manager(); + let file_manager = ctx.get_file_manager_mut(); for (output_path, content) in outputs { file_manager.insert(&output_path, &content, None)?; }