use std::path::{Path, PathBuf};
use std::process::ExitCode;
use bynk::cli::{Cli, Command};
use bynk::compiler::{self, Compiler};
use bynk::dev::{self, DevOptions};
use bynk::doctor::{self, Context, DoctorOptions};
use bynk::new::{self, NewOptions};
use bynk::probe::{SystemToolbox, Toolbox, Version};
use bynk::report::{self, Format};
use clap::Parser;
fn main() -> ExitCode {
let cli = Cli::parse();
match cli.command {
Command::Doctor {
input,
only,
strict,
format,
} => run_doctor(
input,
bynk::cli::doctor_options(only, strict),
format.into(),
),
Command::Dev {
path,
context,
inspect,
inspect_port,
wrangler_args,
} => run_dev(
path,
DevOptions {
context,
inspect,
inspect_port,
wrangler_args,
},
),
Command::New { path, name } => new::run(&NewOptions { path, name }),
}
}
fn resolve_compiler(tb: &dyn Toolbox) -> Compiler {
let override_path = std::env::var_os("BYNK_BYNKC").map(PathBuf::from);
let bynk_bin_dir = std::env::current_exe()
.ok()
.and_then(|p| p.parent().map(Path::to_path_buf));
let driver = Version::parse(bynk::DRIVER_VERSION).unwrap_or(Version {
major: 0,
minor: 0,
patch: 0,
});
compiler::resolve(
tb,
override_path.as_deref(),
bynk_bin_dir.as_deref(),
driver,
)
}
fn run_doctor(input: PathBuf, opts: DoctorOptions, format: Format) -> ExitCode {
let tb = SystemToolbox;
let compiler = resolve_compiler(&tb);
let project_root = find_project_root(&input);
let ctx = Context {
in_repo: in_bynk_repo(&input),
project_root,
node_floor: bynk_emit::NODE_MAJOR_FLOOR,
};
let report = doctor::diagnose(&tb, &compiler, &ctx, &opts);
print!("{}", report::render(&report, format));
if report.exit_nonzero(&opts) {
ExitCode::FAILURE
} else {
ExitCode::SUCCESS
}
}
fn run_dev(path: PathBuf, opts: DevOptions) -> ExitCode {
let tb = SystemToolbox;
let compiler = resolve_compiler(&tb);
let Some(project_root) = find_project_root(&path) else {
eprintln!(
"bynk: not inside a Bynk project (no bynk.toml found from `{}`)",
path.display()
);
return ExitCode::FAILURE;
};
let src_rel = bynk_emit::project::read_project_paths(&project_root)
.include
.into_iter()
.next()
.unwrap_or_else(|| std::path::PathBuf::from("."));
dev::run(
&tb,
&compiler,
&project_root,
&src_rel,
bynk_emit::NODE_MAJOR_FLOOR,
&opts,
)
}
fn find_project_root(start: &Path) -> Option<PathBuf> {
let mut dir = start.canonicalize().ok()?;
loop {
if dir.join("bynk.toml").is_file() {
return Some(dir);
}
if !dir.pop() {
return None;
}
}
}
fn in_bynk_repo(start: &Path) -> bool {
let Ok(mut dir) = start.canonicalize() else {
return false;
};
loop {
if dir.join("bynkc").join("Cargo.toml").is_file()
&& dir.join("design").join("decisions").is_dir()
{
return true;
}
if !dir.pop() {
return false;
}
}
}