pub mod build;
pub mod check;
pub mod doc;
pub mod doctor;
pub mod exit;
pub mod format;
pub mod generate;
pub mod lint;
pub mod migration;
pub mod output;
pub mod serve;
pub mod store;
pub mod test;
pub mod thingd_schema;
use clap::{Parser, Subcommand, ValueEnum};
use output::Output;
#[derive(Parser)]
#[command(
name = "arqen",
version,
about = "Arqen CLI for generating and running applications"
)]
pub struct Cli {
#[command(subcommand)]
command: Commands,
#[arg(long, global = true)]
json: bool,
#[arg(long, global = true)]
verbose: bool,
#[arg(long, global = true)]
quiet: bool,
#[arg(long, global = true, default_value = "auto")]
color: ColorChoice,
}
#[derive(Clone, Copy, Debug, ValueEnum)]
enum ColorChoice {
Auto,
Always,
Never,
}
#[derive(Subcommand)]
pub enum Commands {
New {
name: String,
},
Generate {
#[command(subcommand)]
kind: GenerateKind,
},
Dev {
#[arg(long)]
host: Option<String>,
#[arg(short, long)]
port: Option<u16>,
#[arg(short, long)]
log: Option<String>,
#[arg(short, long)]
storage: Option<String>,
#[arg(long, value_enum)]
log_format: Option<crate::config::LogFormat>,
#[arg(long = "file")]
config_file: Option<String>,
},
#[command(visible_alias = "run", alias = "serve")]
Start {
#[arg(long)]
host: Option<String>,
#[arg(short, long)]
port: Option<u16>,
#[arg(short, long)]
log: Option<String>,
#[arg(short, long)]
storage: Option<String>,
#[arg(long, value_enum)]
log_format: Option<crate::config::LogFormat>,
#[arg(long = "file")]
config_file: Option<String>,
#[arg(long)]
skip_schema_validation: bool,
},
Up {
#[arg(value_name = "SERVICE")]
services: Vec<String>,
#[arg(long, default_value = "arqen.toml")]
file: std::path::PathBuf,
#[arg(long)]
dry_run: bool,
},
Check,
Doctor,
Lint,
Format,
Test {
#[arg(long)]
release: bool,
},
Build {
#[arg(long)]
release: bool,
},
Doc,
Thingd {
#[command(subcommand)]
command: ThingdCommand,
},
Store {
#[command(subcommand)]
command: StoreCommand,
},
}
#[derive(Subcommand)]
pub enum StoreCommand {
Export {
#[arg(long)]
output: std::path::PathBuf,
#[arg(long)]
data_dir: Option<std::path::PathBuf>,
},
Import {
#[arg(long)]
input: std::path::PathBuf,
#[arg(long)]
data_dir: Option<std::path::PathBuf>,
},
}
#[derive(Subcommand)]
pub enum ThingdCommand {
Migrate {
#[arg(long)]
source: std::path::PathBuf,
#[arg(long)]
destination: String,
#[arg(long)]
auth_token: Option<String>,
#[arg(long, default_value_t = 100)]
batch_size: usize,
#[arg(long)]
resume: bool,
#[arg(long)]
dry_run: bool,
#[arg(long)]
check: bool,
#[arg(long)]
include_replication: bool,
#[arg(long)]
encryption_key: Option<String>,
},
SchemaValidate {
path: std::path::PathBuf,
#[arg(long)]
url: Option<String>,
#[arg(long)]
token: Option<String>,
},
SchemaRemote {
url: String,
#[arg(long)]
token: Option<String>,
},
}
#[derive(Subcommand)]
pub enum GenerateKind {
Module {
name: String,
},
Tool {
name: String,
},
Job {
name: String,
},
}
fn dispatch(cli: &Cli, output: &Output) -> anyhow::Result<()> {
match &cli.command {
Commands::New { name } => {
generate::generate_project(name, output)?;
}
Commands::Generate { kind } => match kind {
GenerateKind::Module { name } => generate::generate_module(name, output)?,
GenerateKind::Tool { name } => generate::generate_tool(name, output)?,
GenerateKind::Job { name } => generate::generate_job(name, output)?,
},
Commands::Dev {
host,
port,
log,
storage,
config_file,
log_format,
} => {
let code = serve::serve_dev(
config_file.as_deref(),
host.as_deref(),
*port,
log.as_deref(),
storage.as_deref(),
log_format.as_ref(),
output,
);
if code != exit::SUCCESS {
std::process::exit(code);
}
}
Commands::Start {
host,
port,
log,
storage,
config_file,
log_format,
skip_schema_validation,
} => {
let code = serve::serve_start(
config_file.as_deref(),
host.as_deref(),
*port,
log.as_deref(),
storage.as_deref(),
log_format.as_ref(),
*skip_schema_validation,
output,
);
if code != exit::SUCCESS {
std::process::exit(code);
}
}
Commands::Up {
services,
file,
dry_run,
} => {
let rt = tokio::runtime::Runtime::new().map_err(|e| anyhow::anyhow!(e))?;
rt.block_on(crate::dev::run_up(file, services, *dry_run))?;
}
Commands::Check => {
let code = check::run_check(output);
if code != exit::SUCCESS {
std::process::exit(code);
}
}
Commands::Doctor => {
let code = doctor::run_doctor(output);
if code != exit::SUCCESS {
std::process::exit(code);
}
}
Commands::Lint => {
let code = lint::run_lint(output);
if code != exit::SUCCESS {
std::process::exit(code);
}
}
Commands::Format => {
let code = format::run_format(output);
if code != exit::SUCCESS {
std::process::exit(code);
}
}
Commands::Test { release } => {
let code = test::run_test(*release, output);
if code != exit::SUCCESS {
std::process::exit(code);
}
}
Commands::Build { release } => {
let code = build::run_build(*release, output);
if code != exit::SUCCESS {
std::process::exit(code);
}
}
Commands::Doc => {
let code = doc::run_doc(output);
if code != exit::SUCCESS {
std::process::exit(code);
}
}
Commands::Thingd { command } => {
if let ThingdCommand::Migrate {
source,
destination,
auth_token,
batch_size,
resume,
dry_run,
check,
include_replication,
encryption_key,
} = command
{
let options = crate::migration::ThingdMigrationOptions {
source_path: source.clone(),
destination_url: destination.clone(),
destination_auth_token: auth_token.clone(),
dry_run: *dry_run,
resume: *resume,
include_replication: *include_replication,
batch_size: *batch_size,
source_encryption_key: encryption_key.clone(),
..Default::default()
};
migration::run(options, *check, output)?;
} else {
let code = thingd_schema::run(command, output);
if code != exit::SUCCESS {
std::process::exit(code);
}
}
}
Commands::Store { command } => {
let code = match command {
StoreCommand::Export {
output: path,
data_dir,
} => store::export(data_dir.as_deref(), path, output),
StoreCommand::Import {
input: path,
data_dir,
} => store::import(data_dir.as_deref(), path, output),
};
if code != exit::SUCCESS {
std::process::exit(code);
}
}
}
Ok(())
}
pub fn run() -> i32 {
let cli = Cli::parse();
let color = match cli.color {
ColorChoice::Auto => "auto",
ColorChoice::Always => "always",
ColorChoice::Never => "never",
};
let output = Output::from_args(cli.json, cli.quiet, cli.verbose, color);
match dispatch(&cli, &output) {
Ok(()) => exit::SUCCESS,
Err(e) => {
output.print_error(&e.to_string());
exit::RUNTIME
}
}
}