mod cli;
use anyhow::{Context, Result, ensure};
use clap::{Parser as ClapParser, Subcommand};
use std::{path::PathBuf, process::Command};
use cli::setup::EditorTarget;
use cli::{display_path, BOLD, CYAN, RESET};
#[derive(ClapParser)]
#[command(name = "rosy")]
#[command(version)]
#[command(about = "Rosy Transpiler for beam physics calculations", long_about = None)]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
Run {
source: PathBuf,
#[arg(short = 'd', long)]
output_dir: Option<PathBuf>,
#[arg(short, long)]
release: bool,
#[arg(long)]
optimized: bool,
#[arg(long)]
trace_types: bool,
},
Test {
#[arg(short, long)]
filter: Option<String>,
#[arg(short, long)]
release: bool,
#[arg(long)]
bless: bool,
},
Build {
source: PathBuf,
#[arg(short, long)]
output: Option<String>,
#[arg(short = 'd', long)]
output_dir: Option<PathBuf>,
#[arg(short, long)]
release: bool,
#[arg(long)]
optimized: bool,
#[arg(long)]
trace_types: bool,
},
Lsp {
#[arg(long, hide = true)]
stdio: bool,
},
Setup {
#[command(subcommand)]
editor: EditorTarget,
},
}
fn main() -> Result<()> {
let cli = Cli::parse();
if matches!(&cli.command, Commands::Lsp { .. }) {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.expect("Failed to create tokio runtime");
rt.block_on(rosy_compiler::lsp::run());
return Ok(());
}
if let Commands::Setup { editor } = &cli.command {
return cli::setup::install_editor_extension(editor);
}
if let Commands::Test {
filter,
release,
bless,
} = &cli.command
{
return cli::test::run_construct_tests(filter.as_deref(), *release, *bless);
}
let (source, output_dir, release, optimized, output_name, trace_types) = match &cli.command {
Commands::Run {
source,
output_dir,
release,
optimized,
trace_types,
} => (
source.clone(),
output_dir.clone(),
*release || *optimized,
*optimized,
None,
*trace_types,
),
Commands::Build {
source,
output,
output_dir,
release,
optimized,
trace_types,
} => {
let mut name = output.clone().unwrap_or_else(|| {
source
.file_stem()
.and_then(|s| s.to_str())
.unwrap_or("rosy_output")
.to_string()
});
if cfg!(windows) && !name.ends_with(".exe") {
name.push_str(".exe");
}
(
source.clone(),
output_dir.clone(),
*release || *optimized,
*optimized,
Some(name),
*trace_types,
)
}
Commands::Test { .. } | Commands::Lsp { .. } | Commands::Setup { .. } => unreachable!(),
};
let binary_path = cli::compile::rosy(&source, output_dir, release, optimized, trace_types)?;
match cli.command {
Commands::Run { .. } => {
eprintln!(
"{BOLD}{CYAN} Running{RESET} {}\n",
display_path(&source)
);
let status = Command::new(&binary_path).status().with_context(|| {
format!("Failed to run binary at `{}`!", display_path(&binary_path))
})?;
ensure!(
status.success(),
"Execution failed with exit code: {:?}",
status.code()
);
}
Commands::Build { .. } => {
let destination = PathBuf::from(output_name.unwrap());
std::fs::copy(&binary_path, &destination)
.context("Failed to copy binary to current directory")?;
eprintln!(
" Binary written to {BOLD}{}{RESET}",
display_path(&destination)
);
}
Commands::Test { .. } | Commands::Lsp { .. } | Commands::Setup { .. } => unreachable!(),
}
Ok(())
}