senax-common 0.4.8

Senax common library
Documentation
use anyhow::ensure;
use bytes::{BufMut, BytesMut};
use schemars::JsonSchema;
use senax_encoder::{Decode, Encode, Pack, Unpack};
use serde::{Deserialize, Serialize};
use std::{convert::TryInto, fmt::Display, str::FromStr};

#[derive(
    Deserialize,
    Serialize,
    Decode,
    Encode,
    Pack,
    Unpack,
    Copy,
    Clone,
    Debug,
    Default,
    PartialEq,
    JsonSchema,
)]
#[cfg_attr(
    feature = "graphql6",
    derive(graphql6::SimpleObject, graphql6::InputObject)
)]
#[cfg_attr(feature = "graphql6", graphql(input_name = "GeoPointInput"))]
#[cfg_attr(
    feature = "graphql7",
    derive(graphql7::SimpleObject, graphql7::InputObject)
)]
#[cfg_attr(feature = "graphql7", graphql(input_name = "GeoPointInput"))]
#[cfg_attr(feature = "utoipa5", derive(utoipa5::ToSchema))]
#[cfg_attr(feature = "utoipa5", schema(as = GeoPointInput))]
pub struct GeoPoint {
    pub lat: f64,
    pub lng: f64,
}

impl From<&(f64, f64)> for GeoPoint {
    fn from(v: &(f64, f64)) -> Self {
        Self { lat: v.0, lng: v.1 }
    }
}

impl GeoPoint {
    pub fn to_tuple(&self) -> (f64, f64) {
        (self.lat, self.lng)
    }
}
impl From<Vec<u8>> for GeoPoint {
    fn from(input: Vec<u8>) -> Self {
        Self::from(input.as_slice())
    }
}
impl From<&Vec<u8>> for GeoPoint {
    fn from(input: &Vec<u8>) -> Self {
        Self::from(input.as_slice())
    }
}
impl From<&[u8]> for GeoPoint {
    fn from(input: &[u8]) -> Self {
        if input.len() < 21 {
            return GeoPoint {
                lat: f64::NAN,
                lng: f64::NAN,
            };
        }
        let (bytes, input) = input.split_at(1);
        let endian = u8::from_le_bytes(bytes.try_into().unwrap());
        let (bytes, input) = input.split_at(4);
        let g_type = if endian == 1 {
            u32::from_le_bytes(bytes.try_into().unwrap())
        } else {
            u32::from_be_bytes(bytes.try_into().unwrap())
        };
        if g_type != 1 {
            return GeoPoint {
                lat: f64::NAN,
                lng: f64::NAN,
            };
        }
        let (bytes, input) = input.split_at(8);
        let x = if endian == 1 {
            f64::from_le_bytes(bytes.try_into().unwrap())
        } else {
            f64::from_be_bytes(bytes.try_into().unwrap())
        };
        let (bytes, _input) = input.split_at(8);
        let y = if endian == 1 {
            f64::from_le_bytes(bytes.try_into().unwrap())
        } else {
            f64::from_be_bytes(bytes.try_into().unwrap())
        };
        GeoPoint { lat: y, lng: x }
    }
}

impl GeoPoint {
    pub fn to_lng_lat_wkb(&self) -> Vec<u8> {
        let mut buf = BytesMut::with_capacity(21);
        buf.put_u8(1);
        buf.put_u32_le(1);
        buf.put_f64_le(self.lng);
        buf.put_f64_le(self.lat);
        buf.to_vec()
    }
}

impl FromStr for GeoPoint {
    type Err = anyhow::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let coords: Vec<&str> = s
            .trim_matches(|p| p == '(' || p == ')')
            .split(',')
            .map(|v| v.trim())
            .collect();
        ensure!(coords.len() != 2, "illegal format");
        let x = coords[0].parse::<f64>()?;
        let y = coords[1].parse::<f64>()?;
        Ok(GeoPoint { lat: x, lng: y })
    }
}
impl Display for GeoPoint {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_fmt(format_args!("{},{}", self.lat, self.lng))
    }
}

pub trait ToGeoPoint {
    fn to_geo_point(&self) -> GeoPoint;
}

impl ToGeoPoint for Vec<u8> {
    fn to_geo_point(&self) -> GeoPoint {
        GeoPoint::from(self)
    }
}

impl ToGeoPoint for (f64, f64) {
    fn to_geo_point(&self) -> GeoPoint {
        GeoPoint::from(self)
    }
}