mod commands;
mod types;
use anyhow::Result;
use clap::{Parser, Subcommand};
use commands::{gen::GenKind, db::DbAction, MigrateAction};
#[derive(Parser)]
#[command(
name = "keg",
version,
author = "Your Name <you@example.com>",
about = "Kegani: a developer-friendly, ergonomic, production-ready Rust web framework",
long_about = "Kegani — A complete Rust web framework in a single binary
Commands:
init Create a complete project with layered architecture
new Alias for init — create a new project
gen Generate code: api, model, service, repository, controller, middleware, migration
list List available project templates
run Run the application with hot reload (file watching)
serve Alias for run — starts server with hot reload
build Build a release binary (cross-compile supported)
check Run cargo check with pretty output
test Run tests with options
docker Generate Dockerfile and docker-compose.yml
db Database operations (create, drop, seed, psql)
console Start interactive Rust REPL (evcxr)
docs Open Kegani documentation in browser
info Show detailed project information
secret Generate secure secrets and API keys
env Print environment diagnostics
up Update Kegani framework and CLI
migrate Run database migrations (up, down, status, reset)
clean Remove build artifacts
Examples:
keg init my-api Create a new project
keg new my-api Same as init
keg gen api article Generate full CRUD stack
keg gen model user Generate entity
keg gen migration create_users Generate SQL migration
keg run Run with hot reload
keg build --target x86_64-unknown-linux-musl
keg docker Generate Docker files
keg secret Generate a secure secret key
keg db seed Seed the database
keg console Start Rust REPL"
)]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
Init {
name: Option<String>,
#[arg(short, long, default_value = "api")]
template: String,
#[arg(short, long, default_value = "false")]
interactive: bool,
},
New {
name: Option<String>,
#[arg(short, long, default_value = "api")]
template: String,
#[arg(short, long, default_value = "false")]
interactive: bool,
},
Gen {
#[arg(value_enum)]
kind: GenKind,
name: String,
#[arg(long, default_value = "false")]
list: bool,
},
List {
#[arg(short, long, default_value = "false")]
verbose: bool,
},
Serve {
#[arg(short, long, default_value = "8080")]
port: u16,
#[arg(short, long, default_value = "dev")]
env: String,
},
Run {
#[arg(short, long, default_value = "8080")]
port: u16,
#[arg(short, long, default_value = "true")]
watch: bool,
#[arg(short, long, default_value = "dev")]
env: String,
},
Build {
#[arg(short, long)]
target: Option<String>,
#[arg(long, default_value = "false")]
strip: bool,
#[arg(short, long, default_value = "false")]
verbose: bool,
#[arg(long, default_value = "false")]
size: bool,
},
Check {
#[arg(short, long, default_value = "false")]
verbose: bool,
},
Test {
#[arg(default_value = "")]
filter: String,
#[arg(long, default_value = "false")]
release: bool,
#[arg(short, long, default_value = "false")]
nocapture: bool,
},
Docker,
Db {
#[command(subcommand)]
action: DbAction,
},
Console {
#[arg(short, long)]
package: Option<String>,
},
Docs {
#[arg(default_value = "guide")]
section: String,
},
Info {
#[arg(long, default_value = "false")]
json: bool,
},
Secret {
#[arg(default_value = "secret")]
kind: String,
#[arg(short, long)]
length: Option<usize>,
},
Env {
#[arg(long, default_value = "false")]
json: bool,
},
Up,
Migrate {
#[arg(value_enum)]
action: MigrateAction,
},
Clean {
#[arg(short, long, default_value = "false")]
all: bool,
},
}
fn main() -> Result<()> {
let cli = Cli::parse();
match cli.command {
Commands::Init { name, template, interactive } => {
let name = name.unwrap_or_else(|| {
crate::commands::init::prompt_project_name()
});
commands::init::init(&name, &template, interactive)?
}
Commands::New { name, template, interactive } => {
let name = name.unwrap_or_else(|| {
crate::commands::init::prompt_project_name()
});
commands::init::init(&name, &template, interactive)?
}
Commands::Gen { kind, name, list } => commands::gen::gen(kind, &name, list)?,
Commands::List { verbose } => commands::init::list_templates(verbose)?,
Commands::Run { port, watch, env } => commands::run::run(port, watch, &env)?,
Commands::Serve { port, env } => commands::run::run(port, true, &env)?,
Commands::Build { target, strip, verbose, size } => {
commands::build::build(target.as_deref(), strip, verbose, size)?
}
Commands::Check { verbose } => commands::build::check(verbose)?,
Commands::Test { filter, release, nocapture } => {
commands::build::test(&filter, release, nocapture)?
}
Commands::Docker => commands::docker::docker()?,
Commands::Db { action } => commands::db::run_db_action(action)?,
Commands::Console { package } => commands::console::console(package.as_deref())?,
Commands::Docs { section } => commands::docs::open_docs(§ion)?,
Commands::Info { json } => commands::info::info(json)?,
Commands::Secret { kind, length } => commands::secret::generate_secret(&kind, length)?,
Commands::Env { json } => commands::env::env(json)?,
Commands::Up => commands::update::update()?,
Commands::Migrate { action } => commands::migrate::run_migration(action)?,
Commands::Clean { all } => commands::clean::clean(all)?,
}
Ok(())
}