use fitparser;
use serde::Serialize;
use std::collections::BTreeMap;
use std::error::Error;
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::iter::FromIterator;
use std::path::PathBuf;
use structopt::StructOpt;
#[derive(Debug, StructOpt)]
#[structopt(name = "fit_to_json")]
struct Cli {
#[structopt(name = "FILE", parse(from_os_str))]
files: Vec<PathBuf>,
#[structopt(short, long, parse(from_os_str))]
output: Option<PathBuf>,
}
#[derive(Clone, Debug, Serialize)]
struct FitDataMap {
kind: fitparser::profile::MesgNum,
fields: BTreeMap<String, fitparser::ValueWithUnits>,
}
impl FitDataMap {
fn new(record: fitparser::FitDataRecord) -> Self {
FitDataMap {
kind: record.kind(),
fields: BTreeMap::from_iter(
record
.into_vec()
.into_iter()
.map(|f| (f.name().to_owned(), fitparser::ValueWithUnits::from(f))),
),
}
}
}
#[derive(Clone, Debug)]
enum OutputLocation {
Inplace,
LocalDirectory(PathBuf),
LocalFile(PathBuf),
Stdout,
}
impl OutputLocation {
fn new(location: PathBuf) -> Self {
if location.is_dir() {
OutputLocation::LocalDirectory(location)
} else if location.as_os_str() == "-" {
OutputLocation::Stdout
} else {
OutputLocation::LocalFile(location)
}
}
fn write_json_file(
&self,
filename: &PathBuf,
data: Vec<fitparser::FitDataRecord>,
) -> Result<(), Box<dyn Error>> {
let data: Vec<FitDataMap> = data.into_iter().map(|r| FitDataMap::new(r)).collect();
let json = serde_json::to_string(&data)?;
let outname = match self {
Self::Inplace => filename.with_extension("json"),
Self::LocalDirectory(dest) => dest
.clone()
.join(filename.file_name().unwrap())
.with_extension("json"),
Self::LocalFile(dest) => dest.clone(),
Self::Stdout => {
println!("{}", json);
return Ok(());
}
};
let mut fp = File::create(outname)?;
match fp.write_all(json.as_bytes()) {
Ok(_) => Ok(()),
Err(e) => Err(Box::new(e)),
}
}
}
fn run() -> Result<(), Box<dyn Error>> {
let opt = Cli::from_args();
let output_loc = opt
.output
.map_or(OutputLocation::Inplace, OutputLocation::new);
let collect_all = match output_loc {
OutputLocation::LocalFile(_) => true,
_ => false,
};
if opt.files.is_empty() {
let mut stdin = io::stdin();
let data = fitparser::from_reader(&mut stdin)?;
output_loc.write_json_file(&PathBuf::from("<stdin>"), data)?;
return Ok(());
}
let mut all_fit_data: Vec<fitparser::FitDataRecord> = Vec::new();
for file in opt.files {
let mut fp = File::open(&file)?;
let mut data = fitparser::from_reader(&mut fp)?;
if collect_all {
all_fit_data.append(&mut data);
} else {
output_loc.write_json_file(&file, data)?;
}
}
if collect_all {
output_loc.write_json_file(&PathBuf::new(), all_fit_data)?;
}
Ok(())
}
fn main() {
std::process::exit(match run() {
Ok(_) => 0,
Err(err) => {
eprintln!("{}", err);
1
}
});
}