pub trait Universalis {
type Repr;
fn into(self) -> Self::Repr;
fn from(repr: Self::Repr) -> Self;
#[inline]
fn convert_from<Src>(src: Src) -> Self
where
Self: Sized,
Src: Universalis<Repr = Self::Repr>,
{
let repr = <Src as Universalis>::into(src);
<Self as Universalis>::from(repr)
}
#[inline]
fn map_repr<Mapper>(self, mapper: Mapper) -> Self
where
Self: Sized,
Mapper: FnOnce(Self::Repr) -> Self::Repr,
{
Self::from(mapper(self.into()))
}
#[inline]
fn map_inter<Inter, Mapper>(self, mapper: Mapper) -> Self
where
Self: Sized,
Inter: Universalis<Repr = Self::Repr>,
Mapper: FnOnce(Inter) -> Inter,
{
Self::convert_from(mapper(Inter::convert_from(self)))
}
}
#[inline]
pub fn from_universalis<Dst, Repr>(repr: Repr) -> Dst
where
Dst: Universalis<Repr = Repr>,
{
<Dst as Universalis>::from(repr)
}
#[inline]
pub fn into_universalis<Src, Repr>(src: Src) -> Repr
where
Src: Universalis<Repr = Repr>,
{
<Src as Universalis>::into(src)
}
#[inline]
pub fn convert_from<Src, Dst, Repr>(src: Src) -> Dst
where
Src: Universalis<Repr = Repr>,
Dst: Universalis<Repr = Repr>,
{
<Dst as Universalis>::convert_from(src)
}
#[inline]
pub fn map_repr<Origin, Mapper>(val: Origin, mapper: Mapper) -> Origin
where
Origin: Universalis,
Mapper: FnOnce(Origin::Repr) -> Origin::Repr,
{
<Origin as Universalis>::map_repr(val, mapper)
}
#[inline]
pub fn map_inter<Inter, Origin, Mapper>(val: Origin, mapper: Mapper) -> Origin
where
Origin: Universalis,
Inter: Universalis<Repr = Origin::Repr>,
Mapper: FnOnce(Inter) -> Inter,
{
<Origin as Universalis>::map_inter(val, mapper)
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, PartialEq, Clone)]
struct Meters(f64);
#[derive(Debug, PartialEq, Clone)]
struct Feet(f64);
impl Universalis for Meters {
type Repr = f64;
fn into(self) -> f64 {
self.0
}
fn from(repr: f64) -> Self {
Meters(repr)
}
}
impl Universalis for Feet {
type Repr = f64;
fn into(self) -> f64 {
self.0
}
fn from(repr: f64) -> Self {
Feet(repr)
}
}
#[test]
fn isomorphism_law_into_then_from() {
let original = Meters(2.5);
let repr = Universalis::into(original.clone());
let recovered = <Meters as Universalis>::from(repr);
assert_eq!(recovered, original);
}
#[test]
fn isomorphism_law_from_then_into() {
let repr: f64 = 1.75;
let value = <Meters as Universalis>::from(repr);
let recovered: f64 = Universalis::into(value);
assert_eq!(recovered, repr);
}
#[test]
fn convert_from_between_distinct_types_with_same_repr() {
let m = Meters(1.0);
let f: Feet = convert_from(m);
assert_eq!(f, Feet(1.0));
}
#[test]
fn map_repr_identity_is_noop() {
let m = Meters(42.0);
let result = m.clone().map_repr(|r| r);
assert_eq!(result, m);
}
#[test]
fn map_inter_with_same_type_is_identity() {
let m = Meters(7.0);
let result = map_inter::<Meters, Meters, _>(m.clone(), |x| x);
assert_eq!(result, m);
}
}