164 lines
4.8 KiB
Rust
164 lines
4.8 KiB
Rust
use super::Config;
|
|
use chrono::format::ParseError;
|
|
use chrono::NaiveDateTime;
|
|
use minreq::{Error, Response};
|
|
use serde::Deserialize;
|
|
use std::thread::sleep;
|
|
use std::time::{Duration, Instant};
|
|
|
|
pub trait Timestamped {
|
|
fn timestamp(&self) -> Option<String>;
|
|
}
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
pub struct TagModel {
|
|
pub id: Option<String>,
|
|
pub imei: Option<String>,
|
|
#[serde(rename(deserialize = "batteryValue"))]
|
|
pub battery_value: Option<i32>,
|
|
#[serde(rename(deserialize = "btId"))]
|
|
pub bt_id: Option<String>,
|
|
#[serde(rename(deserialize = "nfcId"))]
|
|
pub nfc_id: Option<String>,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, Clone)]
|
|
pub struct StateModel {
|
|
pub id: Option<String>,
|
|
pub timestamp: Option<String>,
|
|
#[serde(skip_deserializing)]
|
|
pub state: Option<String>,
|
|
#[serde(rename(deserialize = "realState"))]
|
|
pub real_state: Option<String>,
|
|
}
|
|
|
|
impl Timestamped for StateModel {
|
|
fn timestamp(&self) -> Option<String> {
|
|
self.timestamp.clone()
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, Clone)]
|
|
#[serde(deny_unknown_fields)]
|
|
pub struct LocationModel {
|
|
pub timestamp: Option<String>,
|
|
#[serde(rename(deserialize = "type"))]
|
|
pub loc_type: Option<String>,
|
|
#[serde(rename(deserialize = "coordinateLat"))]
|
|
pub lat: Option<f32>,
|
|
#[serde(rename(deserialize = "coordinateLng"))]
|
|
pub lon: Option<f32>,
|
|
pub accuracy: Option<f32>,
|
|
}
|
|
|
|
impl Timestamped for LocationModel {
|
|
fn timestamp(&self) -> Option<String> {
|
|
self.timestamp.clone()
|
|
}
|
|
}
|
|
|
|
pub struct API {
|
|
config: Config,
|
|
pub last_response_ping: u32,
|
|
}
|
|
|
|
impl API {
|
|
pub fn new(config: Config) -> API {
|
|
API {
|
|
last_response_ping: config.throttle,
|
|
config: config,
|
|
}
|
|
}
|
|
|
|
pub fn get_tags(&mut self) -> Result<Vec<TagModel>, Error> {
|
|
let response = self.request(self.config.tags_url.clone())?;
|
|
response.json()
|
|
}
|
|
|
|
pub fn get_states(&mut self, tag_id: &String) -> Result<Vec<StateModel>, Error> {
|
|
let response = self.request(str::replace(&self.config.states_url, "{tag}", &tag_id))?;
|
|
response.json()
|
|
}
|
|
|
|
pub fn get_locations(
|
|
&mut self,
|
|
tag_id: &String,
|
|
state_id: &String,
|
|
) -> Result<Vec<LocationModel>, Error> {
|
|
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()
|
|
}
|
|
|
|
pub fn get_between<T: Timestamped>(
|
|
list: Vec<T>,
|
|
from: Option<NaiveDateTime>,
|
|
to: Option<NaiveDateTime>,
|
|
inclusive_from: bool,
|
|
config: &Config,
|
|
) -> Vec<(NaiveDateTime, T)> {
|
|
let mut timestamped = Vec::new();
|
|
for item in list {
|
|
if let Some(res) = API::parse_timestamp(&item.timestamp(), config) {
|
|
match res {
|
|
Ok(t) => {
|
|
if let Some(to) = to {
|
|
if t > to {
|
|
continue;
|
|
}
|
|
}
|
|
let mut too_old = false;
|
|
if let Some(from) = from {
|
|
if t < from {
|
|
too_old = true;
|
|
}
|
|
}
|
|
if too_old {
|
|
if inclusive_from {
|
|
timestamped.push((t.clone(), item));
|
|
}
|
|
break;
|
|
}
|
|
timestamped.push((t.clone(), item));
|
|
}
|
|
Err(e) => panic!(e),
|
|
}
|
|
} else {
|
|
println!("Skipped item, did not have timestmap");
|
|
}
|
|
}
|
|
timestamped
|
|
}
|
|
|
|
fn request(&mut self, url: String) -> Result<Response, Error> {
|
|
let before = Instant::now();
|
|
let response = minreq::get(url)
|
|
.with_header("content-type", "application/json")
|
|
.with_header("x-api-key", &self.config.api_key)
|
|
.send();
|
|
let after = Instant::now();
|
|
let min_time = Duration::new(0, self.config.throttle * 1_000_000);
|
|
let duration = after.duration_since(before);
|
|
self.last_response_ping = (duration.as_millis() as u32).max(self.config.throttle);
|
|
if min_time > duration {
|
|
sleep(min_time - duration);
|
|
}
|
|
response
|
|
}
|
|
|
|
fn parse_timestamp(
|
|
timestamp: &Option<String>,
|
|
config: &Config,
|
|
) -> Option<Result<NaiveDateTime, ParseError>> {
|
|
if let Some(timestamp) = ×tamp {
|
|
Some(NaiveDateTime::parse_from_str(
|
|
×tamp,
|
|
&config.timestamp_format,
|
|
))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|