use std::env::set_current_dir; use std::fs; use std::io::Write as _; use std::path::Path; use std::process::Command; use structopt::StructOpt; const TOSIN_DEPENDENCY: &str = concat!( r#"tosin = { path = '"#, env!("CARGO_MANIFEST_DIR"), r#"', features = ["sqlite"] } diesel = "1.4.4""# ); const PROJECT_MAIN: &str = r#" #[macro_use] extern crate diesel; use tosin::contrib::admin; use tosin::urls::{url_map, UrlMap}; use tosin::Settings; fn urls() -> UrlMap { url_map! { "admin" / ..admin::site::urls(), } } fn settings() -> Settings { Settings { ..Settings::default() } } tosin::main!(urls(), settings()); "#; const APP_MOD: &str = r#" use tosin::apps::AppConfig; mod migrations; pub mod models; pub mod urls; pub mod views; pub use urls::urls; #[allow(dead_code)] pub const APP: AppConfig = AppConfig { name: module_path!(), models: models::ALL, migrations: migrations::ALL, }; "#; const APP_MIGRATIONS: &str = r#" use tosin::db::migration::{gather, Migration}; gather!(); "#; const APP_MODELS: &str = r#" #[allow(unused_imports)] use tosin::db::models::{gather, Id, Model}; // TODO define models gather!(); "#; const APP_URLS: &str = r#" use tosin::urls::{url_map, UrlMap}; use super::views; pub fn urls() -> UrlMap { todo!("fill in URL map") } "#; const APP_VIEWS: &str = r#" use tosin::http::{Reply, Response}; todo!("write some views"); "#; #[derive(StructOpt, Debug)] enum Opt { /// Start a new project/site (can contain multiple apps) StartProject { name: Option }, /// Start a new app StartApp { name: String }, } fn main() { let opts = Opt::from_args(); match opts { Opt::StartProject { name } => { match name { Some(name) => { // there's a name, so we're creating a project. // TODO make this all more robust let cargo_new = Command::new("cargo") .args(&["new", "--bin", &name]) .status() .unwrap(); if !cargo_new.success() { panic!("cargo new failed"); } set_current_dir(name).unwrap(); let mut cargo_toml = fs::OpenOptions::new() .append(true) .open("Cargo.toml") .unwrap(); writeln!(cargo_toml, "{}", TOSIN_DEPENDENCY).unwrap(); drop(cargo_toml); fs::write("src/main.rs", PROJECT_MAIN).unwrap(); } None => { todo!("absorb existing Rust project maybe??") } } } Opt::StartApp { name } => { // TODO make this more robust if fs::metadata("Cargo.toml").is_ok() { let app_folder = Path::new("src").join(name); fs::create_dir(&app_folder).unwrap(); fs::write(app_folder.join("mod.rs"), APP_MOD).unwrap(); fs::create_dir_all(app_folder.join("migrations")).unwrap(); fs::write(app_folder.join("migrations/mod.rs"), APP_MIGRATIONS).unwrap(); fs::write(app_folder.join("models.rs"), APP_MODELS).unwrap(); fs::write(app_folder.join("urls.rs"), APP_URLS).unwrap(); fs::write(app_folder.join("views.rs"), APP_VIEWS).unwrap(); } else { todo!("new standalone app crate maybe??") } } } }