#[macro_use]
extern crate log;
use arnalisa;
use env_logger;
use serde_json;
use snafu;
use structopt;
use arnalisa::bins::{BinDescription, SourceNames, WriteDot};
use indexmap::IndexMap;
use snafu::{Backtrace, OptionExt, ResultExt, Snafu};
use std::fs::File;
use std::path::{Path, PathBuf};
use structopt::StructOpt;
fn main() {
let mut builder = env_logger::Builder::new();
builder.parse_filters("arnalisa=info");
if let Ok(rust_log) = std::env::var("RUST_LOG") {
builder.parse_filters(&rust_log);
}
builder.init();
if let Err(err) = run(Arguments::from_args()) {
eprintln!("{}", err);
std::process::exit(1);
}
}
fn run(args: Arguments) -> Result<()> {
match args.command {
Command::Check(args) => {
let description_file = Path::new(&args.file).to_path_buf();
let description_path = description_file
.parent()
.context(InvalidParentDirectory {
path: args.file.to_string(),
})?
.to_path_buf();
let mut calibration = CalibrationResolver {
description_directory: description_path.clone(),
calibration_directory: args.calibration_path.clone(),
};
load_and_check_description(&args.file, &mut calibration)?;
Ok(())
}
Command::DumpDot(args) => {
let description = load_and_check_description(
&args.file,
&mut UnitCalibration {},
)?;
description
.write_dot(&mut std::io::stdout(), "pipeline")
.context(Arnalisa)?;
Ok(())
}
Command::Run(args) => {
let description_file = Path::new(&args.file).to_path_buf();
let description_path = description_file
.parent()
.context(InvalidParentDirectory {
path: args.file.to_string(),
})?
.to_path_buf();
let mut calibration = CalibrationResolver {
description_directory: description_path.clone(),
calibration_directory: args.calibration_path.clone(),
};
let bin_description =
load_and_check_description(&args.file, &mut calibration)?;
let source_description =
arnalisa::bins::jsonlsource::Description {
file_path: args.input.to_string(),
};
let sink_description =
arnalisa::bins::jsonlsink::Description {
file_path: args.output.clone(),
sinks: bin_description
.source_names()
.context(Arnalisa)?,
};
arnalisa::run_bin_with_calibration(
&source_description,
&bin_description,
&sink_description,
&calibration,
)
.context(Arnalisa)?;
Ok(())
}
Command::Completions(args) => {
let mut c = Arguments::clap();
c.gen_completions("arnalisa", args.shell, args.outdir);
Ok(())
}
}
}
#[derive(StructOpt, Debug)]
struct CheckArguments {
file: String,
#[structopt(long = "calibration-path", name = "PATH")]
calibration_path: Option<PathBuf>,
}
#[derive(StructOpt, Debug)]
struct DumpDotArguments {
file: String,
}
#[derive(StructOpt, Debug)]
struct RunArguments {
file: String,
input: String,
output: Option<String>,
#[structopt(long = "calibration-path", name = "PATH")]
calibration_path: Option<PathBuf>,
}
#[derive(StructOpt, Debug)]
struct CompletionsArguments {
#[structopt(long = "shell", name = "SHELL")]
shell: structopt::clap::Shell,
#[structopt(long = "outdir", name = "DIR")]
outdir: PathBuf,
}
#[derive(StructOpt, Debug)]
enum Command {
#[structopt(name = "check")]
Check(CheckArguments),
#[structopt(name = "dump-dot")]
DumpDot(DumpDotArguments),
#[structopt(name = "run")]
Run(RunArguments),
#[structopt(name = "completions")]
Completions(CompletionsArguments),
}
#[derive(StructOpt, Debug)]
#[structopt(name = "arnalisa")]
struct Arguments {
#[structopt(subcommand)]
command: Command,
}
#[derive(Clone)]
struct CalibrationResolver {
pub description_directory: PathBuf,
pub calibration_directory: Option<PathBuf>,
}
impl arnalisa::GetCalibration for CalibrationResolver {
fn calibration(
&mut self,
mode: &arnalisa::CalibrationSource,
) -> arnalisa::Result<IndexMap<arnalisa::R64, arnalisa::R64>> {
use arnalisa::CalibrationSource;
use arnalisa::R64;
match *mode {
CalibrationSource::File { ref path } => {
let file = self.description_directory.join(path);
Self::load_calibration_from_file(&file)
}
CalibrationSource::Embedded { ref curve } => Ok(curve
.iter()
.map(|&(x, y)| (R64::from(x), R64::from(y)))
.collect::<IndexMap<R64, R64>>()),
CalibrationSource::Identifier { ref id } => {
if let Some(ref dir) = self.calibration_directory {
let file =
Path::new(dir).join(id).join("calibration.json");
Self::load_calibration_from_file(&file)
} else {
arnalisa::error::GetCalibration {
message: "calibration-path parameter required"
.to_string(),
}
.fail()
}
}
}
}
}
impl CalibrationResolver {
fn load_calibration_from_file(
file: &PathBuf,
) -> arnalisa::Result<IndexMap<arnalisa::R64, arnalisa::R64>> {
info!("Loading calibration from file {:?}", file);
let file = File::open(file).context(arnalisa::error::Io)?;
let calibration: arnalisa::Calibration =
serde_json::from_reader(file)
.context(arnalisa::error::SerdeJson)?;
use arnalisa::IsCalibration;
calibration.calibration()
}
}
struct UnitCalibration {}
impl arnalisa::GetCalibration for UnitCalibration {
fn calibration(
&mut self,
_mode: &arnalisa::CalibrationSource,
) -> arnalisa::Result<IndexMap<arnalisa::R64, arnalisa::R64>> {
let calibration = vec![(0f64, 0f64), (1f64, 1f64)]
.into_iter()
.map(|(x, y)| (arnalisa::R64::from(x), arnalisa::R64::from(y)))
.collect();
Ok(calibration)
}
}
fn load_description(
path: &str,
) -> Result<arnalisa::bins::SourceSinkDescription> {
let d: arnalisa::bins::SourceSinkDescription =
serde_json::from_reader(File::open(path.to_string()).context(Io)?)
.context(SerdeJson)?;
Ok(d)
}
fn load_and_check_description(
path: &str,
get_calibration: &mut dyn arnalisa::GetCalibration,
) -> Result<arnalisa::bins::SourceSinkDescription> {
let f = load_description(path)?;
info!("Checking file: {:?}", path);
f.check_validity(
&arnalisa::Scope::File(path.to_string()),
get_calibration,
)
.context(Arnalisa)?;
info!("File {} OK.", path);
Ok(f)
}
type Result<T, E = Error> = std::result::Result<T, E>;
#[derive(Debug, Snafu)]
#[snafu(visibility(pub(crate)))]
pub enum Error {
#[snafu(display("calibration-path parameter required"))]
CalibrationPathRequired { backtrace: Backtrace },
#[snafu(display("invalid parent directory for file {:?}", path))]
InvalidParentDirectory { path: String, backtrace: Backtrace },
#[snafu(display("{}", source))]
Arnalisa {
#[snafu(backtrace)]
source: arnalisa::Error,
},
#[snafu(display("serde json error: {:?}", source))]
SerdeJson {
source: serde_json::Error,
backtrace: Backtrace,
},
#[snafu(display("i/o error: {:?}", source))]
Io {
source: std::io::Error,
backtrace: Backtrace,
},
}