diff --git a/Cargo.lock b/Cargo.lock index 813a43e..793d3d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -465,6 +465,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.9" @@ -711,7 +720,7 @@ dependencies = [ "anyhow", "clap 2.34.0", "globwalk", - "itertools", + "itertools 0.10.5", "once_cell", "quote", "regex", @@ -798,7 +807,7 @@ checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "salvo-cli" -version = "0.1.20" +version = "0.1.23" dependencies = [ "ansi_term", "anyhow", @@ -806,6 +815,7 @@ dependencies = [ "dialoguer", "git2", "handlebars", + "itertools 0.12.0", "rust-i18n", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 7ef08df..0d30c20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,14 +1,18 @@ [package] name = "salvo-cli" -version = "0.1.20" +version = "0.1.23" edition = "2021" -authors = ["Fankai Liu liufankai137@outlook.com"] +authors = ["Fankai Liu liufankai137@outlook.com","mrxiaozhuox mrxzx.info@gmail.com"] keywords = ["salvo", "cli","template"] description = "This CLI tool is designed to streamline the creation of new Salvo web projects through the generation of template structures. It offers the flexibility to select from web API templates or web site templates, and the convenience of choosing a database connector. It auto-generates foundational code to give users a head start in their development process." license = "MIT/Apache-2.0" repository = "https://github.com/fankaiLiu/salvo-cli" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[[bin]] +name = "salvo" +path = "src/main.rs" + [dependencies] ansi_term = "0.12.1" anyhow = "1.0.75" @@ -19,4 +23,5 @@ handlebars = "4.4.0" serde = { version = "1.0.188", features = ["derive"] } serde_json = "1.0.107" unicode-xid = "0.2.4" -rust-i18n = "2" \ No newline at end of file +rust-i18n = "2" +itertools = "0.12.0" diff --git a/README.md b/README.md index 8884063..24fa86f 100644 --- a/README.md +++ b/README.md @@ -30,58 +30,58 @@ To create a new Salvo project, use the new command followed by the name of your ```bash //use the local language -salvo-cli new project_name +salvo new project_name // Use English -salvo-cli new project_name --lang=en +salvo new project_name --lang=en // 使用简体中文 -salvo-cli new project_name --lang=zh +salvo new project_name --lang=zh // 使用繁體中文 -salvo-cli new project_name --lang=zh_TW +salvo new project_name --lang=zh_TW // Utilisez le français -salvo-cli new project_name --lang=fr +salvo new project_name --lang=fr // 日本語を使用する -salvo-cli new project_name --lang=ja +salvo new project_name --lang=ja // Usa el español -salvo-cli new project_name --lang=es +salvo new project_name --lang=es // Verwenden Sie Deutsch -salvo-cli new project_name --lang=de +salvo new project_name --lang=de // Используйте русский -salvo-cli new project_name --lang=ru +salvo new project_name --lang=ru // Usa l `italiano -salvo-cli new project_name --lang=it +salvo new project_name --lang=it // Use o português -salvo-cli new project_name --lang=pt +salvo new project_name --lang=pt // 한국어를 사용하십시오 -salvo-cli new project_name --lang=ko +salvo new project_name --lang=ko // Bruk norsk -salvo-cli new project_name --lang=no +salvo new project_name --lang=no // Notaðu íslensku -salvo-cli new project_name --lang=is +salvo new project_name --lang=is // Використовуйте українську -salvo-cli new project_name --lang=uk +salvo new project_name --lang=uk // ใช้ภาษาไทย -salvo-cli new project_name --lang=th +salvo new project_name --lang=th // Χρησιμοποιήστε την ελληνική -salvo-cli new project_name --lang=el +salvo new project_name --lang=el // Brug dansk -salvo-cli new project_name --lang=da +salvo new project_name --lang=da ``` ## Update @@ -97,11 +97,11 @@ cargo install --force salvo-cli | ✅ | web api template | | ✅ | web site template | | ✅ | Template with SQLx, SeaORM, Diesel, Rbatis (support for SQLite, PostgreSQL, MySQL) | -| ✅ | Basic middleware | +| ✅ | jwt,cors... middleware | | ✅ | Support for MongoDB | -| ⏳ | More middleware | +| ⏳ | command:salvo run | | ⏳ | Support for docker | -| ⏳ | More integrations with good crates (validation, embedding, permissions or others?) | +| ⏳ | More integrations with good crates (validation, permissions or others?) | | ⏳ | Split into multiple crates for clearer code organization | ## License diff --git a/locales/app.yml b/locales/app.yml index abe087a..b59d976 100644 --- a/locales/app.yml +++ b/locales/app.yml @@ -418,4 +418,4 @@ access_instructions: uk: Після його запуску Ви можете отримати доступ до /login за допомогою стандартного імені користувача:zhangsan та пароля:123 th: หลังจากทำการรันแล้ว คุณสามารถเข้าถึง /login ด้วยชื่อผู้ใช้งานเริ่มต้น:zhangsan และรหัสผ่าน:123 el: Αφού το τρέξετε, μπορείτε να προσπελάσετε το /login με το προεπιλεγμένο όνομα χρήστη:zhangsan και τον κωδικό πρόσβασης:123 - da: Efter du har kørt det, kan du få adgang til /login med standard brugernavn:zhangsan og adgangskode:123 \ No newline at end of file + da: Efter du har kørt det, kan du få adgang til /login med standard brugernavn:zhangsan og adgangskode:123 diff --git a/src/main.rs b/src/main.rs index 2f04bb7..3335dec 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,12 @@ use anyhow::Result; use clap::Parser; - +mod test; mod utils; use i18n::set_locale; mod i18n; rust_i18n::i18n!("locales", fallback = "en"); #[derive(Parser, Debug)] -#[clap(version = "0.1.20", author = "Fankai liu ")] +#[clap(version = "0.1.23", author = "Fankai liu ")] struct Opts { #[clap(subcommand)] subcmd: SubCommand, diff --git a/src/template/src/routers/mod.hbs b/src/template/src/routers/mod.hbs index 137ef3b..e4c218d 100644 --- a/src/template/src/routers/mod.hbs +++ b/src/template/src/routers/mod.hbs @@ -81,8 +81,7 @@ pub fn router() -> Router { .delete(delete_user), ), ]; - let static_routers = static_routers::create_static_routers(); - no_auth_routers.extend(static_routers); + let router = Router::new() .hoop(Logger::new()) .hoop(CatchPanic::new()) diff --git a/src/test/mod.rs b/src/test/mod.rs new file mode 100644 index 0000000..da9de87 --- /dev/null +++ b/src/test/mod.rs @@ -0,0 +1 @@ +mod test_write_project; diff --git a/src/test/test_write_project.rs b/src/test/test_write_project.rs new file mode 100644 index 0000000..0adf2bd --- /dev/null +++ b/src/test/test_write_project.rs @@ -0,0 +1,81 @@ +#[cfg(test)] +mod tests { + use crate::{ + utils::{ + create_project::write_project_file, + get_selection::{DbConnectionType, DbType, TemplateType, UserSelected}, + }, + Project, + }; + use itertools::Itertools; + use std::path::Path; + + #[test] + fn test_write_project_all_combinations() { + let template_types = [TemplateType::SalvoWebSite, TemplateType::SalvoWebApi]; + //let db_types = [DbType::Sqlite, DbType::Mysql, DbType::Postgres, DbType::Mssql]; + let db_types = [DbType::Sqlite]; + let db_conn_types = [ + DbConnectionType::Sqlx, + DbConnectionType::SeaOrm, + DbConnectionType::Diesel, + DbConnectionType::Rbatis, + DbConnectionType::Mongodb, + DbConnectionType::Nothing, + ]; + + // Generate all combinations + let combinations = template_types + .iter() + .cartesian_product(db_types.iter()) + .cartesian_product(db_conn_types.iter()) + .map(|((template_type, db_type), db_conn_type)| (template_type, db_type, db_conn_type)) + .collect::>(); + + // Test each combination + for (template_type, db_type, db_conn_type) in combinations { + // Generate a unique project name for each combination + let project_name = format!("test_{:?}_{:?}_{:?}", template_type, db_type, db_conn_type); + + let path_str = format!("target/{}", project_name); + std::fs::remove_dir_all(&path_str).unwrap_or(()); + let path = Path::new(&path_str); + + let user_selected = UserSelected { + template_type: *template_type, + db_type: *db_type, + db_conn_type: *db_conn_type, + }; + let project = Project { + project_name: project_name.clone(), + lang: Some("zh".to_string()), + }; + match write_project_file(path, user_selected, project) { + Ok(()) => { + let output = std::process::Command::new("cargo") + .arg("check") + .current_dir(&path_str) + .output() + .expect("failed to execute process"); + if !output.status.success() { + eprintln!( + "Failed on combination: template_type={:?}, db_type={:?}, db_conn_type={:?}", + template_type, db_type, db_conn_type + ); + eprintln!("Output: {:?}", output); + panic!(); + } + } + Err(e) => { + eprintln!( + "Failed to write project file on combination: template_type={:?}, db_type={:?}, db_conn_type={:?}", + template_type, db_type, db_conn_type + ); + eprintln!("Error: {:?}", e); + panic!(); + } + } + std::fs::remove_dir_all(&path_str).unwrap_or(()); + } + } +} diff --git a/src/utils/create_project.rs b/src/utils/create_project.rs index 887107b..16fe6b7 100644 --- a/src/utils/create_project.rs +++ b/src/utils/create_project.rs @@ -45,7 +45,13 @@ pub fn create_project(project: Project) -> Result<()> { } fn after_print_info(project_name: &String, config: UserSelected) { + println!(); // a new line + + // print success info success(t!("create_success", project_name = project_name).replace(r"\n", "\n")); + + println!(); // a new line + match config.db_conn_type { DbConnectionType::Sqlx => { success(t!("create_success_sqlx").replace(r"\n", "\n")); @@ -91,7 +97,7 @@ fn after_print_info(project_name: &String, config: UserSelected) { } } -fn write_project_file( +pub fn write_project_file( project_path: &Path, user_selected: UserSelected, project: Project, @@ -196,9 +202,12 @@ fn write_project_file( handle_404_file.write_all(handle_404_template_rendered.as_bytes())?; //assets let assets_path = project_path.join("assets"); + std::fs::create_dir_all(&assets_path)?; + //assets/favicon.ico let favicon_bytes = include_bytes!("../template/assets/favicon.ico"); let mut favicon_file = File::create(assets_path.join("favicon.ico"))?; + favicon_file.write_all(favicon_bytes)?; if need_db_conn { //assets/js @@ -254,6 +263,15 @@ fn write_project_file( let router_user_rendered = handlebars.render_template(router_user_template, &data)?; let mut router_user_file = File::create(router_path.join("user.rs"))?; router_user_file.write_all(router_user_rendered.as_bytes())?; + + //src/router/static_routers.rs + let router_static_routers_template = + include_str!("../template/src/routers/static_routers.hbs"); + let router_static_routers_rendered = + handlebars.render_template(router_static_routers_template, &data)?; + let mut router_static_routers_file = File::create(router_path.join("static_routers.rs"))?; + router_static_routers_file.write_all(router_static_routers_rendered.as_bytes())?; + //src/services let services_path = src_path.join("services"); std::fs::create_dir_all(&services_path)?; @@ -267,6 +285,7 @@ fn write_project_file( let services_user_rendered = handlebars.render_template(services_user_template, &data)?; let mut services_user_file = File::create(services_path.join("user.rs"))?; services_user_file.write_all(services_user_rendered.as_bytes())?; + //src/utils let utils_path = src_path.join("utils"); std::fs::create_dir_all(&utils_path)?; @@ -643,6 +662,12 @@ fn create_basic_file( let mut router_demo_file = File::create(router_path.join("demo.rs"))?; router_demo_file.write_all(router_demo_rendered.as_bytes())?; + //src/router/static_routers.rs + // let router_static_routers_template = include_str!("../template/src/routers/static_routers.hbs"); + // let router_static_routers_rendered = handlebars.render_template(router_static_routers_template, &data)?; + // let mut router_static_routers_file = File::create(router_path.join("static_routers.rs"))?; + // router_static_routers_file.write_all(router_static_routers_rendered.as_bytes())?; + Ok((src_path, router_path)) } diff --git a/src/utils/print_util.rs b/src/utils/print_util.rs index b996462..9faf894 100644 --- a/src/utils/print_util.rs +++ b/src/utils/print_util.rs @@ -2,17 +2,19 @@ use ansi_term::Colour::{Blue, Green, Red, Yellow}; pub fn print_logo() { let art = r" - _______________________ ________ ______________________ - __ ___/__ |__ /__ | / /_ __ \ __ ____/__ /____ _/ - _____ \__ /| |_ / __ | / /_ / / / _ / __ / __ / - ____/ /_ ___ | /____ |/ / / /_/ / / /___ _ /____/ / - /____/ /_/ |_/_____/____/ \____/ \____/ /_____/___/ + ____ _ _ __ _____ ____ _ ___ + / ___| / \ | |\ \ / / _ \ / ___| | |_ _| + \___ \ / _ \ | | \ \ / / | | | | | | | | | + ___) / ___ \| |__\ V /| |_| | | |___| |___ | | + |____/_/ \_\_____\_/ \___/ \____|_____|___| + "; let lines = art.lines(); for line in lines { let (part_blue, part_green) = line.split_at(line.len() / 2); println!("{}{}", Blue.paint(part_blue), Green.paint(part_green)); } + println!(); // a new line } pub fn warning>(msg: S) {