pub mod api;
pub mod htmx;
pub mod microservice;
pub mod minimal;
pub mod saas;
use std::path::Path;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum DbDriver {
#[default]
Postgres,
Mysql,
Sqlite,
}
impl DbDriver {
pub fn sqlx_feature(&self) -> &'static str {
match self {
Self::Postgres => "postgres",
Self::Mysql => "mysql",
Self::Sqlite => "sqlite",
}
}
pub fn example_url(&self) -> &'static str {
match self {
Self::Postgres => "postgres://postgres:postgres@localhost/app",
Self::Mysql => "mysql://root:root@localhost/app",
Self::Sqlite => "sqlite://db.sqlite3",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum AuthDriver {
#[default]
Jwt,
Session,
Social,
MagicLink,
None,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum CacheDriver {
#[default]
Memory,
Redis,
None,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum QueueDriver {
#[default]
None,
Postgres,
Redis,
}
pub struct WizardConfig {
pub template: Template,
pub db: DbDriver,
pub auth: AuthDriver,
pub cache: CacheDriver,
pub queue: QueueDriver,
pub include_examples: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Template {
Api,
Saas,
Htmx,
Microservice,
Minimal,
}
impl Template {
pub fn from_str(s: &str) -> anyhow::Result<Self> {
match s {
"api" => Ok(Self::Api),
"saas" => Ok(Self::Saas),
"htmx" => Ok(Self::Htmx),
"microservice" => Ok(Self::Microservice),
"minimal" => Ok(Self::Minimal),
_ => anyhow::bail!(
"Unknown template '{s}'. Valid options: api, saas, htmx, microservice, minimal"
),
}
}
pub fn label(&self) -> &'static str {
match self {
Self::Api => "api",
Self::Saas => "saas",
Self::Htmx => "htmx",
Self::Microservice => "microservice",
Self::Minimal => "minimal",
}
}
pub fn handles_directory_creation(&self) -> bool {
matches!(self, Self::Api)
}
pub fn generate(
&self,
project_name: &str,
root: &Path,
config: &WizardConfig,
) -> anyhow::Result<()> {
match self {
Self::Api => api::generate(project_name, root, config),
Self::Saas => saas::generate(project_name, root, config),
Self::Htmx => htmx::generate(project_name, root, config),
Self::Microservice => microservice::generate(project_name, root, config),
Self::Minimal => minimal::generate(project_name, root, config),
}
}
}
pub fn prompt_wizard() -> anyhow::Result<WizardConfig> {
use dialoguer::{Confirm, Select};
let template_items = &[
"api REST API with JWT auth + CRUD ready",
"saas Multi-tenant SaaS (magic link, billing hooks)",
"htmx Full-stack with Htmx + Minijinja templates",
"microservice Minimal service with health endpoint + Docker",
"minimal Bare skeleton (axum + sqlx only)",
];
let tmpl_sel = Select::new()
.with_prompt("Project template")
.items(template_items)
.default(0)
.interact()?;
let template = match tmpl_sel {
0 => Template::Api,
1 => Template::Saas,
2 => Template::Htmx,
3 => Template::Microservice,
_ => Template::Minimal,
};
let db_items = &["PostgreSQL", "MySQL", "SQLite"];
let db_sel = Select::new()
.with_prompt("Database")
.items(db_items)
.default(0)
.interact()?;
let db = match db_sel {
1 => DbDriver::Mysql,
2 => DbDriver::Sqlite,
_ => DbDriver::Postgres,
};
let auth_items = &["JWT", "Session", "Social (OAuth)", "Magic Link", "None"];
let auth_sel = Select::new()
.with_prompt("Auth driver")
.items(auth_items)
.default(0)
.interact()?;
let auth = match auth_sel {
1 => AuthDriver::Session,
2 => AuthDriver::Social,
3 => AuthDriver::MagicLink,
4 => AuthDriver::None,
_ => AuthDriver::Jwt,
};
let cache_items = &["Memory (in-process)", "Redis", "None"];
let cache_sel = Select::new()
.with_prompt("Cache driver")
.items(cache_items)
.default(0)
.interact()?;
let cache = match cache_sel {
1 => CacheDriver::Redis,
2 => CacheDriver::None,
_ => CacheDriver::Memory,
};
let queue_items = &["None", "PostgreSQL", "Redis"];
let queue_sel = Select::new()
.with_prompt("Queue driver")
.items(queue_items)
.default(0)
.interact()?;
let queue = match queue_sel {
1 => QueueDriver::Postgres,
2 => QueueDriver::Redis,
_ => QueueDriver::None,
};
let include_examples = Confirm::new()
.with_prompt("Include example files?")
.default(true)
.interact()?;
Ok(WizardConfig {
template,
db,
auth,
cache,
queue,
include_examples,
})
}