use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Coordinates {
pub lat: f64,
pub long: f64,
}
impl Coordinates {
pub fn new(lat: f64, long: f64) -> Self {
Coordinates { lat, long }
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Location {
pub start: Coordinates,
pub end: Coordinates,
}
impl Location {
pub fn new(start_lat: f64, start_lang: f64, end_lat: f64, end_lang: f64) -> Self {
Location {
start: Coordinates::new(start_lat, start_lang),
end: Coordinates::new(end_lat, end_lang),
}
}
pub fn from_coords(start: Coordinates, end: Coordinates) -> Self {
Location { start, end }
}
}
pub mod regex {
use lazy_static::lazy_static;
use regex::Regex;
lazy_static! {
pub static ref BUS_FREQ_RE: Regex =
Regex::new(r"^(\d{1,3})?-?(\d{1,3})?$").unwrap();
pub static ref CARPARK_COORDS_RE: Regex =
Regex::new(r"^([+-]?([0-9]*[.])?[0-9]+) ([+-]?([0-9]*[.])?[0-9]+)$").unwrap();
pub static ref SPEED_BAND_RE: Regex =
Regex::new(r"^([+-]?([0-9]*[.])?[0-9]+) ([+-]?([0-9]*[.])?[0-9]+) ([+-]?([0-9]*[.])?[0-9]+) ([+-]?([0-9]*[.])?[0-9]+)$")
.unwrap();
}
}
pub mod serde_date {
pub mod ymd_hms_option {
use lazy_static::lazy_static;
use serde::{Deserialize, Deserializer, Serializer};
use time::{
format_description::{self, FormatItem},
OffsetDateTime,
};
lazy_static! {
static ref FORMAT: Vec<FormatItem<'static>> = format_description::parse_borrowed::<2>(
"[year]-[month]-[day] [hour]:[minute]:[second]"
)
.unwrap();
}
pub fn serialize<S>(date: &Option<OffsetDateTime>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match date {
Some(dt) => {
let s = dt.format(&FORMAT).unwrap();
serializer.serialize_str(&s)
}
None => serializer.serialize_none(),
}
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<OffsetDateTime>, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
OffsetDateTime::parse(&s, &FORMAT)
.map(Some)
.map_err(serde::de::Error::custom)
}
}
pub mod str_time_option {
use serde::{Deserialize, Deserializer, Serializer};
use time::Time;
pub fn ser_str_time_opt<S>(
opt_time: &Option<Time>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match opt_time {
Some(time) => {
let hr = time.hour();
let min = time.minute();
let mut sec_str = String::with_capacity(1);
sec_str.push('0');
let s = [hr.to_string(), min.to_string(), sec_str].join(":");
serializer.serialize_str(&s)
}
None => serializer.serialize_none(),
}
}
pub fn de_str_time_opt_erp<'de, D>(deserializer: D) -> Result<Option<Time>, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
if s.eq("-") {
return Ok(None);
}
let hr = &mut s[0..1].parse().map_err(serde::de::Error::custom)?;
let min = &s[3..4].parse().map_err(serde::de::Error::custom)?;
if *hr == 24 {
*hr = 0
}
Time::from_hms(*hr, *min, 0)
.map(Some)
.map_err(serde::de::Error::custom)
}
pub fn de_str_time_opt_br<'de, D>(deserializer: D) -> Result<Option<Time>, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
if s.eq("-") {
return Ok(None);
}
let hr = &mut s[0..1].parse().map_err(serde::de::Error::custom)?;
let min = &s[2..3].parse().map_err(serde::de::Error::custom)?;
if *hr == 24 {
*hr = 0
}
Time::from_hms(*hr, *min, 0)
.map(Some)
.map_err(serde::de::Error::custom)
}
}
pub mod str_date {
use lazy_static::lazy_static;
use serde::{Deserialize, Deserializer, Serializer};
use time::{
format_description::{self, FormatItem},
Date,
};
lazy_static! {
static ref FORMAT: Vec<FormatItem<'static>> =
format_description::parse_borrowed::<2>("[year]-[month]-[day]").unwrap();
}
pub fn serialize<S>(date: &Date, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let s = date.format(&FORMAT).unwrap();
serializer.serialize_str(&s)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Date, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Date::parse(&s, &FORMAT).map_err(serde::de::Error::custom)
}
}
}
pub mod de {
use std::fmt;
use std::fmt::Display;
use std::iter::FromIterator;
use std::marker::PhantomData as Phantom;
use std::str::FromStr;
use crate::utils::{regex::*, Coordinates};
#[cfg(not(feature = "fastfloat"))]
use crate::utils::Location;
use serde::de::{self, Visitor};
use serde::{Deserialize, Deserializer};
use serde_json::Value;
use std::fmt::Formatter;
pub struct WrapErr;
pub trait Sep {
fn delimiter() -> &'static str;
}
impl Display for WrapErr {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "StringWrapErr")
}
}
pub fn treat_error_as_none<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
where
T: Deserialize<'de>,
D: Deserializer<'de>,
{
let value: Value = Deserialize::deserialize(deserializer)?;
Ok(T::deserialize(value).ok())
}
pub fn from_str_to_bool<'de, D>(deserializer: D) -> Result<bool, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
match s.as_ref() {
"Y" | "Yes" => Ok(true),
_ => Ok(false),
}
}
pub fn from_str_to_coords<'de, D>(deserializer: D) -> Result<Option<Coordinates>, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
if s.is_empty() || !CARPARK_COORDS_RE.is_match(s.as_str()) {
return Ok(None);
}
let caps = CARPARK_COORDS_RE.captures(&s).unwrap();
let lat = caps.get(1).map_or(0.0, |m| m.as_str().parse().unwrap());
let long = caps.get(3).map_or(0.0, |m| m.as_str().parse().unwrap());
Ok(Some(Coordinates::new(lat, long)))
}
#[cfg(not(feature = "fastfloat"))]
pub fn from_str_loc_to_loc<'de, D>(deserializer: D) -> Result<Option<Location>, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
if s.is_empty() || !SPEED_BAND_RE.is_match(s.as_str()) {
return Ok(None);
}
let caps = SPEED_BAND_RE.captures(&s).unwrap();
let lat_start = caps.get(1).map_or(0.0, |m| m.as_str().parse().unwrap());
let long_start = caps.get(3).map_or(0.0, |m| m.as_str().parse().unwrap());
let lat_end = caps.get(5).map_or(0.0, |m| m.as_str().parse().unwrap());
let long_end = caps.get(7).map_or(0.0, |m| m.as_str().parse().unwrap());
Ok(Some(Location::new(
lat_start, long_start, lat_end, long_end,
)))
}
pub fn from_str<'de, T, D>(deserializer: D) -> Result<T, D::Error>
where
T: FromStr,
T::Err: Display,
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
T::from_str(&s).map_err(de::Error::custom)
}
#[cfg(feature = "fastfloat")]
pub fn from_str_fast_float<'de, T, D>(deserializer: D) -> Result<T, D::Error>
where
D: Deserializer<'de>,
T: fast_float::FastFloat,
{
let s = String::deserialize(deserializer)?;
fast_float::parse(s).map_err(|_| de::Error::custom("Invalid float string!"))
}
pub fn from_str_error_as_none<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
where
T: FromStr,
T::Err: Display,
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Ok(T::from_str(&s).ok())
}
pub fn delimited<'de, V, T, D>(deserializer: D) -> Result<V, D::Error>
where
V: FromIterator<T>,
T: FromStr + Sep,
T::Err: Display,
D: Deserializer<'de>,
{
struct DelimitedBy<V, T>(Phantom<V>, Phantom<T>);
impl<'de, V, T> Visitor<'de> for DelimitedBy<V, T>
where
V: FromIterator<T>,
T: FromStr + Sep,
T::Err: Display,
{
type Value = V;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("string containing / separated elements")
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
s.split(T::delimiter())
.map(FromStr::from_str)
.collect::<Result<_, _>>()
.map_err(de::Error::custom)
}
}
let visitor = DelimitedBy(Phantom, Phantom);
deserializer.deserialize_str(visitor)
}
}