mod artifact;
mod bbox;
mod coils;
mod compound;
mod cut;
mod magnet;
mod strip_netcdf;
mod validate;
mod vessel;
mod vmec;
#[allow(dead_code, unused_imports, unused_variables, unexpected_cfgs)]
mod openapi;
mod api;
use clap::{Parser, Subcommand};
use std::path::PathBuf;
pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
#[derive(Parser, Debug)]
#[command(about = "alphastell — VMEC 由来の CAD 生成と検証")]
struct Cli {
#[command(subcommand)]
command: Command,
}
#[derive(Subcommand, Debug)]
enum Command {
Vessel {
#[arg(long)]
input: PathBuf,
#[arg(long)]
output: PathBuf,
#[arg(long)]
scale: f64,
},
#[command(group(
clap::ArgGroup::new("op")
.required(true)
.multiple(false)
.args(["cut", "union"])
))]
Cut {
#[arg(short = 'i', long)]
input: PathBuf,
#[arg(short = 'o', long)]
output: PathBuf,
#[arg(short = 's', long, default_value = "0", value_parser = cut::parse_tau_fraction, allow_hyphen_values = true)]
start: f64,
#[arg(short = 'e', long, value_parser = cut::parse_tau_fraction, allow_hyphen_values = true)]
end: f64,
#[arg(short = 'c', long)]
cut: bool,
#[arg(short = 'u', long)]
union: bool,
},
Magnet {
#[arg(long)]
input: PathBuf,
#[arg(long)]
output: PathBuf,
#[arg(long, default_value_t = 0.4)]
width: f64,
#[arg(long, default_value_t = 0.5)]
thickness: f64,
#[arg(long, default_value_t = 360.0)]
toroidal_extent: f64,
#[arg(long)]
scale: f64,
},
Compound {
#[arg(short = 'i', long = "input")]
inputs: Vec<PathBuf>,
#[arg(short = 'o', long)]
output: PathBuf,
},
Validate {
a: PathBuf,
b: PathBuf,
#[arg(long, default_value_t = 4)]
max_ratio: u32,
#[arg(long, default_value_t = 0.01)]
tol: f64,
#[arg(long, default_value_t = false)]
union: bool,
},
Server{
#[arg(long, env = "PORT", default_value = "8080")]
port: u16,
#[arg(long, env = "PORT_FRONTEND", default_value = "8070")]
port_frontend: u16,
},
Bbox {
#[arg(required = true)]
inputs: Vec<PathBuf>,
},
StripNetcdf {
#[arg(long)]
input: PathBuf,
#[arg(long)]
output: PathBuf,
#[arg(long, required = true)]
include: Vec<String>,
},
}
fn main() -> Result<()> {
let cli = Cli::parse();
match cli.command {
Command::Vessel {
input,
output,
scale,
} => {
println!("Loading VMEC: {}", input.display());
let file = std::fs::File::open(&input)
.map_err(|e| format!("open {}: {}", input.display(), e))?;
for a in vessel::run(file, scale)? {
a.write(&output, &a.name)?;
}
println!("Done.");
Ok(())
}
Command::Cut {
input,
output,
start,
end,
cut,
union,
} => {
let mode = if cut {
cut::Mode::Intersect
} else {
debug_assert!(union, "clap ArgGroup guarantees exactly one of --cut / --union");
cut::Mode::Subtract
};
cut::run(&input, &output, start, end, mode)
}
Command::Magnet {
input,
output,
width,
thickness,
toroidal_extent,
scale,
} => {
println!("Parsing coils: {}", input.display());
let file = std::fs::File::open(&input)
.map_err(|e| format!("open {}: {}", input.display(), e))?;
for a in magnet::run(file, width, thickness, toroidal_extent, scale)? {
a.write(&output, &a.name)?;
}
println!("Done.");
Ok(())
}
Command::Compound {
inputs,
output,
} => compound::run(&inputs, Vec::new(), &output),
Command::Validate {
a,
b,
max_ratio,
tol,
union,
} => validate::run(&a, &b, max_ratio, tol, union),
Command::Server {
port,
port_frontend,
} => {
api::run(port, port_frontend);
Ok(())
}
Command::Bbox { inputs } => bbox::run(&inputs),
Command::StripNetcdf { input, output, include } => {
strip_netcdf::run(&input, &output, &include)
}
}
}