use std::{fmt::{Debug, Formatter},
ops::{Add, AddAssign, Deref, Div, Mul, MulAssign, Sub, SubAssign}};
use crate::{add_unsigned, mul_unsigned, sub_unsigned, LossyConvertToByte};
pub type ChUnitPrimitiveType = u16;
#[derive(Copy, Clone, Default, PartialEq, Ord, PartialOrd, Eq, Hash)]
pub struct ChUnit {
pub value: ChUnitPrimitiveType,
}
impl ChUnit {
#[must_use]
pub fn new(value: ChUnitPrimitiveType) -> Self { Self { value } }
#[must_use]
pub fn as_usize(&self) -> usize { usize(*self) }
#[must_use]
pub fn as_u32(&self) -> u32 { u32(*self) }
}
pub fn ch(arg_num: impl Into<ChUnit>) -> ChUnit { arg_num.into() }
pub fn usize(arg_num: impl Into<usize>) -> usize { arg_num.into() }
pub fn u32(arg_num: impl Into<u32>) -> u32 { arg_num.into() }
pub fn isize(arg_num: impl Into<isize>) -> isize { arg_num.into() }
pub fn i32(arg_num: impl Into<i32>) -> i32 { arg_num.into() }
pub fn i16(arg_num: impl Into<i16>) -> i16 { arg_num.into() }
pub fn f64(arg_num: impl Into<f64>) -> f64 { arg_num.into() }
pub fn u8(arg_num: impl Into<u8>) -> u8 { arg_num.into() }
pub fn u16(arg_num: impl Into<u16>) -> u16 { arg_num.into() }
impl Debug for ChUnit {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.value)
}
}
impl Deref for ChUnit {
type Target = ChUnitPrimitiveType;
fn deref(&self) -> &Self::Target { &self.value }
}
pub mod ch_unit_math_ops {
use super::{add_unsigned, ch, mul_unsigned, sub_unsigned, Add, AddAssign, ChUnit,
Div, Mul, MulAssign, Sub, SubAssign};
impl MulAssign<ChUnit> for ChUnit {
fn mul_assign(&mut self, rhs: Self) {
self.value = mul_unsigned!(self.value, rhs.value);
}
}
impl Add for ChUnit {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
ch(add_unsigned!(self.value, rhs.value))
}
}
impl Add<u16> for ChUnit {
type Output = Self;
fn add(self, rhs: u16) -> Self::Output { ch(add_unsigned!(self.value, rhs)) }
}
impl AddAssign for ChUnit {
fn add_assign(&mut self, rhs: Self) {
self.value = add_unsigned!(self.value, rhs.value);
}
}
impl AddAssign<u16> for ChUnit {
fn add_assign(&mut self, rhs: u16) {
self.value = add_unsigned!(self.value, rhs);
}
}
impl Sub for ChUnit {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
ch(sub_unsigned!(self.value, rhs.value))
}
}
impl Sub<u16> for ChUnit {
type Output = Self;
fn sub(self, rhs: u16) -> Self::Output { ch(sub_unsigned!(self.value, rhs)) }
}
impl SubAssign for ChUnit {
fn sub_assign(&mut self, rhs: Self) {
self.value = sub_unsigned!(self.value, rhs.value);
}
}
impl SubAssign<u16> for ChUnit {
fn sub_assign(&mut self, rhs: u16) {
self.value = sub_unsigned!(self.value, rhs);
}
}
impl Mul for ChUnit {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
ch(mul_unsigned!(self.value, rhs.value))
}
}
impl Mul<u16> for ChUnit {
type Output = Self;
fn mul(self, rhs: u16) -> Self::Output { ch(mul_unsigned!(self.value, rhs)) }
}
impl Div<u16> for ChUnit {
type Output = Self;
fn div(self, rhs: u16) -> Self::Output { ch(self.value / rhs) }
}
impl Div<ChUnit> for ChUnit {
type Output = Self;
fn div(self, rhs: ChUnit) -> Self::Output { ch(self.value / rhs.value) }
}
}
pub mod convert_to_other_types_from_ch {
use super::{ChUnit, ChUnitPrimitiveType, LossyConvertToByte};
impl From<ChUnit> for ChUnitPrimitiveType {
fn from(arg: ChUnit) -> Self { arg.value }
}
impl From<ChUnit> for u8 {
fn from(arg: ChUnit) -> Self { arg.value.to_u8_lossy() }
}
impl From<ChUnit> for f64 {
fn from(arg: ChUnit) -> Self { f64::from(arg.value) }
}
impl From<ChUnit> for i32 {
fn from(arg: ChUnit) -> Self { i32::from(arg.value) }
}
impl From<ChUnit> for u32 {
fn from(arg: ChUnit) -> Self { u32::from(arg.value) }
}
impl From<ChUnit> for usize {
fn from(arg: ChUnit) -> Self { usize::from(arg.value) }
}
impl From<ChUnit> for i16 {
#[allow(clippy::cast_possible_wrap)]
fn from(arg: ChUnit) -> Self {
arg.value as i16
}
}
impl From<ChUnit> for isize {
#[allow(clippy::cast_possible_wrap)]
fn from(arg: ChUnit) -> Self {
arg.value as isize
}
}
}
pub mod convert_from_other_types_to_ch {
use super::{ChUnit, ChUnitPrimitiveType};
fn f64_to_u16(value: f64) -> Result<u16, String> {
let value = value.round(); if value < 0.0 || value > f64::from(u16::MAX) {
return Err(format!("Failed to convert {value} to u16: out of range"));
}
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_sign_loss)]
Ok(value as u16)
}
impl From<f64> for ChUnit {
fn from(value: f64) -> Self {
let int_value: u16 = match f64_to_u16(value) {
Ok(it) => it,
Err(err) => {
tracing::error!(message = "Problem converting f64 to u16", err = err);
0
}
};
Self { value: int_value }
}
}
impl From<f32> for ChUnit {
fn from(value: f32) -> Self {
let int_value: u16 = match f64_to_u16(f64::from(value)) {
Ok(it) => it,
Err(err) => {
tracing::error!(message = "Problem converting f32 to u16", err = err);
0
}
};
Self { value: int_value }
}
}
impl From<isize> for ChUnit {
fn from(value: isize) -> Self {
Self {
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_sign_loss)]
value: value.try_into().unwrap_or(value as ChUnitPrimitiveType),
}
}
}
impl From<u8> for ChUnit {
fn from(it: u8) -> Self {
let value = ChUnitPrimitiveType::from(it);
Self { value }
}
}
impl From<ChUnitPrimitiveType> for ChUnit {
fn from(value: ChUnitPrimitiveType) -> Self { Self { value } }
}
impl From<usize> for ChUnit {
fn from(value: usize) -> Self {
Self {
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_sign_loss)]
value: value.try_into().unwrap_or(value as ChUnitPrimitiveType),
}
}
}
impl From<i32> for ChUnit {
fn from(value: i32) -> Self {
Self {
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_sign_loss)]
value: value.try_into().unwrap_or(value as ChUnitPrimitiveType),
}
}
}
impl From<u32> for ChUnit {
fn from(value: u32) -> Self {
Self {
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_sign_loss)]
value: value.try_into().unwrap_or(value as ChUnitPrimitiveType),
}
}
}
impl From<i16> for ChUnit {
fn from(value: i16) -> Self {
Self {
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_sign_loss)]
value: value.try_into().unwrap_or(value as ChUnitPrimitiveType),
}
}
}
}
#[cfg(test)]
mod tests_convert {
use super::*;
use crate::assert_eq2;
#[test]
fn test_as_usize() {
let ch_1: ChUnit = ch(1);
assert_eq2!(ch_1.as_usize(), 1);
}
#[test]
fn test_from_whatever_into_ch() {
let ch_1: ChUnit = ch(1);
assert_eq2!(*ch_1, 1);
let ch_2: ChUnit = ch(1) + ch(1);
assert_eq2!(*ch_2, 2);
let ch_3: ChUnit = ch(1) - ch(1);
assert_eq2!(*ch_3, 0);
let ch_4: ChUnit = ch(0) - ch(1);
assert_eq2!(*ch_4, 0);
}
#[test]
fn test_from_ch_into_usize() {
let usize_1: usize = usize(ch(1));
assert_eq2!(usize_1, 1);
let usize_2: usize = usize(ch(1) + ch(1));
assert_eq2!(usize_2, 2);
let usize_3: usize = usize(ch(1) - ch(1));
assert_eq2!(usize_3, 0);
let usize_4: usize = usize(ch(0) - ch(1));
assert_eq2!(usize_4, 0);
}
#[test]
fn test_from_ch_into_u16() {
let u16_1: u16 = u16(ch(1));
assert_eq2!(u16_1, 1);
let u16_2: u16 = u16(ch(1) + ch(1));
assert_eq2!(u16_2, 2);
let u16_3: u16 = u16(ch(1) - ch(1));
assert_eq2!(u16_3, 0);
let u16_4: u16 = u16(ch(0) - ch(1));
assert_eq2!(u16_4, 0);
}
}
#[cfg(test)]
mod tests_ch_unit_math_ops {
use super::*;
use crate::assert_eq2;
#[test]
fn test_add_ch_units() {
let ch_1: ChUnit = ch(1);
let ch_2: ChUnit = ch(2);
let result: ChUnit = ch_1 + ch_2;
assert_eq2!(*result, 3);
}
#[test]
fn test_add_assign_ch_units() {
let mut ch_1: ChUnit = ch(1);
let ch_2: ChUnit = ch(2);
ch_1 += ch_2;
assert_eq2!(*ch_1, 3);
}
#[test]
fn test_sub_ch_units() {
let ch_1: ChUnit = ch(3);
let ch_2: ChUnit = ch(1);
let result: ChUnit = ch_1 - ch_2;
assert_eq2!(*result, 2);
}
#[test]
fn test_sub_assign_ch_units() {
let mut ch_1: ChUnit = ch(3);
let ch_2: ChUnit = ch(1);
ch_1 -= ch_2;
assert_eq2!(*ch_1, 2);
}
#[test]
fn test_mul_ch_units() {
let ch_1: ChUnit = ch(2);
let ch_2: ChUnit = ch(3);
let result: ChUnit = ch_1 * ch_2;
assert_eq2!(*result, 6);
}
#[test]
fn test_mul_assign_ch_units() {
let mut ch_1: ChUnit = ch(2);
let ch_2: ChUnit = ch(3);
ch_1 *= ch_2;
assert_eq2!(*ch_1, 6);
}
#[test]
fn test_div_ch_units() {
let ch_1: ChUnit = ch(6);
let result: ChUnit = ch_1 / 2;
assert_eq2!(*result, 3);
}
}