Separate lib and ux

This commit is contained in:
Sofia 2020-08-29 02:27:23 +03:00
parent 2b835a72b0
commit b1b8ac417b
12 changed files with 196 additions and 139 deletions

5
.gitignore vendored
View File

@ -1,3 +1,6 @@
/target /target
secret.toml secret.toml
*.gpx *.gpx
*.code-workspace
/thingy_lib/Cargo.lock
/thingy_lib/target/

9
Cargo.lock generated
View File

@ -371,9 +371,16 @@ name = "thingy"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"argh", "argh",
"chrono",
"geo-types", "geo-types",
"gpx", "gpx",
"thingy-lib",
]
[[package]]
name = "thingy-lib"
version = "0.1.0"
dependencies = [
"chrono",
"minreq", "minreq",
"serde", "serde",
"toml", "toml",

View File

@ -5,10 +5,12 @@ authors = ["Teascade <teascade@gmail.com>"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
serde = { version = "1.0", features = ["derive"] }
toml = "0.5"
minreq = { version = "2.2.0", features = ["https", "json-using-serde"] }
chrono = "0.4"
argh = "0.1" argh = "0.1"
gpx = "0.8" gpx = "0.8"
geo-types = "*" geo-types = "*"
thingy-lib = { path = "./thingy_lib/" }
[target.x86_64-pc-windows-msvc]
rustflags = ["-Ctarget-feature=+crt-static"]
[target.i686-pc-windows-msvc]
rustflags = ["-Ctarget-feature=+crt-static"]

View File

@ -1,52 +1,15 @@
use super::api::ErrorModel;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::io;
#[derive(Debug)] #[derive(Debug)]
pub enum GenericError { pub enum GenericError {
TomlError(toml::de::Error), ThingyLibError(thingy_lib::Error),
YepzonServerError(ErrorModel),
MinreqError(minreq::Error),
ChronoParseError(chrono::ParseError),
IOError(io::Error),
FromUTF8Error(std::string::FromUtf8Error),
GPXError(gpx::errors::Error), GPXError(gpx::errors::Error),
MessagedError(String, Option<Box<GenericError>>), MessagedError(String, Option<Box<GenericError>>),
} }
impl From<toml::de::Error> for GenericError { impl From<thingy_lib::Error> for GenericError {
fn from(error: toml::de::Error) -> Self { fn from(error: thingy_lib::Error) -> Self {
GenericError::TomlError(error) GenericError::ThingyLibError(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)
}
}
impl From<std::string::FromUtf8Error> for GenericError {
fn from(error: std::string::FromUtf8Error) -> Self {
GenericError::FromUTF8Error(error)
} }
} }
@ -59,21 +22,13 @@ impl From<gpx::errors::Error> for GenericError {
impl Display for GenericError { impl Display for GenericError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
let err = match self { let err = match self {
GenericError::ChronoParseError(e) => format!("Date-Time value parse error: {}", e), GenericError::ThingyLibError(e) => e.to_string(),
GenericError::FromUTF8Error(e) => format!("UTF-8 error: {}", e), GenericError::GPXError(e) => format!("GPX error: {}", e),
GenericError::IOError(e) => format!("IO error: {}", e),
GenericError::MinreqError(e) => format!("Network error: {}", e),
GenericError::TomlError(e) => format!("Toml error: {}", e),
GenericError::YepzonServerError(e) => format!(
"Yepzon server error: {}",
e.message.as_ref().unwrap_or(&String::new())
),
GenericError::MessagedError(msg, err) => format!( GenericError::MessagedError(msg, err) => format!(
"{}\n {}", "{}\n {}",
msg, msg,
err.as_ref().map(|e| e.to_string()).unwrap_or(String::new()) err.as_ref().map(|e| e.to_string()).unwrap_or(String::new())
), ),
GenericError::GPXError(e) => format!("GPX error: {}", e),
}; };
write!(f, "{}", err) write!(f, "{}", err)
} }

View File

@ -1,9 +1,9 @@
use super::{Config, GenericError, LocationModel, TagModel}; use super::{Config, GenericError, LocationModel, TagModel};
use chrono::offset::Utc as UTC_ZONE;
use chrono::{DateTime, NaiveDateTime, Utc};
use geo_types::Point; use geo_types::Point;
use gpx::{Gpx, GpxVersion, Metadata, Person, Route, Track, TrackSegment, Waypoint}; use gpx::{Gpx, GpxVersion, Metadata, Person, Route, Track, TrackSegment, Waypoint};
use std::fs::File; use std::fs::File;
use thingy_lib::chrono::offset::Utc as UTC_ZONE;
use thingy_lib::chrono::{DateTime, NaiveDateTime, Utc};
pub fn generate_gpx( pub fn generate_gpx(
tag: &TagModel, tag: &TagModel,

View File

@ -1,16 +1,15 @@
mod api; #![windows_subsystem = "windows"]
mod args; mod args;
mod commands;
mod config;
mod errors; mod errors;
mod gpx; mod gpx;
use api::{LocationModel, TagModel, API};
use args::*; use args::*;
use chrono::offset::Local; use errors::GenericError;
use chrono::{NaiveDate, NaiveDateTime, NaiveTime, ParseError}; use thingy_lib::api::{LocationModel, TagModel, API};
use config::Config; use thingy_lib::chrono::offset::Local;
use errors::{GenericError, MessagedError}; use thingy_lib::chrono::{NaiveDate, NaiveDateTime, NaiveTime, ParseError};
use thingy_lib::{Config, MessagedError};
fn main() { fn main() {
let env: EnvOpt = argh::from_env(); let env: EnvOpt = argh::from_env();
@ -36,7 +35,11 @@ fn from_env(env: EnvOpt) -> Result<(), GenericError> {
None => None, None => None,
}; };
let (_, locs) = commands::between(&mut api, opt.device, since, until)?; let (tag, locs) = thingy_lib::between(&mut api, opt.device, since, until)?;
let gpx = gpx::generate_gpx(&tag, &locs, &api.config)?;
gpx::write_gpx(&gpx)?;
dbg!( dbg!(
&locs.iter().map(|loc| loc.0).collect::<Vec<NaiveDateTime>>(), &locs.iter().map(|loc| loc.0).collect::<Vec<NaiveDateTime>>(),
locs.len(), locs.len(),
@ -44,27 +47,27 @@ fn from_env(env: EnvOpt) -> Result<(), GenericError> {
Ok(()) Ok(())
} }
Subcommand::Init(opt) => { Subcommand::Init(opt) => {
commands::init(&env.config, opt.api_key)?; thingy_lib::init(&env.config, opt.api_key)?;
Ok(()) Ok(())
} }
Subcommand::Api(_) => { Subcommand::Api(_) => {
let config = Config::from_path(&env.config)?; let config = Config::from_path(&env.config)?;
let api = API::new(config.clone()); let api = API::new(config.clone());
match commands::check_api(&api) { match thingy_lib::check_api(&api) {
Ok(_) => { Ok(_) => {
println!("API verified, no issues"); println!("API verified, no issues");
Ok(()) Ok(())
} }
Err(e) => Err(GenericError::MessagedError( Err(e) => Err(GenericError::MessagedError(
"API integrity failed, or API-key is not valid. ".to_owned(), "API integrity failed, or API-key is not valid. ".to_owned(),
Some(Box::new(e)), Some(Box::new(e.into())),
)), )),
} }
} }
Subcommand::ShareToken(opt) => { Subcommand::ShareToken(opt) => {
let config = Config::from_path(&env.config)?; let config = Config::from_path(&env.config)?;
let api = API::new(config.clone()); let api = API::new(config.clone());
println!("{}", commands::sharetoken(&api, opt.device)?); println!("{}", thingy_lib::sharetoken(&api, opt.device)?);
Ok(()) Ok(())
} }
Subcommand::Nick(opt) => { Subcommand::Nick(opt) => {
@ -73,12 +76,13 @@ fn from_env(env: EnvOpt) -> Result<(), GenericError> {
match opt.subcommand { match opt.subcommand {
NickSub::List(_) => { NickSub::List(_) => {
for ((idx, nick), tag) in commands::nick_list(&api)? { for ((idx, nick), tag) in thingy_lib::nick_list(&api)? {
println!("{}. {}\n{}", idx, nick, tag); println!("{}. {}\n{}", idx, nick, tag);
} }
} }
NickSub::Set(opt) => { NickSub::Set(opt) => {
commands::nick_set(&mut api, &env.config, opt.device, opt.nickname)?; thingy_lib::nick_set(&mut api, opt.device, opt.nickname)?;
api.config.write_to(&env.config)?;
} }
} }
Ok(()) Ok(())
@ -98,10 +102,10 @@ fn from_env(env: EnvOpt) -> Result<(), GenericError> {
dbg!(&tags); dbg!(&tags);
} }
GetSub::States(opt) => { GetSub::States(opt) => {
API::print_list(commands::get_states(&api, opt.device)); API::print_list(thingy_lib::get_states(&api, opt.device));
} }
GetSub::Locations(opt) => { GetSub::Locations(opt) => {
API::print_list(commands::get_locations(&api, opt.device, opt.state)) API::print_list(thingy_lib::get_locations(&api, opt.device, opt.state))
} }
} }
Ok(()) Ok(())

11
thingy_lib/Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
[package]
name = "thingy-lib"
version = "0.1.0"
authors = ["Teascade <teascade@gmail.com>"]
edition = "2018"
[dependencies]
serde = { version = "1.0", features = ["derive"] }
toml = "0.5"
minreq = { version = "2.2.0", features = ["https", "json-using-serde"] }
chrono = "0.4"

View File

@ -1,7 +1,7 @@
pub mod structs; pub mod structs;
use super::Config; use super::Config;
use super::GenericError; use super::LibError;
use chrono::format::ParseError; use chrono::format::ParseError;
use chrono::NaiveDateTime; use chrono::NaiveDateTime;
use minreq::Response; use minreq::Response;
@ -25,15 +25,14 @@ impl API {
} }
} }
#[allow(dead_code)] pub fn get_raw_string(&self, url: APIUrl) -> Result<String, LibError> {
pub fn get_raw_string(&self, url: APIUrl) -> Result<String, GenericError> {
Ok(self Ok(self
.request(API::api_url(url, &self.config))? .request(API::api_url(url, &self.config))?
.as_str()? .as_str()?
.to_owned()) .to_owned())
} }
pub fn get_sharetoken(&self, tag_id: &String) -> Result<SharetokenModel, GenericError> { pub fn get_sharetoken(&self, tag_id: &String) -> Result<SharetokenModel, LibError> {
let response = self.request(API::api_url( let response = self.request(API::api_url(
APIUrl::Sharetoken(tag_id.clone()), APIUrl::Sharetoken(tag_id.clone()),
&self.config, &self.config,
@ -41,33 +40,28 @@ impl API {
Ok(response.json()?) Ok(response.json()?)
} }
#[allow(dead_code)] pub fn get_tags(&self) -> Result<Vec<TagModel>, LibError> {
pub fn get_tags(&self) -> Result<Vec<TagModel>, GenericError> {
let response = self.request(API::api_url(APIUrl::Tags, &self.config))?; let response = self.request(API::api_url(APIUrl::Tags, &self.config))?;
let tags = response.json(); let tags = response.json();
if let Err(_) = tags { if let Err(_) = tags {
let err: ErrorModel = response.json()?; let err: ErrorModel = response.json()?;
Err(GenericError::from(err)) Err(LibError::from(err))
} else { } else {
tags.map_err(GenericError::from) tags.map_err(LibError::from)
} }
} }
#[allow(dead_code)] pub fn get_tag(&self, tag_id: &String) -> Result<TagModel, LibError> {
pub fn get_tag(&self, tag_id: &String) -> Result<TagModel, GenericError> {
let response = self.request(API::api_url(APIUrl::Tag(tag_id.clone()), &self.config))?; let response = self.request(API::api_url(APIUrl::Tag(tag_id.clone()), &self.config))?;
Ok(response.json()?) Ok(response.json()?)
} }
pub fn get_states(&self, tag_id: &String) -> Result<Vec<StateModel>, GenericError> { pub fn get_states(&self, tag_id: &String) -> Result<Vec<StateModel>, LibError> {
let response = self.request(API::api_url(APIUrl::States(tag_id.clone()), &self.config))?; let response = self.request(API::api_url(APIUrl::States(tag_id.clone()), &self.config))?;
Ok(response.json()?) Ok(response.json()?)
} }
pub fn get_current_locations( pub fn get_current_locations(&self, tag_id: &String) -> Result<Vec<LocationModel>, LibError> {
&self,
tag_id: &String,
) -> Result<Vec<LocationModel>, GenericError> {
let response = self.request(API::api_url( let response = self.request(API::api_url(
APIUrl::CurrLocations(tag_id.clone()), APIUrl::CurrLocations(tag_id.clone()),
&self.config, &self.config,
@ -79,7 +73,7 @@ impl API {
&self, &self,
tag_id: &String, tag_id: &String,
state_id: &String, state_id: &String,
) -> Result<Vec<LocationModel>, GenericError> { ) -> Result<Vec<LocationModel>, LibError> {
let response = self.request(API::api_url( let response = self.request(API::api_url(
APIUrl::Locations(tag_id.clone(), state_id.clone()), APIUrl::Locations(tag_id.clone(), state_id.clone()),
&self.config, &self.config,
@ -96,7 +90,7 @@ impl API {
pub fn begin_location_fetch( pub fn begin_location_fetch(
&mut self, &mut self,
) -> Receiver<Result<Result<Vec<LocationModel>, ErrorModel>, GenericError>> { ) -> Receiver<Result<Result<Vec<LocationModel>, ErrorModel>, LibError>> {
let (sender, receiver) = mpsc::channel(); let (sender, receiver) = mpsc::channel();
let mut locations = self.location_req_que.clone(); let mut locations = self.location_req_que.clone();
let config = self.config.clone(); let config = self.config.clone();
@ -139,7 +133,7 @@ impl API {
Ok(loc) => Ok(Ok(loc)), Ok(loc) => Ok(Ok(loc)),
Err(_) => Ok(Err(r.json()?)), Err(_) => Ok(Err(r.json()?)),
}) })
.map_err(GenericError::from); .map_err(LibError::from);
i_sender.send((location, response)).ok(); i_sender.send((location, response)).ok();
}); });
timer += interval; timer += interval;
@ -169,7 +163,7 @@ impl API {
receiver receiver
} }
pub fn get_between<T: Timestamped + Clone>( pub(crate) fn get_between<T: Timestamped + Clone>(
list: &Vec<T>, list: &Vec<T>,
from: Option<NaiveDateTime>, from: Option<NaiveDateTime>,
to: Option<NaiveDateTime>, to: Option<NaiveDateTime>,
@ -210,7 +204,7 @@ impl API {
} }
#[allow(dead_code)] #[allow(dead_code)]
pub fn print_list<T: Display + Timestamped>(list: Result<Vec<T>, GenericError>) { pub fn print_list<T: Display + Timestamped>(list: Result<Vec<T>, LibError>) {
match list { match list {
Ok(items) => { Ok(items) => {
for item in items { for item in items {
@ -243,7 +237,7 @@ impl API {
fn add_error_back( fn add_error_back(
location: String, location: String,
rec: &Result<Result<Vec<LocationModel>, ErrorModel>, GenericError>, rec: &Result<Result<Vec<LocationModel>, ErrorModel>, LibError>,
locations: &mut Vec<String>, locations: &mut Vec<String>,
) -> bool { ) -> bool {
if let Ok(inner) = rec { if let Ok(inner) = rec {
@ -258,12 +252,12 @@ impl API {
} }
} }
fn request(&self, url: String) -> Result<Response, GenericError> { fn request(&self, url: String) -> Result<Response, LibError> {
let response = minreq::get(url) let response = minreq::get(url)
.with_header("content-type", "application/json") .with_header("content-type", "application/json")
.with_header("x-api-key", &self.config.api_key) .with_header("x-api-key", &self.config.api_key)
.send(); .send();
response.map_err(GenericError::from) response.map_err(LibError::from)
} }
fn parse_timestamp( fn parse_timestamp(

View File

@ -1,4 +1,4 @@
use super::{Config, GenericError}; use super::{Config, LibError};
use serde::Deserialize; use serde::Deserialize;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
@ -77,10 +77,10 @@ impl TagModel {
} }
} }
pub fn get_id(&self) -> Result<String, GenericError> { pub fn get_id(&self) -> Result<String, LibError> {
match &self.id { match &self.id {
Some(id) => Ok(id.to_string()), Some(id) => Ok(id.to_string()),
None => Err(GenericError::MessagedError( None => Err(LibError::MessagedError(
"Could not find device id. Error probably on Yetzon server side.".to_owned(), "Could not find device id. Error probably on Yetzon server side.".to_owned(),
None, None,
)), )),
@ -153,10 +153,10 @@ pub struct StateModel {
} }
impl StateModel { impl StateModel {
pub fn get_id(&self) -> Result<String, GenericError> { pub fn get_id(&self) -> Result<String, LibError> {
match &self.id { match &self.id {
Some(id) => Ok(id.to_string()), Some(id) => Ok(id.to_string()),
None => Err(GenericError::MessagedError( None => Err(LibError::MessagedError(
"Could not find state id. Error probably on Yetzon server side.".to_owned(), "Could not find state id. Error probably on Yetzon server side.".to_owned(),
None, None,
)), )),

View File

@ -1,4 +1,4 @@
use super::{GenericError, MessagedError}; use super::errors::{LibError, MessagedError};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashMap; use std::collections::HashMap;
use std::fs::File; use std::fs::File;
@ -27,7 +27,7 @@ pub struct Config {
} }
impl Config { impl Config {
pub fn from_path(path: &PathBuf) -> Result<Config, GenericError> { pub fn from_path(path: &PathBuf) -> Result<Config, LibError> {
let mut file = File::open(&path) let mut file = File::open(&path)
.with_msg(format!("Could not find {}", path.to_str().unwrap_or("")))?; .with_msg(format!("Could not find {}", path.to_str().unwrap_or("")))?;
let mut string = String::new(); let mut string = String::new();
@ -37,7 +37,7 @@ impl Config {
Ok(toml::from_str(&string).with_msg("given config file is not a valid config file")?) Ok(toml::from_str(&string).with_msg("given config file is not a valid config file")?)
} }
pub fn write_to(&self, path: &PathBuf) -> Result<(), GenericError> { pub fn write_to(&self, path: &PathBuf) -> Result<(), LibError> {
let mut file = File::create(&path).unwrap(); let mut file = File::create(&path).unwrap();
file.write_all(&toml::to_vec(self).unwrap()) file.write_all(&toml::to_vec(self).unwrap())
.with_msg("Could not write config.toml, make sure you have correct permissions.")?; .with_msg("Could not write config.toml, make sure you have correct permissions.")?;

88
thingy_lib/src/errors.rs Normal file
View File

@ -0,0 +1,88 @@
use super::api::ErrorModel;
use std::fmt::{Display, Formatter};
use std::io;
#[derive(Debug)]
pub enum LibError {
TomlError(toml::de::Error),
YepzonServerError(ErrorModel),
MinreqError(minreq::Error),
ChronoParseError(chrono::ParseError),
IOError(io::Error),
FromUTF8Error(std::string::FromUtf8Error),
MessagedError(String, Option<Box<LibError>>),
}
impl From<toml::de::Error> for LibError {
fn from(error: toml::de::Error) -> Self {
LibError::TomlError(error)
}
}
impl From<minreq::Error> for LibError {
fn from(error: minreq::Error) -> Self {
LibError::MinreqError(error)
}
}
impl From<chrono::ParseError> for LibError {
fn from(error: chrono::ParseError) -> Self {
LibError::ChronoParseError(error)
}
}
impl From<ErrorModel> for LibError {
fn from(error: ErrorModel) -> Self {
LibError::YepzonServerError(error)
}
}
impl From<io::Error> for LibError {
fn from(error: io::Error) -> Self {
LibError::IOError(error)
}
}
impl From<std::string::FromUtf8Error> for LibError {
fn from(error: std::string::FromUtf8Error) -> Self {
LibError::FromUTF8Error(error)
}
}
impl Display for LibError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
let err = match self {
LibError::ChronoParseError(e) => format!("Date-Time value parse error: {}", e),
LibError::FromUTF8Error(e) => format!("UTF-8 error: {}", e),
LibError::IOError(e) => format!("IO error: {}", e),
LibError::MinreqError(e) => format!("Network error: {}", e),
LibError::TomlError(e) => format!("Toml error: {}", e),
LibError::YepzonServerError(e) => format!(
"Yepzon server error: {}",
e.message.as_ref().unwrap_or(&String::new())
),
LibError::MessagedError(msg, err) => format!(
"{}\n {}",
msg,
err.as_ref().map(|e| e.to_string()).unwrap_or(String::new())
),
};
write!(f, "{}", err)
}
}
pub trait MessagedError<A> {
fn with_msg<T: Into<String>>(self, text: T) -> Result<A, LibError>;
}
impl<A, B: Into<LibError>> MessagedError<A> for Result<A, B> {
fn with_msg<T: Into<String>>(self, text: T) -> Result<A, LibError> {
match self {
Ok(ok) => Ok(ok),
Err(e) => Err(LibError::MessagedError(
text.into(),
Some(Box::new(e.into())),
)),
}
}
}

View File

@ -1,14 +1,21 @@
use super::api::{LocationModel, SharetokenModel, StateModel, TagModel, API}; pub mod api;
use super::config::Config; mod config;
use super::errors::GenericError; mod errors;
use super::gpx;
use api::{LocationModel, SharetokenModel, StateModel, TagModel, API};
use chrono::NaiveDateTime; use chrono::NaiveDateTime;
use errors::LibError;
use std::io::prelude::*; use std::io::prelude::*;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::mpsc::TryRecvError; use std::sync::mpsc::TryRecvError;
use std::time::Duration; use std::time::Duration;
pub fn init(path: &PathBuf, api_key: Option<String>) -> Result<(), GenericError> { 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<String>) -> Result<(), LibError> {
let mut config = Config::default(); let mut config = Config::default();
if let Some(api_key) = api_key { if let Some(api_key) = api_key {
config.api_key = api_key; config.api_key = api_key;
@ -21,7 +28,7 @@ pub fn between(
tag_str: String, tag_str: String,
from: Option<NaiveDateTime>, from: Option<NaiveDateTime>,
to: Option<NaiveDateTime>, to: Option<NaiveDateTime>,
) -> Result<(TagModel, Vec<(NaiveDateTime, LocationModel)>), GenericError> { ) -> Result<(TagModel, Vec<(NaiveDateTime, LocationModel)>), LibError> {
let tags = api.get_tags()?; let tags = api.get_tags()?;
let tag = find_tag(tag_str, &tags, &api.config)?; let tag = find_tag(tag_str, &tags, &api.config)?;
let tag_id = tag.get_id()?; let tag_id = tag.get_id()?;
@ -73,12 +80,10 @@ pub fn between(
let locs = API::get_between(&locations, from, to, false, &api.config); let locs = API::get_between(&locations, from, to, false, &api.config);
let gpx = gpx::generate_gpx(&tag, &locs, &api.config)?;
gpx::write_gpx(&gpx)?;
Ok((tag, locs)) Ok((tag, locs))
} }
pub fn check_api(api: &API) -> Result<(), GenericError> { pub fn check_api(api: &API) -> Result<(), LibError> {
let tags = api.get_tags()?; let tags = api.get_tags()?;
if let Some(tag) = tags.get(0) { if let Some(tag) = tags.get(0) {
let tag_id = tag.get_id()?; let tag_id = tag.get_id()?;
@ -91,23 +96,23 @@ pub fn check_api(api: &API) -> Result<(), GenericError> {
api.get_locations(&tag_id, &state_id)?; api.get_locations(&tag_id, &state_id)?;
Ok(()) Ok(())
} else { } else {
Err(GenericError::MessagedError( Err(LibError::MessagedError(
"Could not find any states for the first device found.".to_owned(), "Could not find any states for the first device found.".to_owned(),
None, None,
)) ))
} }
} else { } else {
Err(GenericError::MessagedError("Could not find any devices. Please make sure this API-key is paired with a device first.".to_owned(), None)) 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, GenericError> { pub fn sharetoken(api: &API, tag: String) -> Result<SharetokenModel, LibError> {
let tags = api.get_tags()?; let tags = api.get_tags()?;
let tag = find_tag(tag, &tags, &api.config)?; let tag = find_tag(tag, &tags, &api.config)?;
Ok(api.get_sharetoken(&tag.get_id()?)?) Ok(api.get_sharetoken(&tag.get_id()?)?)
} }
pub fn nick_list(api: &API) -> Result<Vec<((usize, String), TagModel)>, GenericError> { pub fn nick_list(api: &API) -> Result<Vec<((usize, String), TagModel)>, LibError> {
let mut tags = api.get_tags()?; let mut tags = api.get_tags()?;
tags.sort_by(|tag1, tag2| tag1.id.cmp(&tag2.id)); tags.sort_by(|tag1, tag2| tag1.id.cmp(&tag2.id));
let list = tags let list = tags
@ -127,33 +132,26 @@ pub fn nick_list(api: &API) -> Result<Vec<((usize, String), TagModel)>, GenericE
Ok(list) Ok(list)
} }
pub fn nick_set( pub fn nick_set(api: &mut API, tag_str: String, nickname: String) -> Result<(), LibError> {
api: &mut API,
config_path: &PathBuf,
tag_str: String,
nickname: String,
) -> Result<(), GenericError> {
let mut tags = api.get_tags()?; let mut tags = api.get_tags()?;
tags.sort_by(|tag1, tag2| tag1.id.cmp(&tag2.id)); tags.sort_by(|tag1, tag2| tag1.id.cmp(&tag2.id));
let tag = find_tag(tag_str.clone(), &tags, &api.config)?; let tag = find_tag(tag_str.clone(), &tags, &api.config)?;
if let Some(id) = &tag.id { if let Some(id) = &tag.id {
api.config.nicknames.insert(id.clone(), nickname); api.config.nicknames.insert(id.clone(), nickname);
api.config.write_to(config_path)?;
Ok(()) Ok(())
} else { } else {
Err(GenericError::MessagedError( Err(LibError::MessagedError(
format!("Device {} does not have an id", tag_str), format!("Device {} does not have an id", tag_str),
None, None,
)) ))
} }
} }
#[allow(dead_code)]
pub fn get_locations( pub fn get_locations(
api: &API, api: &API,
tag: String, tag: String,
state: Option<String>, state: Option<String>,
) -> Result<Vec<LocationModel>, GenericError> { ) -> Result<Vec<LocationModel>, LibError> {
let tags = api.get_tags()?; let tags = api.get_tags()?;
let tag = find_tag(tag, &tags, &api.config)?; let tag = find_tag(tag, &tags, &api.config)?;
if let Some(state) = state { if let Some(state) = state {
@ -163,8 +161,7 @@ pub fn get_locations(
} }
} }
#[allow(dead_code)] pub fn get_states(api: &API, tag: String) -> Result<Vec<StateModel>, LibError> {
pub fn get_states(api: &API, tag: String) -> Result<Vec<StateModel>, GenericError> {
let tags = api.get_tags()?; let tags = api.get_tags()?;
let tag = find_tag(tag, &tags, &api.config)?; let tag = find_tag(tag, &tags, &api.config)?;
Ok(api.get_states(&tag.get_id()?)?) Ok(api.get_states(&tag.get_id()?)?)
@ -175,11 +172,7 @@ fn exp_time(api: &API, reqs_left: u64) -> Duration {
Duration::from_millis(interval * reqs_left) Duration::from_millis(interval * reqs_left)
} }
fn find_tag( fn find_tag(tag_str: String, tags: &Vec<TagModel>, config: &Config) -> Result<TagModel, LibError> {
tag_str: String,
tags: &Vec<TagModel>,
config: &Config,
) -> Result<TagModel, GenericError> {
let mut tag = None; let mut tag = None;
if let Ok(num) = tag_str.parse::<i32>() { if let Ok(num) = tag_str.parse::<i32>() {
if let Some(found) = tags.get((num - 1).max(0) as usize) { if let Some(found) = tags.get((num - 1).max(0) as usize) {
@ -195,7 +188,7 @@ fn find_tag(
} }
match tag { match tag {
Some(tag) => Ok(tag), Some(tag) => Ok(tag),
None => Err(GenericError::MessagedError( None => Err(LibError::MessagedError(
format!("Could not find device {}", tag_str), format!("Could not find device {}", tag_str),
None, None,
)), )),