mod api; mod cmd; mod config; mod errors; use api::API; use chrono::NaiveDateTime; use cmd::*; use config::Config; use errors::{GenericError, MessagedError}; use std::fs::File; use std::io::prelude::*; use std::sync::mpsc::TryRecvError; use std::time::Duration; fn main() { let env: EnvOpt = argh::from_env(); if let Err(e) = from_env(env) { eprintln!("Critical Error: {}", e); std::process::exit(1); } } fn from_env(env: EnvOpt) -> Result<(), GenericError> { match env.subcommand { Subcommand::Run(opt) => { let mut file = File::open(&opt.config).with_msg(format!( "Could not find {}", opt.config.to_str().unwrap_or("") ))?; let mut string = String::new(); file.read_to_string(&mut string) .with_msg("config file is not valid UTF-8")?; let config: Config = toml::from_str(&string).with_msg("given config file is not a valid config file")?; run( &config, Some("1.1.2015 00:00:00".to_owned()), Some("1.5.2021 00:00:00".to_owned()), )?; Ok(()) } Subcommand::Init(_) => { let config = Config::default(); let mut file = File::create("config.toml").unwrap(); file.write_all(&toml::to_vec(&config).unwrap()) .with_msg("Could not write config.toml, make sure you have correct permissions.")?; Ok(()) } } } fn run(config: &Config, from: Option, to: Option) -> Result<(), GenericError> { let mut api = API::new(config.clone()); let from = get_opt(from.map(|f| NaiveDateTime::parse_from_str(&f, &config.between_format)))?; let to = get_opt(to.map(|t| NaiveDateTime::parse_from_str(&t, &config.between_format)))?; let tags = api.get_tags()?; let first_tag = tags[0].id.clone().unwrap(); let state_list = api.get_states(&first_tag)?; let states = API::get_between(&state_list, from, to, true, &config); let mut locations = Vec::new(); for (_, state) in states.iter() { api.queue_location(&first_tag, state.id.as_ref().unwrap()); } let receiver = api.begin_location_fetch(); let mut counter = 0; loop { match receiver.try_recv() { Ok(res) => match res { Ok(loc) => { counter += 1; let remaining = exp_time(&api, states.len() as u64 - counter as u64); print!( "Done: {:<5.2}% Remaining: {:<5.2} seconds\r", counter as f32 / states.len() as f32 * 100., remaining.as_secs_f32() ); std::io::stdout().lock().flush().ok(); match loc { Ok(mut loc) => locations.append(&mut loc), Err(e) => eprintln!( "Error fetching location data: {}", e.message.unwrap_or(String::new()) ), } } Err(e) => eprintln!("{:?}", e), }, Err(e) => { if let TryRecvError::Disconnected = e { break; } } } } println!("{:<100}", "Done!"); locations.sort_by(|loc1, loc2| loc2.timestamp.cmp(&loc1.timestamp)); let _locs = API::get_between(&locations, from, to, false, &config); /*dbg!( &locs.iter().map(|loc| loc.0).collect::>(), locs.len(), locations.len() );*/ Ok(()) } fn get_opt(option: Option>) -> Result, B> { if let Some(res) = option { match res { Ok(a) => Ok(Some(a)), Err(b) => Err(b), } } else { Ok(None) } } fn exp_time(api: &API, reqs_left: u64) -> Duration { let interval = 1_000 / api.config.throttle as u64; Duration::from_millis(interval * reqs_left) }