pub mod api; mod config; mod errors; use api::{LocationModel, SharetokenModel, StateModel, TagModel, API}; use chrono::NaiveDateTime; use errors::LibError; use std::io::prelude::*; use std::path::PathBuf; 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(path: &PathBuf, api_key: Option) -> Result<(), LibError> { let mut config = Config::default(); if let Some(api_key) = api_key { config.api_key = api_key; } Ok(config.write_to(path)?) } pub fn between( api: &mut API, tag_str: String, from: Option, to: Option, ) -> 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 { 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, 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, ) -> Result, 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, 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, config: &Config) -> Result { let mut tag = None; if let Ok(num) = tag_str.parse::() { 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, )), } }