#[macro_export]
macro_rules! commands {
($($module:ident $(($($alias:ident),*))?),*) => {
pastey::paste! {
pub fn build_args() -> clap::Command {
let mut cmd = clap::Command::new("railway")
.about("Railway CLI")
.author(clap::crate_authors!())
.propagate_version(true)
.about(concat!(
clap::crate_description!(),
"\n\nTip: Using an AI coding agent? Run `railway setup agent -y` to install Railway skills and MCP configuration."
))
.long_about(None)
.version(clap::crate_version!());
$(
{
// Get the subcommand as defined by the module.
let sub = <$module::Args as ::clap::CommandFactory>::command();
let sub = {
let mut s = sub;
$(
$(
s = s.visible_alias(stringify!($alias));
)*
)?
#[allow(unused_imports)]
{
use $crate::commands::get_dynamic_args;
{
use $crate::commands::$module::*;
s = get_dynamic_args(s);
}
}
s = s.name(stringify!($module));
s
};
cmd = cmd.subcommand(sub);
}
)*
cmd = cmd
.mut_subcommand("list", |cmd| cmd.visible_alias("ls"))
.mut_subcommand("delete", |cmd| {
cmd.visible_alias("rm").visible_alias("remove")
})
.mut_subcommand("project", |cmd| cmd.visible_alias("projects"))
.mut_subcommand("bucket", |cmd| cmd.visible_alias("buckets"))
.mut_subcommand("volume", |cmd| cmd.visible_alias("volumes"))
.mut_subcommand("deployment", |cmd| cmd.visible_alias("deployments"))
.mut_subcommand("templates", |cmd| cmd.visible_alias("template"))
.mut_subcommand("check_updates", |cmd| cmd.visible_alias("check-updates"));
cmd
}
pub async fn exec_cli(matches: clap::ArgMatches) -> anyhow::Result<()> {
match matches.subcommand() {
$(
Some((stringify!([<$module:snake>]), sub_matches)) => {
let subcommand_name = sub_matches.subcommand_name().map(|s| s.to_string());
let args = <$module::Args as ::clap::FromArgMatches>::from_arg_matches(sub_matches)
.map_err(anyhow::Error::from)?;
let start = ::std::time::Instant::now();
let result = $module::command(args).await;
let duration = start.elapsed();
$crate::telemetry::send($crate::telemetry::CliTrackEvent {
command: stringify!([<$module:snake>]).to_string(),
sub_command: subcommand_name,
success: result.is_ok(),
error_message: result.as_ref().err().map(|e| {
let msg = format!("{e}");
if msg.len() > 256 { msg[..256].to_string() } else { msg }
}),
duration_ms: duration.as_millis() as u64,
cli_version: env!("CARGO_PKG_VERSION"),
os: ::std::env::consts::OS,
arch: ::std::env::consts::ARCH,
is_ci: $crate::config::Configs::env_is_ci(),
}).await;
result?;
},
)*
_ => {
build_args().print_help()?;
println!();
}
}
Ok(())
}
}
};
}
use is_terminal::IsTerminal;
pub fn is_stdout_terminal() -> bool {
std::io::stdout().is_terminal()
}
#[macro_export]
macro_rules! interact_or {
($message:expr) => {
if !$crate::macros::is_stdout_terminal() {
::anyhow::bail!($message);
}
};
}