From 68d77a23e4f575240cbabfffe3b77a0461be3657 Mon Sep 17 00:00:00 2001 From: Nikkuss Date: Sat, 12 Apr 2025 14:06:12 +0400 Subject: [PATCH] replace handlebars with jinja --- Cargo.lock | 139 +++--------------- Cargo.toml | 2 +- process-compose.nix | 2 +- src/generator/mod.rs | 1 - src/generator/modules/discovery/column.rs | 5 +- src/generator/modules/discovery/table.rs | 3 +- src/generator/modules/models/mod.rs | 112 ++++++++------ src/generator/modules/templates/mod.rs | 39 ++--- src/main.rs | 1 - src/templates.rs | 1 - templates/{model.hbs => model.hbs.old} | 0 templates/model.jinja | 1 + templates/model_mod.jinja | 9 ++ ...odel_prelude.hbs => model_prelude.hbs.old} | 0 templates/model_prelude.jinja | 4 + ...de_part.hbs => model_prelude_part.hbs.old} | 2 + tests/src/models/mod.rs | 9 +- tests/src/models/prelude.rs | 4 +- tests/src/models/user.rs | 10 +- 19 files changed, 144 insertions(+), 200 deletions(-) rename templates/{model.hbs => model.hbs.old} (100%) create mode 100644 templates/model.jinja create mode 100644 templates/model_mod.jinja rename templates/{model_prelude.hbs => model_prelude.hbs.old} (100%) create mode 100644 templates/model_prelude.jinja rename templates/{model_prelude_part.hbs => model_prelude_part.hbs.old} (83%) diff --git a/Cargo.lock b/Cargo.lock index 1c0ec74..ecee75b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -413,7 +413,6 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", "syn", ] @@ -439,37 +438,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "derive_builder" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" -dependencies = [ - "derive_builder_macro", -] - -[[package]] -name = "derive_builder_core" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "derive_builder_macro" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" -dependencies = [ - "derive_builder_core", - "syn", -] - [[package]] name = "detect-lang" version = "0.1.5" @@ -755,22 +723,6 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" -[[package]] -name = "handlebars" -version = "6.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759e2d5aea3287cb1190c8ec394f42866cb5bf74fcbf213f354e3c856ea26098" -dependencies = [ - "derive_builder", - "log", - "num-order", - "pest", - "pest_derive", - "serde", - "serde_json", - "thiserror", -] - [[package]] name = "hashbrown" version = "0.15.2" @@ -1170,6 +1122,23 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memo-map" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d1115007560874e373613744c6fba374c17688327a71c1476d1a5954cc857b" + +[[package]] +name = "minijinja" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98642a6dfca91122779a307b77cd07a4aa951fbe32232aaf5bad9febc66be754" +dependencies = [ + "memo-map", + "self_cell", + "serde", +] + [[package]] name = "miniz_oxide" version = "0.7.4" @@ -1258,21 +1227,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-modular" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17bb261bf36fa7d83f4c294f834e91256769097b3cb505d44831e0a179ac647f" - -[[package]] -name = "num-order" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "537b596b97c40fcf8056d153049eb22f481c17ebce72a513ec9286e4986d1bb6" -dependencies = [ - "num-modular", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -1372,51 +1326,6 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" -[[package]] -name = "pest" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" -dependencies = [ - "memchr", - "thiserror", - "ucd-trie", -] - -[[package]] -name = "pest_derive" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "pest_meta" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0" -dependencies = [ - "once_cell", - "pest", - "sha2", -] - [[package]] name = "pin-project-lite" version = "0.2.16" @@ -1653,11 +1562,11 @@ dependencies = [ "color-eyre", "comfy-table", "comment-parser", - "handlebars", "heck 0.5.0", "include_dir", "indicatif", "inquire", + "minijinja", "path-clean", "pathdiff", "quote", @@ -1734,6 +1643,12 @@ dependencies = [ "syn", ] +[[package]] +name = "self_cell" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2fdfc24bc566f839a2da4c4295b82db7d25a24253867d5c64355abb5799bdbe" + [[package]] name = "serde" version = "1.0.219" @@ -2380,12 +2295,6 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" -[[package]] -name = "ucd-trie" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" - [[package]] name = "unicode-bidi" version = "0.3.18" diff --git a/Cargo.toml b/Cargo.toml index 10d6168..8c80df7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,11 +10,11 @@ clap = { version = "4.5.32", features = ["derive", "env"] } color-eyre = "0.6.3" comfy-table = { version = "7.1.4", default-features = false } comment-parser = "0.1.0" -handlebars = "6.3.2" heck = "0.5.0" include_dir = "0.7.4" indicatif = "0.17.11" inquire = "0.7.5" +minijinja = { version = "2.9.0", features = ["loader"] } path-clean = "1.0.1" pathdiff = "0.2.3" quote = "1.0.40" diff --git a/process-compose.nix b/process-compose.nix index 4fa5bea..188054d 100644 --- a/process-compose.nix +++ b/process-compose.nix @@ -3,7 +3,7 @@ processes = { frontend = { command = '' - RUST_LOG=debug,sqlx=warn ${pkgs.cargo-watch}/bin/cargo-watch -i tests/src -x 'run' + RUST_LOG=debug,sqlx=warn ${pkgs.cargo-watch}/bin/cargo-watch -i tests/src -x 'run && cat tests/src/models/user.rs' ''; }; }; diff --git a/src/generator/mod.rs b/src/generator/mod.rs index 889e5bb..7093024 100644 --- a/src/generator/mod.rs +++ b/src/generator/mod.rs @@ -1,7 +1,6 @@ pub mod file; pub mod modules; use color_eyre::Result; -use handlebars::Handlebars; use toml_edit::DocumentMut; #[derive(Clone, Debug)] pub struct DatabaseUrl(String); diff --git a/src/generator/modules/discovery/column.rs b/src/generator/modules/discovery/column.rs index 0d51cbc..f5c80b3 100644 --- a/src/generator/modules/discovery/column.rs +++ b/src/generator/modules/discovery/column.rs @@ -1,14 +1,17 @@ use color_eyre::{eyre::ContextCompat, Result}; use heck::ToUpperCamelCase; use sea_schema::sea_query::{ColumnDef, ColumnSpec, ColumnType, IndexCreateStatement}; +use serde::{Deserialize, Serialize}; use crate::generator::modules::sea_orm::config::DateTimeCrate; use super::db::DbType; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize)] pub struct Column { pub name: String, + #[serde(skip_serializing)] pub col_type: ColumnType, + #[serde(skip_serializing)] pub attrs: Vec, } diff --git a/src/generator/modules/discovery/table.rs b/src/generator/modules/discovery/table.rs index ed8e381..5988a79 100644 --- a/src/generator/modules/discovery/table.rs +++ b/src/generator/modules/discovery/table.rs @@ -1,8 +1,9 @@ use super::column::Column; use color_eyre::{eyre::eyre, Result}; use sea_schema::sea_query::{self, TableCreateStatement}; +use serde::{Deserialize, Serialize}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize)] pub struct Table { pub name: String, pub columns: Vec, diff --git a/src/generator/modules/models/mod.rs b/src/generator/modules/models/mod.rs index 18f085e..1d600c0 100644 --- a/src/generator/modules/models/mod.rs +++ b/src/generator/modules/models/mod.rs @@ -3,17 +3,19 @@ use std::path::PathBuf; use crate::generator::file::pathbuf_to_rust_path; use super::{ - discovery::DiscoveredSchema, sea_orm::SeaOrmConfig, templates::TemplateConfig, Module, - ModulesContext, + discovery::{table::Table, DiscoveredSchema}, + sea_orm::SeaOrmConfig, + templates::TemplateConfig, + Module, ModulesContext, }; use color_eyre::{ eyre::{eyre, Context, ContextCompat}, Result, }; -use handlebars::Handlebars; use heck::ToPascalCase; +use minijinja::Environment; use serde::{Deserialize, Serialize}; -#[derive(Debug, Clone, Deserialize)] +#[derive(Debug, Clone, Deserialize, Serialize)] #[serde(default)] pub struct ModelsConfig { pub enable: bool, @@ -33,27 +35,27 @@ impl Default for ModelsConfig { #[derive(Debug, Clone, Serialize)] pub struct ModelTemplateContext { - entities_path: String, - model_path: String, - model_name: String, - entity_name: String, - active_model_name: String, - prelude_path: String, + entities_path: Option, + tables: Option>, + prelude_path: Option, + table_name: Option, + config: ModelsConfig, } impl ModelTemplateContext { - pub fn new(entities_path: String, model_name: String, prelude_path: String) -> Self { - let model_path = model_name.clone(); - let active_model_name = format!("{}ActiveModel", model_name).to_pascal_case(); - let model_name = format!("{}Model", model_name).to_pascal_case(); - let entity_name = model_path.clone().to_pascal_case(); + pub fn new( + entities_path: Option, + prelude_path: Option, + tables: Option>, + table_name: Option, + config: ModelsConfig, + ) -> Self { Self { entities_path, - model_path, - model_name, - active_model_name, + tables, prelude_path, - entity_name, + table_name, + config, } } } @@ -103,7 +105,7 @@ impl Module for ModelsModule { if let (Some(config), Some(templates), Some(sea_orm_config), Some(schema)) = ( map.get::(), - map.get::>(), + map.get::>(), map.get::(), map.get::(), ) { @@ -111,45 +113,57 @@ impl Module for ModelsModule { tracing::info!(?models_path, "Models path"); let entities_path = sea_orm_config.path.clone().unwrap(); let mod_path = models_path.join("mod.rs"); + + let relative_entities_path = pathdiff::diff_paths(&entities_path, &mod_path) + .context("Failed to calculate relative path")?; + let relative_entities_rust_path = pathbuf_to_rust_path(relative_entities_path); + let context = ModelTemplateContext::new( + Some(relative_entities_rust_path.clone()), + None, + Some(schema.tables.clone()), + None, + config.clone(), + ); if config.prelude { - files.push((mod_path.clone(), "pub mod prelude;".to_string())); + let prelude = templates + .get_template("model_prelude")? + .render(&context) + .context("Failed to render model prelude part")?; + files.push((models_path.join("prelude.rs"), prelude)); } + + let mod_content = templates + .get_template("model_mod")? + .render(&context) + .context("Failed to render model mod")?; + files.push((mod_path.clone(), mod_content)); for table in &schema.tables { tracing::debug!(?table, "Generating model for table"); let path = models_path.join(format!("{}.rs", table.name)); - let relative_entities_path = pathdiff::diff_paths(&entities_path, &path) - .context("Failed to calculate relative path")?; - let relative_entities_rust_path = pathbuf_to_rust_path(relative_entities_path); let context = ModelTemplateContext::new( - relative_entities_rust_path.clone(), - table.name.clone(), - "super::prelude".to_string(), + Some(relative_entities_rust_path.clone()), + if config.prelude { + Some("super::prelude".to_string()) + } else { + None + }, + None, + Some(table.name.clone()), + config.clone(), ); - if config.prelude { - let prelude_part = templates - .render("model_prelude_part", &context) - .context("Failed to render model prelude part")?; - files.push((models_path.join("prelude.rs"), prelude_part.clone())); - } - files.push((mod_path.clone(), format!("pub mod {};", table.name))); - if path.exists() { - tracing::debug!(?path, "Model file already exists"); - continue; - } + // files.push((mod_path.clone(), format!("pub mod {};", table.name))); + // if path.exists() { + // tracing::debug!(?path, "Model file already exists"); + // continue; + // } - if config.prelude { - let content = templates - .render("model_prelude", &context) - .context("Failed to render model prelude")?; - files.push((path.clone(), content.clone())); - } else { - let content = templates - .render("model", &context) - .context("Failed to render model")?; - files.push((path.clone(), content.clone())); - } + let content = templates + .get_template("model")? + .render(&context) + .context("Failed to render model")?; + files.push((path.clone(), content.clone())); } } else { // One or both keys are missing diff --git a/src/generator/modules/templates/mod.rs b/src/generator/modules/templates/mod.rs index bb1f37e..e7c4ff5 100644 --- a/src/generator/modules/templates/mod.rs +++ b/src/generator/modules/templates/mod.rs @@ -1,12 +1,12 @@ -use std::{collections::HashMap, path::PathBuf}; - use super::{Module, ModulesContext}; use color_eyre::{ eyre::{eyre, ContextCompat}, Result, }; -use handlebars::Handlebars; +use heck::ToPascalCase; +use minijinja::Environment; use serde::Deserialize; +use std::{collections::HashMap, path::PathBuf}; use include_dir::{include_dir, Dir, DirEntry}; use tokio::fs; @@ -107,19 +107,21 @@ impl Module for TemplateModule { } } async fn execute(&mut self, ctx: &mut ModulesContext) -> Result<()> { - let mut registry: Handlebars<'static> = Handlebars::new(); - registry.set_strict_mode(true); + let mut env: Environment<'static> = Environment::new(); + env.add_function("pascalCase", |f: String| f.to_pascal_case()); + // registry.set_strict_mode(true); if let Some(config) = ctx.get_anymap().get::() { - for (templates, path) in config.to_paths() { - tracing::debug!(?templates, ?path, "Registering template"); + for (template, path) in config.to_paths() { + tracing::debug!(?template, ?path, "Registering template"); let content = fs::read_to_string(path).await?; - registry - .register_template_string(templates.as_str(), content) - .map_err(|_| eyre!("Failed to register template"))?; + env.add_template_owned(template, content)?; + // registry + // .register_template_string(templates.as_str(), content) + // .map_err(|_| eyre!("Failed to register template"))?; } - Self::register_default_templates(TEMPLATE_DIR.entries(), &mut registry).await?; + Self::register_default_templates(TEMPLATE_DIR.entries(), &mut env).await?; } - ctx.get_anymap_mut().insert(registry); + ctx.get_anymap_mut().insert(env); Ok(()) } } @@ -127,12 +129,12 @@ impl Module for TemplateModule { impl TemplateModule { async fn register_default_templates<'a>( entries: &[DirEntry<'a>], - handlebars: &mut Handlebars<'a>, + env: &mut Environment<'a>, ) -> Result<()> { for entry in entries.iter().filter(|file| { file.path() .extension() - .is_some_and(|f| f.to_str().is_some_and(|f| f == "hbs")) + .is_some_and(|f| f.to_str().is_some_and(|f| f == "jinja")) || file.as_dir().is_some() }) { match entry { @@ -144,17 +146,18 @@ impl TemplateModule { .replace("/", "."); let content = file .contents_utf8() - .context(format!("Template {} failed to parse", name))?; + .context(format!("Template {} failed to parse", name))? + .to_owned(); tracing::debug!(?name, "Registering template"); - if !handlebars.has_template(&name) { - handlebars.register_template_string(&name, content)?; + if env.get_template(name.as_str()).is_err() { + env.add_template_owned(name, content)?; } else { tracing::debug!(?name, "Template already registered, skipping"); } } DirEntry::Dir(dir) => { - Box::pin(Self::register_default_templates(dir.entries(), handlebars)).await?; + Box::pin(Self::register_default_templates(dir.entries(), env)).await?; } } } diff --git a/src/main.rs b/src/main.rs index fef0124..737a9e2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,6 @@ mod templates; use clap::Parser; use color_eyre::{eyre::eyre, Result}; -use handlebars::Handlebars; use tokio::{fs, io::AsyncWriteExt, process::Command}; use toml_edit::DocumentMut; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; diff --git a/src/templates.rs b/src/templates.rs index 532fcdb..fd2beeb 100644 --- a/src/templates.rs +++ b/src/templates.rs @@ -1,5 +1,4 @@ use color_eyre::eyre::{ContextCompat, Result}; -use handlebars::Handlebars; use std::path::PathBuf; use tokio::fs; diff --git a/templates/model.hbs b/templates/model.hbs.old similarity index 100% rename from templates/model.hbs rename to templates/model.hbs.old diff --git a/templates/model.jinja b/templates/model.jinja new file mode 100644 index 0000000..a17eb2d --- /dev/null +++ b/templates/model.jinja @@ -0,0 +1 @@ +use {{entities_path}}::{{table_name}}::{ActiveModel, Model, Entity}; diff --git a/templates/model_mod.jinja b/templates/model_mod.jinja new file mode 100644 index 0000000..c2cbef2 --- /dev/null +++ b/templates/model_mod.jinja @@ -0,0 +1,9 @@ +{% if config.prelude %} +pub mod prelude; +{% endif%} +{% if entities_path == "super::_entities" %} +pub mod _entities; +{% endif %} +{% for table in tables %} +pub mod {{table.name}}; +{% endfor %} diff --git a/templates/model_prelude.hbs b/templates/model_prelude.hbs.old similarity index 100% rename from templates/model_prelude.hbs rename to templates/model_prelude.hbs.old diff --git a/templates/model_prelude.jinja b/templates/model_prelude.jinja new file mode 100644 index 0000000..363322f --- /dev/null +++ b/templates/model_prelude.jinja @@ -0,0 +1,4 @@ +{% for table in tables %} +pub use {{ entities_path }}::{{ table.name }}::{ActiveModel as {{pascalCase(table.name)}}ActiveModel, Model as {{pascalCase(table.name)}}Model, Entity as {{pascalCase(table.name)}}}; +{# pub use {{entities_path}}::{{table.name}}::{ActiveModel as {% call upperCamelCase(table.name) %}ActiveModel, Model as {% call upperCamelCase(table.name) %}Model, Entity as {% call upperCamelCase(table.name) %}}; #} +{% endfor %} diff --git a/templates/model_prelude_part.hbs b/templates/model_prelude_part.hbs.old similarity index 83% rename from templates/model_prelude_part.hbs rename to templates/model_prelude_part.hbs.old index ddba2a8..db0b96d 100644 --- a/templates/model_prelude_part.hbs +++ b/templates/model_prelude_part.hbs.old @@ -1 +1,3 @@ +{{#each tables}} pub use {{entities_path}}::{{model_name}}::{ActiveModel as {{active_model_name}}, Model as {{model_name}}, Entity as {{entity_name}}}; +{{/each}} diff --git a/tests/src/models/mod.rs b/tests/src/models/mod.rs index 852af71..9a803c4 100644 --- a/tests/src/models/mod.rs +++ b/tests/src/models/mod.rs @@ -1 +1,8 @@ -pub mod prelude;pub mod user; \ No newline at end of file + +pub mod prelude; + + +pub mod _entities; + + +pub mod user; diff --git a/tests/src/models/prelude.rs b/tests/src/models/prelude.rs index c69a8a7..161193d 100644 --- a/tests/src/models/prelude.rs +++ b/tests/src/models/prelude.rs @@ -1 +1,3 @@ -pub use super::_entities::UserModel::{ActiveModel as UserActiveModel, Model as UserModel, Entity as User}; + +pub use super::_entities::user::{ActiveModel as UserActiveModel, Model as UserModel, Entity as User}; + diff --git a/tests/src/models/user.rs b/tests/src/models/user.rs index f35815e..49f1a64 100644 --- a/tests/src/models/user.rs +++ b/tests/src/models/user.rs @@ -1,9 +1 @@ -use super::prelude::*; -use sea_orm::ActiveModelBehavior; - -#[async_trait::async_trait] -impl ActiveModelBehavior for UserActiveModel {} - -impl UserModel {} - -impl UserActiveModel {} +use super::_entities::user::{ActiveModel, Model, Entity}; \ No newline at end of file