use core::{fmt, marker::PhantomData, ops};
use cfg_if::cfg_if;
cfg_if! {
if #[cfg(all(feature = "no_std", not(feature = "no_alloc")))] {
extern crate alloc;
use alloc::{string::String, format};
}
}
use crate::{units::{any::{CastFrom, SiDefinedUnitDefinition}, base::BaseUnitMap}, SiDefinedUnit};
use super::{
ops::{Div, Mul, PowFrac, PowI},
units::any::{is_same_type, SiAnyUnit, SiOpsUnit},
};
pub struct Value<V, T: SiAnyUnit> {
pub value: V,
_t: PhantomData<T>,
}
impl<T: SiAnyUnit, V: fmt::Debug> fmt::Debug for Value<V, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Value").field(&self.value).finish()
}
}
impl<T: SiAnyUnit, V: Clone> Clone for Value<V, T> {
fn clone(&self) -> Self {
Self {
value: self.value.clone(),
_t: PhantomData,
}
}
fn clone_from(&mut self, source: &Self) {
self.value = source.value.clone();
}
}
impl<T: SiAnyUnit, V: Copy> Copy for Value<V, T> {}
impl<T: SiAnyUnit, V> Value<V, T> {
#[inline]
pub const fn new(value: V) -> Value<V, T> {
Self {
value,
_t: PhantomData,
}
}
#[inline]
pub const fn as_ref(&self) -> &V {
&self.value
}
#[inline]
pub fn as_mut_ref(&mut self) -> &mut V {
&mut self.value
}
}
impl<T: SiAnyUnit + SiOpsUnit, V> Value<V, T> {
#[inline]
pub fn cast<B: SiAnyUnit + SiOpsUnit + CastFrom<T>>(self) -> Value<V, B> {
debug_assert!(B::CAN_CAST_FROM);
Value::new(self.value)
}
#[cfg(not(feature = "no_alloc"))]
#[inline]
pub fn try_cast<B: SiAnyUnit + SiOpsUnit>(self) -> Result<Value<V, B>, String> {
if is_same_type::<T, B>() {
Ok(Value::new(self.value))
} else {
Err(format!(
r#"
cannot cast SI value
origin: {}
target: {}
"#,
T::UNIT_MAP,
B::UNIT_MAP
))
}
}
#[inline]
pub unsafe fn unsafe_cast<B: SiAnyUnit>(self) -> Value<V, B> {
Value::new(self.value)
}
#[inline]
pub fn powi_type<const N: i32>(self) -> Value<V, PowI<T, N>> {
Value::new(self.value)
}
#[inline]
pub fn powf_type<const N: i32, const D: u32>(self) -> Value<V, PowFrac<T, N, D>> {
assert!(D > 0);
Value::new(self.value)
}
}
impl<T: SiAnyUnit + SiOpsUnit, V: Copy> Value<V, T> {
#[inline]
pub const fn cast_const<B: SiAnyUnit + SiOpsUnit + CastFrom<T>>(self) -> Value<V, B> {
assert!(B::CAN_CAST_FROM);
Value::new(self.value)
}
#[inline]
pub const fn powi_type_const<const N: i32>(self) -> Value<V, PowI<T, N>> {
Value::new(self.value)
}
#[inline]
pub const fn powf_type_const<const N: i32, const D: u32>(self) -> Value<V, PowFrac<T, N, D>> {
assert!(D > 0);
Value::new(self.value)
}
}
impl<T: SiAnyUnit + SiOpsUnit, V: PartialEq> Value<V, T> {
#[inline]
pub fn is_value_equal<R: SiAnyUnit + SiOpsUnit>(&self, rhs: &Value<V, R>) -> bool {
self.value == rhs.value
}
#[inline]
pub fn is_equal<R: SiAnyUnit + SiOpsUnit>(&self, rhs: &Value<V, R>) -> bool {
is_same_type::<T, R>() && self.is_value_equal(rhs)
}
}
#[cfg(not(feature = "no_alloc"))]
impl<T: SiAnyUnit + SiOpsUnit, V: fmt::Display> Value<V, T> {
#[inline]
pub fn format_si(&self) -> String {
format!("{} ({})", self.value, T::UNIT_MAP)
}
}
macro_rules! __impl_int_ops {
($ty0:ty, $($ty:ty),+ $(,)?) => {
__impl_int_ops!($ty0);
$(__impl_int_ops!($ty);)+
};
($ty:ty) => {
impl<T: SiAnyUnit> Value<$ty, T> {
#[inline]
pub const fn cadd(mut self, rhs: Value<$ty, T>) -> Value<$ty, T> {
self.value += rhs.value;
self
}
#[inline]
pub const fn csub(mut self, rhs: Value<$ty, T>) -> Value<$ty, T> {
self.value -= rhs.value;
self
}
#[inline]
pub const fn cmul(mut self, rhs: $ty) -> Value<$ty, T> {
self.value *= rhs;
self
}
#[inline]
pub const fn cdiv(mut self, rhs: $ty) -> Value<$ty, T> {
self.value /= rhs;
self
}
}
};
}
macro_rules! __impl_float_ops {
($ty0:ty, $($ty:ty),+ $(,)?) => {
__impl_float_ops!($ty0);
$(__impl_float_ops!($ty);)+
};
($ty:ty) => {
impl<T: SiAnyUnit> Value<$ty, T> {
#[inline]
pub fn padd(mut self, rhs: Value<$ty, T>) -> Value<$ty, T> {
self.value += rhs.value;
self
}
#[inline]
pub fn psub(mut self, rhs: Value<$ty, T>) -> Value<$ty, T> {
self.value -= rhs.value;
self
}
#[inline]
pub fn pmul(mut self, rhs: $ty) -> Value<$ty, T> {
self.value *= rhs;
self
}
#[inline]
pub fn pdiv(mut self, rhs: $ty) -> Value<$ty, T> {
self.value /= rhs;
self
}
}
};
}
#[cfg(feature = "const_soft_float")]
macro_rules! __impl_const_float_ops {
($($ty:ty),*) => {
$(
impl<T: SiAnyUnit> Value<$ty, T> {
#[inline]
pub const fn cadd(self, rhs: Self) -> Self {
Self::new(self.value.add(rhs.value))
}
#[inline]
pub const fn csub(self, rhs: Self) -> Self {
Self::new(self.value.sub(rhs.value))
}
#[inline]
pub const fn cmul(self, rhs: $ty) -> Self {
Self::new(self.value.mul(rhs))
}
#[inline]
pub const fn cdiv(self, rhs: $ty) -> Self {
Self::new(self.value.div(rhs))
}
}
)*
};
}
__impl_int_ops!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, usize, isize);
__impl_float_ops!(f32, f64);
#[cfg(feature = "const_soft_float")]
__impl_const_float_ops!(
const_soft_float::soft_f32::SoftF32,
const_soft_float::soft_f64::SoftF64
);
impl<T: SiAnyUnit, V: ops::Add<L>, L> ops::Add<Value<L, T>> for Value<V, T> {
type Output = Value<V::Output, T>;
#[inline]
fn add(self, rhs: Value<L, T>) -> Self::Output {
Value::new(self.value.add(rhs.value))
}
}
impl<T: SiAnyUnit, V: ops::Sub<L>, L> ops::Sub<Value<L, T>> for Value<V, T> {
type Output = Value<V::Output, T>;
#[inline]
fn sub(self, rhs: Value<L, T>) -> Self::Output {
Value::new(self.value.sub(rhs.value))
}
}
impl<T: SiAnyUnit + SiOpsUnit, V: ops::Mul<L>, L, B: SiAnyUnit + SiOpsUnit> ops::Mul<Value<L, B>>
for Value<V, T>
{
type Output = Value<V::Output, Mul<T, B>>;
#[inline]
fn mul(self, rhs: Value<L, B>) -> Self::Output {
Value::new(self.value.mul(rhs.value))
}
}
impl<T: SiAnyUnit + SiOpsUnit, V: ops::Div<L>, L, B: SiAnyUnit + SiOpsUnit> ops::Div<Value<L, B>>
for Value<V, T>
{
type Output = Value<V::Output, Div<T, B>>;
#[inline]
fn div(self, rhs: Value<L, B>) -> Self::Output {
Value::new(self.value.div(rhs.value))
}
}
impl<T: SiAnyUnit + SiOpsUnit, V: ops::Rem<L>, L, B: SiAnyUnit + SiOpsUnit> ops::Rem<Value<L, B>>
for Value<V, T>
{
type Output = Value<V::Output, Div<T, B>>;
#[inline]
fn rem(self, rhs: Value<L, B>) -> Self::Output {
Value::new(self.value.rem(rhs.value))
}
}
impl<T: SiAnyUnit, V: ops::Neg> ops::Neg for Value<V, T> {
type Output = Value<V::Output, T>;
#[inline]
fn neg(self) -> Self::Output {
Value::new(self.value.neg())
}
}
pub struct PureValue;
impl SiAnyUnit for PureValue {}
impl SiOpsUnit for PureValue {
const UNIT_MAP: BaseUnitMap = BaseUnitMap::EMPTY;
}
impl SiDefinedUnit for PureValue {
const DEF: Option<SiDefinedUnitDefinition> = None;
}