use crate::output;
use crate::output::CommandOutput;
use crate::pathfinding::pathfind_route;
use clap::Parser;
use std::error::Error;
use std::path::PathBuf;
use std::process::exit;
use std::rc::Rc;
use survex_rs::data::{RefStation, SurveyData};
use survex_rs::read::load_from_path;
#[derive(Parser)]
#[command(name = "survex-dist")]
#[command(author, version, about)]
pub struct Args {
pub file: PathBuf,
#[clap(required_unless_present = "analyse")]
pub start: Option<String>,
#[clap(required_unless_present = "analyse")]
pub end: Option<String>,
#[clap(short, long)]
pub via: Vec<String>,
#[clap(short, long)]
pub avoid: Vec<String>,
#[clap(short, long, default_value = "table")]
pub format: output::Format,
#[clap(short, long)]
pub no_path: bool,
#[clap(long)]
pub analyse: bool,
}
pub fn run() -> Result<(), Box<dyn Error>> {
let start_time = std::time::Instant::now();
let args = Args::parse();
let mut data = load_from_path(args.file.clone()).unwrap_or_else(|_| {
eprintln!(
"Unable to open or read file '{}'. Is it a valid Survex 3D file?",
args.file.display()
);
exit(1);
});
if args.analyse {
run_analysis(data)?;
return Ok(());
}
let start = get_station_by_label(&data, &args.start.clone().unwrap());
let start = start.borrow();
let start_id = start.index;
let end = get_station_by_label(&data, &args.end.clone().unwrap());
let end = end.borrow();
let end_id = end.index;
let mut avoided = Vec::new();
for query in &args.avoid {
let station = get_station_by_label(&data, query);
let station = station.borrow();
data.graph.remove_node(station.index);
avoided.push(station.label.clone());
}
let mut route = vec![start_id];
let mut via = Vec::new();
for query in &args.via {
let station = get_station_by_label(&data, query);
let station = station.borrow();
via.push(station.label.clone());
route.push(station.index);
}
route.push(end_id);
let path = pathfind_route(&data, route);
let path = match path {
Some(path) => path,
None => {
eprintln!(
"Unable to find a route between '{}' and '{}'.",
args.start.unwrap(),
args.end.unwrap()
);
exit(1);
}
};
let mut route = Vec::new();
for index in path {
let station = data.get_by_index(index).unwrap();
route.push(station);
}
let output = CommandOutput::new(start_time, args, route, avoided, via);
output.print()?;
Ok(())
}
fn run_analysis(_data: SurveyData) -> Result<(), Box<dyn Error>> {
println!("Analysis not yet implemented.");
Ok(())
}
fn get_station_by_label(data: &SurveyData, query: &str) -> RefStation {
let mut matches = Vec::new();
for station in &data.stations {
let stn = station.borrow();
if stn.label == query {
return Rc::clone(station);
} else if station.borrow().label.contains(query) {
matches.push(station);
}
}
if matches.is_empty() {
eprintln!(
"There were no full or partial matches for the station name '{}'.",
query
);
eprintln!("Please check the station name is correct and try again.");
exit(1);
} else if matches.len() == 1 {
return Rc::clone(matches[0]);
} else {
eprintln!(
"There were {} possible matches for the station name '{}'.\n",
matches.len(),
query
);
if matches.len() > 20 {
eprintln!("The first 20 matches were:\n");
} else {
eprintln!("The matches were:\n");
}
for station in matches.iter().take(20) {
let stn = station.borrow();
eprintln!(" {}", stn.label);
}
eprintln!("\nPlease provide a more specific station name and try again.");
exit(1);
}
}