mod native;
mod new;
mod prompt;
mod templates;
mod upgrade;
mod wiring;
use std::process::ExitCode;
const VERSION: &str = env!("CARGO_PKG_VERSION");
fn main() -> ExitCode {
let args: Vec<String> = std::env::args().skip(1).collect();
let args: Vec<&str> = args
.iter()
.map(String::as_str)
.skip_while(|a| *a == "rahti")
.collect();
let result = match args.first().copied() {
Some("new") => new::run(&args[1..]),
Some("upgrade") => upgrade::run(&args[1..]),
Some("native") => return native::run(&args[1..]),
_ => return other(&args),
};
match result {
Ok(()) => ExitCode::SUCCESS,
Err(message) => {
eprintln!("error: {message}");
ExitCode::FAILURE
}
}
}
fn other(args: &[&str]) -> ExitCode {
match args.first().copied() {
Some("--version" | "-V") => {
println!("cargo-rahti {VERSION}");
ExitCode::SUCCESS
}
Some("help" | "--help" | "-h") | None => {
print!("{}", usage());
ExitCode::SUCCESS
}
Some(other) => {
eprintln!("error: `{other}` is not a cargo-rahti command.\n");
eprint!("{}", usage());
ExitCode::FAILURE
}
}
}
fn usage() -> String {
format!(
"cargo-rahti {VERSION}
Create and maintain Rahti projects.
USAGE:
cargo rahti new <name> [options]
cargo rahti upgrade [--dry-run] [--force [path...]] [--db [backend]] [--ws] [--mcp]
cargo rahti native <command> [options]
NEW:
A flag adds a feature. Leaving it out is how you say no.
--tailwind Add Tailwind CSS. Without it, plain CSS.
--db [backend] Add a database — sqlite, postgres or mysql. Bare
`--db` is sqlite, the one that needs no server.
Writes src/models/ and src/migrations/, and adds
SeaORM to Cargo.toml.
--ws Add WebSockets: the `ws` feature on the rahti
dependency, which compiles `rahti::ws` and the
`#[socket]` attribute.
--mcp Add Rahti's MCP developer tooling configuration.
Requires `cargo install rahti-mcp` once per machine.
--local <path> Depend on a Rahti checkout by path rather than by
version. For working on the framework itself.
UPGRADE:
-n, --dry-run Print what would change, and change nothing.
-f, --force [path...]
Take back scaffolded files you have edited. Bare,
it takes the framework's own — src/main.rs, build.rs,
the docs, the PulsePoint assets — and leaves
everything else under src/, which is your
application. Name paths to take those too, including
an application file. Pair it with --dry-run first:
the replaced version is not kept anywhere.
--db [backend] Add a database to a project that has none — the same
flag `new` takes. Writes the files, records the
backend in rahti.config.json, and adds SeaORM to
Cargo.toml and DATABASE_URL to .env.
--ws Add WebSockets to a project without them, feature and
all.
--mcp Add MCP tooling to a project without it, including
the recorded .mcp.json client configuration.
Rewrites the scaffolded files you have not edited, and leaves the ones
you have. Which is which is decided by the hashes in rahti.config.json,
not by guessing. A dry run never asks a question — name the feature to
preview adding it.
Cargo.toml and .env are never regenerated, because your dependencies and
your credentials are yours — but they are amended when a feature needs
it, by additions only, and never the same one twice.
NATIVE:
Optional Windows and Android packaging, in a separate tool so that a web
project never resolves a native dependency:
cargo install cargo-rahti-native
Once it is installed, `cargo rahti native init`, `doctor`, `dev` and
`build` are forwarded to it. Run `cargo rahti native --help` for those.
-h, --help Print this message.
-V, --version Print the version.
An interactive run asks about any feature you did not name. A run with
nowhere to ask — a pipe, a CI job — takes the flags at their word and adds
only what they list.
"
)
}