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"
states_url = "https://platform.yepzon.com/tags/{tag}/states"
locations_url = "https://platform.yepzon.com/tags/{tag}/locations/{state}"

View File

@ -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<String>;
}
#[derive(Deserialize, Debug)]
pub struct ErrorModel {
#[serde(rename(deserialize = "Message"))]
pub message: Option<String>,
}
#[derive(Deserialize, Debug)]
pub struct TagModel {
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())?;
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))?;
response.json()
Ok(response.json()?)
}
pub fn get_locations(
&mut self,
tag_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(&url, "{state}", &state_id);
let response = self.request(url)?;
response.json()
Ok(response.json()?)
}
pub fn get_between<T: Timestamped>(
@ -131,7 +144,7 @@ impl API {
timestamped
}
fn request(&mut self, url: String) -> Result<Response, Error> {
fn request(&mut self, url: String) -> Result<Response, GenericError> {
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(

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 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<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 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<String>, to: Option<String>) -> 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);
}