148 lines
4.6 KiB
Rust
148 lines
4.6 KiB
Rust
mod api;
|
|
mod cmd;
|
|
mod config;
|
|
mod errors;
|
|
|
|
use api::API;
|
|
use chrono::offset::Local;
|
|
use chrono::{NaiveDate, NaiveDateTime, NaiveTime, ParseError};
|
|
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::Since(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")?;
|
|
|
|
let since = Some(try_get_datetime(opt.since, &config)?);
|
|
|
|
let until = match opt.until {
|
|
Some(until) => Some(try_get_datetime(until, &config)?),
|
|
None => None,
|
|
};
|
|
|
|
run(&config, since, until)?;
|
|
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<NaiveDateTime>,
|
|
to: Option<NaiveDateTime>,
|
|
) -> Result<(), GenericError> {
|
|
let mut api = API::new(config.clone());
|
|
|
|
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::<Vec<NaiveDateTime>>(),
|
|
locs.len(),
|
|
locations.len()
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
fn get_opt<A, B>(option: Option<Result<A, B>>) -> Result<Option<A>, 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)
|
|
}
|
|
|
|
fn try_get_datetime(parsed: String, config: &Config) -> Result<NaiveDateTime, ParseError> {
|
|
match NaiveDateTime::parse_from_str(&parsed, &config.timedate_format) {
|
|
Ok(timedate) => Ok(timedate),
|
|
Err(_) => match NaiveDate::parse_from_str(&parsed, &config.date_format) {
|
|
Ok(date) => Ok(date.and_hms(0, 0, 0)),
|
|
Err(_) => match NaiveTime::parse_from_str(&parsed, &config.time_format) {
|
|
Ok(time) => Ok(Local::today().naive_local().and_time(time)),
|
|
Err(e) => Err(e),
|
|
},
|
|
},
|
|
}
|
|
}
|