210 lines
6.5 KiB
Rust
210 lines
6.5 KiB
Rust
pub mod api;
|
|
mod config;
|
|
mod errors;
|
|
|
|
use api::{LocationModel, SharetokenModel, StateModel, TagModel, API};
|
|
use chrono::offset::Local;
|
|
use chrono::{NaiveDate, NaiveDateTime, NaiveTime, ParseError};
|
|
use errors::LibError;
|
|
use std::io::prelude::*;
|
|
use std::sync::mpsc::TryRecvError;
|
|
use std::time::Duration;
|
|
|
|
pub use chrono;
|
|
pub use config::Config;
|
|
pub use errors::LibError as Error;
|
|
pub use errors::MessagedError;
|
|
|
|
pub fn init(api_key: Option<String>) -> Result<Config, LibError> {
|
|
let mut config = Config::default();
|
|
if let Some(api_key) = api_key {
|
|
config.api_key = api_key;
|
|
}
|
|
Ok(config)
|
|
}
|
|
|
|
pub fn between(
|
|
api: &mut API,
|
|
tag_str: String,
|
|
from: Option<NaiveDateTime>,
|
|
to: Option<NaiveDateTime>,
|
|
) -> Result<(TagModel, Vec<(NaiveDateTime, LocationModel)>), LibError> {
|
|
let tags = api.get_tags()?;
|
|
let tag = find_tag(tag_str, &tags, &api.config)?;
|
|
let tag_id = tag.get_id()?;
|
|
|
|
print!("Preparing..\r");
|
|
std::io::stdout().lock().flush().ok();
|
|
|
|
let state_list = api.get_states(&tag_id)?;
|
|
|
|
let states = API::get_between(&state_list, from, to, true, &api.config);
|
|
|
|
let mut locations = Vec::new();
|
|
for (_, state) in states.iter() {
|
|
api.queue_location(&tag_id, 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) => match loc {
|
|
Ok(mut 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();
|
|
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}", "\nDone!");
|
|
locations.sort_by(|loc1, loc2| loc2.timestamp.cmp(&loc1.timestamp));
|
|
|
|
let locs = API::get_between(&locations, from, to, false, &api.config);
|
|
|
|
Ok((tag, locs))
|
|
}
|
|
|
|
pub fn check_api(api: &API) -> Result<(), LibError> {
|
|
let tags = api.get_tags()?;
|
|
if let Some(tag) = tags.get(0) {
|
|
let tag_id = tag.get_id()?;
|
|
|
|
api.get_current_locations(&tag_id)?;
|
|
api.get_sharetoken(&tag_id)?;
|
|
|
|
if let Some(state) = api.get_states(&tag_id)?.get(0) {
|
|
let state_id = state.get_id()?;
|
|
api.get_locations(&tag_id, &state_id)?;
|
|
Ok(())
|
|
} else {
|
|
Err(LibError::MessagedError(
|
|
"Could not find any states for the first device found.".to_owned(),
|
|
None,
|
|
))
|
|
}
|
|
} else {
|
|
Err(LibError::MessagedError("Could not find any devices. Please make sure this API-key is paired with a device first.".to_owned(), None))
|
|
}
|
|
}
|
|
|
|
pub fn sharetoken(api: &API, tag: String) -> Result<SharetokenModel, LibError> {
|
|
let tags = api.get_tags()?;
|
|
let tag = find_tag(tag, &tags, &api.config)?;
|
|
Ok(api.get_sharetoken(&tag.get_id()?)?)
|
|
}
|
|
|
|
pub fn nick_list(api: &API) -> Result<Vec<((usize, String), TagModel)>, LibError> {
|
|
let mut tags = api.get_tags()?;
|
|
tags.sort_by(|tag1, tag2| tag1.id.cmp(&tag2.id));
|
|
let list = tags
|
|
.iter()
|
|
.enumerate()
|
|
.map(|(idx, tag)| {
|
|
(
|
|
(
|
|
idx + 1,
|
|
tag.get_nick(&api.config)
|
|
.unwrap_or("No current nick".to_owned()),
|
|
),
|
|
tag.clone(),
|
|
)
|
|
})
|
|
.collect();
|
|
Ok(list)
|
|
}
|
|
|
|
pub fn nick_set(api: &mut API, tag_str: String, nickname: String) -> Result<(), LibError> {
|
|
let mut tags = api.get_tags()?;
|
|
tags.sort_by(|tag1, tag2| tag1.id.cmp(&tag2.id));
|
|
let tag = find_tag(tag_str.clone(), &tags, &api.config)?;
|
|
if let Some(id) = &tag.id {
|
|
api.config.nicknames.insert(id.clone(), nickname);
|
|
Ok(())
|
|
} else {
|
|
Err(LibError::MessagedError(
|
|
format!("Device {} does not have an id", tag_str),
|
|
None,
|
|
))
|
|
}
|
|
}
|
|
|
|
pub fn get_locations(
|
|
api: &API,
|
|
tag: String,
|
|
state: Option<String>,
|
|
) -> Result<Vec<LocationModel>, LibError> {
|
|
let tags = api.get_tags()?;
|
|
let tag = find_tag(tag, &tags, &api.config)?;
|
|
if let Some(state) = state {
|
|
Ok(api.get_locations(&tag.get_id()?, &state)?)
|
|
} else {
|
|
Ok(api.get_current_locations(&tag.get_id()?)?)
|
|
}
|
|
}
|
|
|
|
pub fn get_states(api: &API, tag: String) -> Result<Vec<StateModel>, LibError> {
|
|
let tags = api.get_tags()?;
|
|
let tag = find_tag(tag, &tags, &api.config)?;
|
|
Ok(api.get_states(&tag.get_id()?)?)
|
|
}
|
|
|
|
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 find_tag(tag_str: String, tags: &Vec<TagModel>, config: &Config) -> Result<TagModel, LibError> {
|
|
let mut tag = None;
|
|
if let Ok(num) = tag_str.parse::<i32>() {
|
|
if let Some(found) = tags.get((num - 1).max(0) as usize) {
|
|
tag = Some(found.clone());
|
|
}
|
|
}
|
|
for curr in tags.iter() {
|
|
if let Some(nick) = curr.get_nick(config) {
|
|
if nick == tag_str {
|
|
tag = Some(curr.clone());
|
|
}
|
|
}
|
|
}
|
|
match tag {
|
|
Some(tag) => Ok(tag),
|
|
None => Err(LibError::MessagedError(
|
|
format!("Could not find device {}", tag_str),
|
|
None,
|
|
)),
|
|
}
|
|
}
|
|
|
|
pub 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),
|
|
},
|
|
},
|
|
}
|
|
}
|