diff --git a/config.toml b/config.toml index 5ab2f43..9163a5e 100644 --- a/config.toml +++ b/config.toml @@ -1,4 +1,4 @@ -api_key = "E3GrOiQAnY61BP623XXzt9Fo87A1IQrS1FFzD57P" +api_key = "" tags_url = "https://platform.yepzon.com/tags" states_url = "https://platform.yepzon.com/tags/{tag}/states" locations_url = "https://platform.yepzon.com/tags/{tag}/locations/{state}" diff --git a/src/api.rs b/src/api.rs index d59213b..e91ad38 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1,4 +1,5 @@ use super::Config; +use super::GenericError; use chrono::format::ParseError; use chrono::NaiveDateTime; use minreq::{Error, Response}; @@ -10,6 +11,12 @@ pub trait Timestamped { fn timestamp(&self) -> Option; } +#[derive(Deserialize, Debug)] +pub struct ErrorModel { + #[serde(rename(deserialize = "Message"))] + pub message: Option, +} + #[derive(Deserialize, Debug)] pub struct TagModel { pub id: Option, @@ -70,25 +77,31 @@ impl API { } } - pub fn get_tags(&mut self) -> Result, Error> { + pub fn get_tags(&mut self) -> Result, GenericError> { let response = self.request(self.config.tags_url.clone())?; - response.json() + let tags = response.json(); + if let Err(_) = tags { + let err: ErrorModel = response.json()?; + Err(GenericError::from(err)) + } else { + tags.map_err(GenericError::from) + } } - pub fn get_states(&mut self, tag_id: &String) -> Result, Error> { + pub fn get_states(&mut self, tag_id: &String) -> Result, GenericError> { let response = self.request(str::replace(&self.config.states_url, "{tag}", &tag_id))?; - response.json() + Ok(response.json()?) } pub fn get_locations( &mut self, tag_id: &String, state_id: &String, - ) -> Result, Error> { + ) -> Result, GenericError> { let url = str::replace(&self.config.locations_url, "{tag}", &tag_id); let url = str::replace(&url, "{state}", &state_id); let response = self.request(url)?; - response.json() + Ok(response.json()?) } pub fn get_between( @@ -131,7 +144,7 @@ impl API { timestamped } - fn request(&mut self, url: String) -> Result { + fn request(&mut self, url: String) -> Result { let before = Instant::now(); let response = minreq::get(url) .with_header("content-type", "application/json") @@ -144,7 +157,7 @@ impl API { if min_time > duration { sleep(min_time - duration); } - response + response.map_err(GenericError::from) } fn parse_timestamp( diff --git a/src/errors.rs b/src/errors.rs new file mode 100644 index 0000000..ab709cb --- /dev/null +++ b/src/errors.rs @@ -0,0 +1,41 @@ +use super::api::ErrorModel; +use std::io; + +#[derive(Debug)] +pub enum GenericError { + TomlError(toml::de::Error), + YepzonServerError(ErrorModel), + MinreqError(minreq::Error), + ChronoParseError(chrono::ParseError), + IOError(io::Error), +} + +impl From for GenericError { + fn from(error: toml::de::Error) -> Self { + GenericError::TomlError(error) + } +} + +impl From for GenericError { + fn from(error: minreq::Error) -> Self { + GenericError::MinreqError(error) + } +} + +impl From for GenericError { + fn from(error: chrono::ParseError) -> Self { + GenericError::ChronoParseError(error) + } +} + +impl From for GenericError { + fn from(error: ErrorModel) -> Self { + GenericError::YepzonServerError(error) + } +} + +impl From for GenericError { + fn from(error: io::Error) -> Self { + GenericError::IOError(error) + } +} diff --git a/src/main.rs b/src/main.rs index 76f09b2..84d6ee4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,62 +1,55 @@ mod api; mod cmd; mod config; +mod errors; use api::API; -use chrono::{NaiveDateTime, ParseError}; +use chrono::NaiveDateTime; use cmd::*; use config::Config; +use errors::GenericError; use std::fs::File; use std::io::prelude::*; fn main() { let env: EnvOpt = argh::from_env(); + if let Err(e) = from_env(env) { + eprintln!("Error: {:?}", e); + std::process::exit(1); + } +} +fn from_env(env: EnvOpt) -> Result<(), GenericError> { match env.subcommand { Subcommand::Run(opt) => { - let mut file = match File::open("config.toml") { - Ok(file) => file, - Err(e) => { - eprintln!("Could not read config file: {}\nMake sure one exists with init subcommand.", e); - return; - } - }; + let mut file = File::open("config.toml")?; let mut string = String::new(); - if let Err(e) = file.read_to_string(&mut string) { - eprintln!("Config file data not valid UTF-8: {}", e); - return; - } + file.read_to_string(&mut string)?; - let config: Config = match toml::from_str(&string) { - Ok(config) => config, - Err(e) => { - eprintln!("Config file could not be parsed: {}", e); - return; - } - }; + let config: Config = toml::from_str(&string)?; - run(&config, None, None).ok(); + run(&config, None, None)?; + Ok(()) } Subcommand::Init(opt) => { let config = Config::default(); let mut file = File::create("config.toml").unwrap(); - if let Err(e) = file.write_all(&toml::to_vec(&config).unwrap()) { - eprintln!("Could not write to file: {}", e); - } + file.write_all(&toml::to_vec(&config).unwrap())?; + Ok(()) } } } -fn run(config: &Config, from: Option, to: Option) -> Result<(), ParseError> { +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().unwrap(); + let tags = api.get_tags()?; let first_tag = tags[0].id.clone().unwrap(); - let state_list = api.get_states(&first_tag).unwrap(); + let state_list = api.get_states(&first_tag)?; let states = API::get_between(state_list.clone(), from, to, true, &config); let len = states.len(); @@ -64,9 +57,7 @@ fn run(config: &Config, from: Option, to: Option) -> Result<(), let mut locations = Vec::new(); for (idx, (_, state)) in states.iter().enumerate() { println!("Expected {}s left", exp_time(&api, (len - idx) as u32)); - let mut location_list = api - .get_locations(&first_tag, state.id.as_ref().unwrap()) - .unwrap(); + let mut location_list = api.get_locations(&first_tag, state.id.as_ref().unwrap())?; locations.append(&mut location_list); }