yepzon-locationer/src/main.rs

148 lines
4.6 KiB
Rust
Raw Normal View History

2020-08-23 00:53:49 +02:00
mod api;
2020-08-23 01:18:31 +02:00
mod cmd;
2020-08-23 00:53:49 +02:00
mod config;
2020-08-23 02:08:05 +02:00
mod errors;
2020-08-23 00:53:49 +02:00
use api::API;
2020-08-24 02:33:40 +02:00
use chrono::offset::Local;
use chrono::{NaiveDate, NaiveDateTime, NaiveTime, ParseError};
2020-08-23 01:18:31 +02:00
use cmd::*;
2020-08-23 00:53:49 +02:00
use config::Config;
2020-08-24 01:17:50 +02:00
use errors::{GenericError, MessagedError};
2020-08-23 00:53:49 +02:00
use std::fs::File;
use std::io::prelude::*;
2020-08-23 19:54:34 +02:00
use std::sync::mpsc::TryRecvError;
2020-08-24 01:40:05 +02:00
use std::time::Duration;
2020-08-23 00:53:49 +02:00
fn main() {
2020-08-23 01:18:31 +02:00
let env: EnvOpt = argh::from_env();
2020-08-23 02:08:05 +02:00
if let Err(e) = from_env(env) {
2020-08-24 01:17:50 +02:00
eprintln!("Critical Error: {}", e);
2020-08-23 02:08:05 +02:00
std::process::exit(1);
}
}
2020-08-23 00:53:49 +02:00
2020-08-23 02:08:05 +02:00
fn from_env(env: EnvOpt) -> Result<(), GenericError> {
2020-08-23 01:18:31 +02:00
match env.subcommand {
2020-08-24 02:33:40 +02:00
Subcommand::Since(opt) => {
2020-08-24 01:51:59 +02:00
let mut file = File::open(&opt.config).with_msg(format!(
"Could not find {}",
opt.config.to_str().unwrap_or("")
))?;
2020-08-23 01:18:31 +02:00
let mut string = String::new();
2020-08-24 01:17:50 +02:00
file.read_to_string(&mut string)
2020-08-24 01:51:59 +02:00
.with_msg("config file is not valid UTF-8")?;
2020-08-23 01:18:31 +02:00
2020-08-24 01:17:50 +02:00
let config: Config =
2020-08-24 01:51:59 +02:00
toml::from_str(&string).with_msg("given config file is not a valid config file")?;
2020-08-23 01:18:31 +02:00
2020-08-24 02:33:40 +02:00
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)?;
2020-08-23 02:08:05 +02:00
Ok(())
2020-08-23 01:18:31 +02:00
}
2020-08-24 01:17:50 +02:00
Subcommand::Init(_) => {
2020-08-23 01:18:31 +02:00
let config = Config::default();
let mut file = File::create("config.toml").unwrap();
2020-08-24 01:17:50 +02:00
file.write_all(&toml::to_vec(&config).unwrap())
.with_msg("Could not write config.toml, make sure you have correct permissions.")?;
2020-08-23 02:08:05 +02:00
Ok(())
2020-08-23 01:18:31 +02:00
}
}
2020-08-23 00:53:49 +02:00
}
2020-08-24 02:33:40 +02:00
fn run(
config: &Config,
from: Option<NaiveDateTime>,
to: Option<NaiveDateTime>,
) -> Result<(), GenericError> {
2020-08-23 00:53:49 +02:00
let mut api = API::new(config.clone());
2020-08-23 02:08:05 +02:00
let tags = api.get_tags()?;
2020-08-23 00:53:49 +02:00
let first_tag = tags[0].id.clone().unwrap();
2020-08-23 02:08:05 +02:00
let state_list = api.get_states(&first_tag)?;
2020-08-23 00:53:49 +02:00
2020-08-24 00:36:47 +02:00
let states = API::get_between(&state_list, from, to, true, &config);
2020-08-23 00:53:49 +02:00
2020-08-24 00:36:47 +02:00
let mut locations = Vec::new();
2020-08-23 19:54:34 +02:00
for (_, state) in states.iter() {
api.queue_location(&first_tag, state.id.as_ref().unwrap());
}
let receiver = api.begin_location_fetch();
2020-08-24 00:36:47 +02:00
let mut counter = 0;
2020-08-23 19:54:34 +02:00
loop {
match receiver.try_recv() {
Ok(res) => match res {
Ok(loc) => {
2020-08-24 00:36:47 +02:00
counter += 1;
2020-08-24 01:40:05 +02:00
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()
2020-08-24 00:36:47 +02:00
);
2020-08-24 01:40:05 +02:00
std::io::stdout().lock().flush().ok();
2020-08-24 00:36:47 +02:00
match loc {
Ok(mut loc) => locations.append(&mut loc),
2020-08-24 01:40:05 +02:00
Err(e) => eprintln!(
2020-08-24 01:17:50 +02:00
"Error fetching location data: {}",
e.message.unwrap_or(String::new())
),
2020-08-24 00:36:47 +02:00
}
2020-08-23 19:54:34 +02:00
}
Err(e) => eprintln!("{:?}", e),
},
Err(e) => {
if let TryRecvError::Disconnected = e {
break;
}
}
}
2020-08-23 00:53:49 +02:00
}
2020-08-24 01:40:05 +02:00
println!("{:<100}", "Done!");
2020-08-24 00:36:47 +02:00
locations.sort_by(|loc1, loc2| loc2.timestamp.cmp(&loc1.timestamp));
2020-08-23 00:53:49 +02:00
2020-08-24 02:33:40 +02:00
let locs = API::get_between(&locations, from, to, false, &config);
dbg!(
2020-08-24 00:36:47 +02:00
&locs.iter().map(|loc| loc.0).collect::<Vec<NaiveDateTime>>(),
locs.len(),
locations.len()
2020-08-24 02:33:40 +02:00
);
2020-08-23 00:53:49 +02:00
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)
}
}
2020-08-24 01:40:05 +02:00
fn exp_time(api: &API, reqs_left: u64) -> Duration {
let interval = 1_000 / api.config.throttle as u64;
Duration::from_millis(interval * reqs_left)
2020-08-23 00:53:49 +02:00
}
2020-08-24 02:33:40 +02:00
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),
},
},
}
}