use std::{
fmt::{self},
str::FromStr,
thread, time,
};
use chrono::{DateTime, Local, NaiveDate};
use crate::{
camera::{Camera, CameraFeatures},
errors::WardError,
parse,
};
#[derive(Clone)]
pub struct Car {
pub number: String,
pub begin: Option<NaiveDate>,
pub end: Option<NaiveDate>,
pub notify: Notify,
pub note: Option<String>,
}
#[derive(Debug, Clone)]
pub struct Event {
pub time: Option<DateTime<Local>>,
pub car_number: String,
pub status: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Notify {
On,
Off,
}
impl FromStr for Notify {
type Err = String;
fn from_str(input: &str) -> Result<Notify, Self::Err> {
match input.to_lowercase().as_str() {
"on" => Ok(Notify::On),
"off" => Ok(Notify::Off),
&_ => Ok(Notify::Off),
}
}
}
impl fmt::Display for Notify {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
&Notify::On => write!(f, "on"),
&Notify::Off => write!(f, "off"),
}
}
}
pub trait CarFeatures {
fn list_cars(&self) -> Result<Vec<Car>, WardError>;
fn add_car(&self, car: &Car) -> Result<(), WardError>;
fn add_cars<'a>(&'a self, cars: &'a Vec<Car>) -> Result<Vec<&str>, WardError>;
fn edit_car(&self, car: &Car) -> Result<(), WardError>;
fn remove_car(&self, number: &str) -> Result<(), WardError>;
fn remove_cars(&self, date: Option<NaiveDate>) -> Result<Vec<String>, WardError>;
fn events(
&self,
begin_date: &str,
end_date: &str,
stauts: Option<bool>,
) -> Result<Vec<Event>, WardError>;
}
impl CarFeatures for Camera {
fn list_cars(&self) -> Result<Vec<Car>, WardError> {
let path = "/lnpr_cgi?action=list";
let response = &self.send_request(&path)?;
response.check_status_code()?;
Ok(parse::raw_numbers(&response.body))
}
fn add_car(&self, car: &Car) -> Result<(), WardError> {
let path = build_path("add", car);
Ok(self.send_request(&path)?.check_status_code()?)
}
fn add_cars<'a>(&'a self, cars: &'a Vec<Car>) -> Result<Vec<&str>, WardError> {
let mut successfully_added_cars = vec![];
cars.into_iter().for_each(|car| {
if self.add_car(&car).is_ok() {
successfully_added_cars.push(car.number.as_str())
}
});
Ok(successfully_added_cars)
}
fn edit_car(&self, car: &Car) -> Result<(), WardError> {
let path = build_path("edit", car);
Ok(self.send_request(&path)?.check_status_code()?)
}
fn remove_car(&self, number: &str) -> Result<(), WardError> {
let path = format!("/lnpr_cgi?action=remove&Number={}", number);
Ok(self.send_request(&path)?.check_status_code()?)
}
fn remove_cars(&self, date: Option<NaiveDate>) -> Result<Vec<String>, WardError> {
let mut successfully_removed_cars = vec![];
let mut cars_to_remove = vec![];
let cars = self.list_cars()?;
let cars_with_end_date = cars.iter().filter(|car| car.end.is_some());
cars_with_end_date.into_iter().for_each(|car| match date {
Some(naive_date) => {
if Some(naive_date) == car.end {
cars_to_remove.push(car);
}
}
None => {
let now = Local::now();
let now_date = now.date_naive();
if Some(now_date) != car.end {
cars_to_remove.push(car);
}
}
});
cars_to_remove.into_iter().for_each(|car| {
if self.remove_car(&car.number).is_ok() {
successfully_removed_cars.push(car.clone().number);
thread::sleep(time::Duration::from_millis(100));
}
});
Ok(successfully_removed_cars)
}
fn events(
&self,
begin_date: &str,
end_date: &str,
status: Option<bool>,
) -> Result<Vec<Event>, WardError> {
let path = format!("/lnprevent_cgi?action=export&Begin={begin_date}&End={end_date}");
let response = &self.send_request(&path)?;
response.check_status_code()?;
Ok(parse::events(&response.body, status)?)
}
}
fn build_path(action: &str, car: &Car) -> String {
format!(
"/lnpr_cgi?action={}&Number={}&Begin={}&End={}&Notify={}&Note={}",
action,
car.number,
parse::str_from_date(car.begin),
parse::str_from_date(car.end),
car.notify,
car.note.as_ref().unwrap_or(&"".to_string())
)
}
#[cfg(test)]
mod tests {
use chrono::NaiveDate;
use super::{build_path, Car, Notify};
#[test]
fn building_path() {
let path = build_path(
"add",
&Car {
number: "111".to_string(),
begin: Some(NaiveDate::default()),
end: None,
notify: Notify::Off,
note: Some("Some Comment".to_string()),
},
);
assert_eq!(
path,
"/lnpr_cgi?action=add&Number=111&Begin=1970-01-01&End=&Notify=off&Note=Some Comment"
);
}
}