Add GenericError

This commit is contained in:
Sofia 2020-08-23 03:08:05 +03:00
parent fa77f315a1
commit 96c03ae7ed
4 changed files with 83 additions and 38 deletions

View File

@ -1,4 +1,4 @@
api_key = "E3GrOiQAnY61BP623XXzt9Fo87A1IQrS1FFzD57P" api_key = ""
tags_url = "https://platform.yepzon.com/tags" tags_url = "https://platform.yepzon.com/tags"
states_url = "https://platform.yepzon.com/tags/{tag}/states" states_url = "https://platform.yepzon.com/tags/{tag}/states"
locations_url = "https://platform.yepzon.com/tags/{tag}/locations/{state}" locations_url = "https://platform.yepzon.com/tags/{tag}/locations/{state}"

View File

@ -1,4 +1,5 @@
use super::Config; use super::Config;
use super::GenericError;
use chrono::format::ParseError; use chrono::format::ParseError;
use chrono::NaiveDateTime; use chrono::NaiveDateTime;
use minreq::{Error, Response}; use minreq::{Error, Response};
@ -10,6 +11,12 @@ pub trait Timestamped {
fn timestamp(&self) -> Option<String>; fn timestamp(&self) -> Option<String>;
} }
#[derive(Deserialize, Debug)]
pub struct ErrorModel {
#[serde(rename(deserialize = "Message"))]
pub message: Option<String>,
}
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct TagModel { pub struct TagModel {
pub id: Option<String>, pub id: Option<String>,
@ -70,25 +77,31 @@ impl API {
} }
} }
pub fn get_tags(&mut self) -> Result<Vec<TagModel>, Error> { pub fn get_tags(&mut self) -> Result<Vec<TagModel>, GenericError> {
let response = self.request(self.config.tags_url.clone())?; 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<Vec<StateModel>, Error> { pub fn get_states(&mut self, tag_id: &String) -> Result<Vec<StateModel>, GenericError> {
let response = self.request(str::replace(&self.config.states_url, "{tag}", &tag_id))?; let response = self.request(str::replace(&self.config.states_url, "{tag}", &tag_id))?;
response.json() Ok(response.json()?)
} }
pub fn get_locations( pub fn get_locations(
&mut self, &mut self,
tag_id: &String, tag_id: &String,
state_id: &String, state_id: &String,
) -> Result<Vec<LocationModel>, Error> { ) -> Result<Vec<LocationModel>, GenericError> {
let url = str::replace(&self.config.locations_url, "{tag}", &tag_id); let url = str::replace(&self.config.locations_url, "{tag}", &tag_id);
let url = str::replace(&url, "{state}", &state_id); let url = str::replace(&url, "{state}", &state_id);
let response = self.request(url)?; let response = self.request(url)?;
response.json() Ok(response.json()?)
} }
pub fn get_between<T: Timestamped>( pub fn get_between<T: Timestamped>(
@ -131,7 +144,7 @@ impl API {
timestamped timestamped
} }
fn request(&mut self, url: String) -> Result<Response, Error> { fn request(&mut self, url: String) -> Result<Response, GenericError> {
let before = Instant::now(); let before = Instant::now();
let response = minreq::get(url) let response = minreq::get(url)
.with_header("content-type", "application/json") .with_header("content-type", "application/json")
@ -144,7 +157,7 @@ impl API {
if min_time > duration { if min_time > duration {
sleep(min_time - duration); sleep(min_time - duration);
} }
response response.map_err(GenericError::from)
} }
fn parse_timestamp( fn parse_timestamp(

41
src/errors.rs Normal file
View File

@ -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<toml::de::Error> for GenericError {
fn from(error: toml::de::Error) -> Self {
GenericError::TomlError(error)
}
}
impl From<minreq::Error> for GenericError {
fn from(error: minreq::Error) -> Self {
GenericError::MinreqError(error)
}
}
impl From<chrono::ParseError> for GenericError {
fn from(error: chrono::ParseError) -> Self {
GenericError::ChronoParseError(error)
}
}
impl From<ErrorModel> for GenericError {
fn from(error: ErrorModel) -> Self {
GenericError::YepzonServerError(error)
}
}
impl From<io::Error> for GenericError {
fn from(error: io::Error) -> Self {
GenericError::IOError(error)
}
}

View File

@ -1,62 +1,55 @@
mod api; mod api;
mod cmd; mod cmd;
mod config; mod config;
mod errors;
use api::API; use api::API;
use chrono::{NaiveDateTime, ParseError}; use chrono::NaiveDateTime;
use cmd::*; use cmd::*;
use config::Config; use config::Config;
use errors::GenericError;
use std::fs::File; use std::fs::File;
use std::io::prelude::*; use std::io::prelude::*;
fn main() { fn main() {
let env: EnvOpt = argh::from_env(); 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 { match env.subcommand {
Subcommand::Run(opt) => { Subcommand::Run(opt) => {
let mut file = match File::open("config.toml") { let mut file = 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 string = String::new(); let mut string = String::new();
if let Err(e) = file.read_to_string(&mut string) { file.read_to_string(&mut string)?;
eprintln!("Config file data not valid UTF-8: {}", e);
return;
}
let config: Config = match toml::from_str(&string) { let config: Config = toml::from_str(&string)?;
Ok(config) => config,
Err(e) => {
eprintln!("Config file could not be parsed: {}", e);
return;
}
};
run(&config, None, None).ok(); run(&config, None, None)?;
Ok(())
} }
Subcommand::Init(opt) => { Subcommand::Init(opt) => {
let config = Config::default(); let config = Config::default();
let mut file = File::create("config.toml").unwrap(); let mut file = File::create("config.toml").unwrap();
if let Err(e) = file.write_all(&toml::to_vec(&config).unwrap()) { file.write_all(&toml::to_vec(&config).unwrap())?;
eprintln!("Could not write to file: {}", e); Ok(())
}
} }
} }
} }
fn run(config: &Config, from: Option<String>, to: Option<String>) -> Result<(), ParseError> { fn run(config: &Config, from: Option<String>, to: Option<String>) -> Result<(), GenericError> {
let mut api = API::new(config.clone()); let mut api = API::new(config.clone());
let from = get_opt(from.map(|f| NaiveDateTime::parse_from_str(&f, &config.between_format)))?; 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 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 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 states = API::get_between(state_list.clone(), from, to, true, &config);
let len = states.len(); let len = states.len();
@ -64,9 +57,7 @@ fn run(config: &Config, from: Option<String>, to: Option<String>) -> Result<(),
let mut locations = Vec::new(); let mut locations = Vec::new();
for (idx, (_, state)) in states.iter().enumerate() { for (idx, (_, state)) in states.iter().enumerate() {
println!("Expected {}s left", exp_time(&api, (len - idx) as u32)); println!("Expected {}s left", exp_time(&api, (len - idx) as u32));
let mut location_list = api let mut location_list = api.get_locations(&first_tag, state.id.as_ref().unwrap())?;
.get_locations(&first_tag, state.id.as_ref().unwrap())
.unwrap();
locations.append(&mut location_list); locations.append(&mut location_list);
} }