use std::{
any::type_name, error::Error, f64::consts::PI, fmt::Display, marker::PhantomData, ops::Mul,
};
use crate::{Size, Units};
use super::{Data, UniqueIdentifier, Write};
macro_rules! converter {
( $( ($u:literal:$t:ident,$l:expr_2021) ),* ) => {
$(
#[doc = "Conversion to "]
#[doc = $u]
pub struct $t<U: UniqueIdentifier>(PhantomData<U>);
impl<U: UniqueIdentifier> UnitsConversion for $t<U> {
const UNITS: f64 = $l;
type ID = U;
}
impl<T, U, C> Write<$t<U>> for C
where
T: Copy + TryFrom<f64> + Mul<T, Output = T>,
<T as TryFrom<f64>>::Error: std::error::Error+'static,
U: UniqueIdentifier<DataType = Vec<T>>,
C: Write<U> + Units,
{
fn write(&mut self) -> Option<Data<$t<U>>> {
<C as Write<U>>::write(self)
.as_ref()
.map(|data| <$t<U> as UnitsConversion>::conversion(data).unwrap())
}
}
impl<U, C> Size<$t<U>> for C
where
U: UniqueIdentifier,
C: Size<U> + Units,
{
fn len(&self) -> usize {
<C as Size<U>>::len(self)
}
}
)*
};
}
converter!(
("nanometers" : NM , 1e9),
("micrometers" : MuM , 1e6),
("degrees" : Deg, 180. / PI),
("arcseconds" : Arcsec, (180. * 3600.) / PI),
("milli-arcseconds": Mas , (180. * 3600e3) / PI)
);
impl<U, W> UniqueIdentifier for W
where
U: UniqueIdentifier,
W: UnitsConversion<ID = U> + Send + Sync,
{
const PORT: u16 = <U as UniqueIdentifier>::PORT;
type DataType = <U as UniqueIdentifier>::DataType;
}
#[derive(Debug)]
pub struct UnitsConversionError {
msg: String,
error: Box<dyn Error>,
}
impl Display for UnitsConversionError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}\ndue to {:?}", self.msg, self.error)
}
}
impl Error for UnitsConversionError {}
pub trait UnitsConversion {
const UNITS: f64;
type ID: UniqueIdentifier;
fn conversion<T>(data: &Data<Self::ID>) -> Result<Data<Self>, UnitsConversionError>
where
Self::ID: UniqueIdentifier<DataType = Vec<T>>,
T: Copy + TryFrom<f64> + Mul<T, Output = T>,
<T as TryFrom<f64>>::Error: std::error::Error + 'static,
Self: UniqueIdentifier<DataType = Vec<T>> + Sized,
{
let msg = format!(
"failed to convert f64 to {} in Write<{}>::write",
type_name::<T>(),
type_name::<Self>()
);
let s: T = T::try_from(Self::UNITS).map_err(|e| UnitsConversionError {
msg,
error: Box::new(e),
})?;
let data: Vec<_> = Into::<&[T]>::into(data).iter().map(|x| *x * s).collect();
Ok(data.into())
}
}
#[cfg(test)]
mod tests {
use crate::Update;
use super::*;
pub enum W {}
impl UniqueIdentifier for W {
type DataType = Vec<f64>;
}
#[derive(Default)]
pub struct Client {
pub data: Vec<f64>,
}
impl Update for Client {}
impl Write<W> for Client {
fn write(&mut self) -> Option<Data<W>> {
Some(vec![1e-9, 2e-6, 3e-3].into())
}
}
#[derive(Default)]
pub struct ClientAngle {
pub data: Vec<f64>,
}
impl Update for ClientAngle {}
impl Write<W> for ClientAngle {
fn write(&mut self) -> Option<Data<W>> {
Some(vec![1., 1e-3].into())
}
}
impl Units for Client {}
impl Units for ClientAngle {}
#[test]
fn units_nm() {
let mut client = Client::default();
let data = <Client as Write<NM<W>>>::write(&mut client);
dbg!(data);
}
#[test]
fn units_mum() {
let mut client = Client::default();
let data = <Client as Write<MuM<W>>>::write(&mut client);
dbg!(data);
}
#[test]
fn units_arcsec() {
let mut client = ClientAngle::default();
let data = <ClientAngle as Write<Arcsec<W>>>::write(&mut client);
dbg!(data);
}
#[test]
fn units_milli_arcsec() {
let mut client = ClientAngle::default();
let data = <ClientAngle as Write<Mas<W>>>::write(&mut client);
dbg!(data);
}
}