From 9153e27ce54aeff58a02c19150b7077dc9ad0ae8 Mon Sep 17 00:00:00 2001 From: Kristofers Solo Date: Mon, 7 Jul 2025 23:39:12 +0300 Subject: [PATCH] refactor(units): remove macros --- derive_macro/src/lib.rs | 7 -- derive_macro/src/unit.rs | 134 -------------------------------------- src/app/utils/filesize.rs | 6 +- src/app/utils/netspeed.rs | 6 +- src/app/utils/unit.rs | 114 ++++++++++++++++++++++++++------ 5 files changed, 101 insertions(+), 166 deletions(-) delete mode 100644 derive_macro/src/unit.rs diff --git a/derive_macro/src/lib.rs b/derive_macro/src/lib.rs index 5a8bc98..410171d 100644 --- a/derive_macro/src/lib.rs +++ b/derive_macro/src/lib.rs @@ -1,5 +1,4 @@ mod merge; -mod unit; use proc_macro::TokenStream; use syn::{DeriveInput, parse_macro_input}; @@ -9,9 +8,3 @@ pub fn merge_derive(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); merge::impl_merge_derive(input) } - -#[proc_macro_derive(UnitConversions, attributes(units, error))] -pub fn unit_derive(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as DeriveInput); - unit::impl_unit_conversions(input) -} diff --git a/derive_macro/src/unit.rs b/derive_macro/src/unit.rs deleted file mode 100644 index ba8a392..0000000 --- a/derive_macro/src/unit.rs +++ /dev/null @@ -1,134 +0,0 @@ -use proc_macro::TokenStream; -use quote::quote; -use syn::{Attribute, DeriveInput, Error, Type, parse_quote}; - -pub fn impl_unit_conversions(input: DeriveInput) -> TokenStream { - let name = &input.ident; - - let mut unsigned_types = Vec::new(); - let mut signed_types = Vec::new(); - let mut error_type: Option = None; - - for attr in &input.attrs { - if attr.path().is_ident("units") { - if let Ok(types) = parse_unit_types(attr) { - for ty in types { - let type_str = quote!(#ty).to_string(); - if is_signed_type(&type_str) { - signed_types.push(ty); - } else { - unsigned_types.push(ty); - } - } - } else if attr.path().is_ident("error") { - if let Ok(err_type) = parse_error_types(attr) { - error_type = Some(err_type); - } - } - } - } - - if unsigned_types.is_empty() && signed_types.is_empty() { - unsigned_types = vec![ - parse_quote!(u8), - parse_quote!(u16), - parse_quote!(u32), - parse_quote!(u64), - parse_quote!(usize), - ]; - signed_types = vec![ - parse_quote!(i8), - parse_quote!(i16), - parse_quote!(i32), - parse_quote!(i64), - parse_quote!(isize), - ]; - } - - let error_type = error_type.unwrap_or_else(|| parse_quote!(String)); - let is_string_error = quote!(#error_type).to_string() == "String"; - - let from_impls = unsigned_types.iter().map(|ty| { - let conversion_expr = if name == "Unit" { - quote! { Self(value as u64) } - } else { - quote! { Self(crate::app::utils::unit::Unit::new(value as u64)) } - }; - quote! { - impl From<#ty> for #name { - fn from(value: #ty) -> Self { - #conversion_expr - } - } - } - }); - - let try_from_impls = signed_types.iter().map(|ty| { - let error_creation = if is_string_error { - quote! { - format!("Cannot convert negative value {} to {}", value, stringify!(#name)) - } - } else { - // For custom error types, try to construct from a string message - // This assumes the error type implements From or similar - quote! { - #error_type::from(format!("Cannot convert negative value {} to {}", value, stringify!(#name))) - } - }; - - let conversion_expr = if name == "Unit" { - quote! { Ok(Self(value as u64)) } - } else { - quote! { Ok(Self(crate::app::utils::unit::Unit::try_from(value)?)) } - }; - - quote! { - impl TryFrom<#ty> for #name { - type Error = #error_type; - - fn try_from(value: #ty) -> Result { - if value < 0 { - return Err(#error_creation); - } - #conversion_expr - } - } - } - }); - - let expanded = quote! { - #(#from_impls)* - #(#try_from_impls)* - }; - - TokenStream::from(expanded) -} - -fn parse_unit_types(attr: &Attribute) -> Result, Error> { - let mut types = Vec::new(); - - attr.parse_nested_meta(|meta| { - if let Ok(ty) = meta.value()?.parse::() { - types.push(ty); - } - Ok(()) - })?; - Ok(types) -} - -fn parse_error_types(attr: &Attribute) -> Result { - let mut error_type = None; - - attr.parse_nested_meta(|meta| { - if let Ok(ty) = meta.value()?.parse::() { - error_type = Some(ty); - } - Ok(()) - })?; - - error_type.ok_or_else(|| Error::new_spanned(attr, "Expected error type")) -} - -fn is_signed_type(type_str: &str) -> bool { - matches!(type_str, "i8" | "i16" | "i32" | "i64" | "isize") -} diff --git a/src/app/utils/filesize.rs b/src/app/utils/filesize.rs index b83a451..7f9c47c 100644 --- a/src/app/utils/filesize.rs +++ b/src/app/utils/filesize.rs @@ -1,10 +1,10 @@ use super::unit::{Unit, UnitDisplay}; -use derive_macro::UnitConversions; +use crate::impl_unit_wrapper; use std::fmt::Display; -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Default, UnitConversions)] -#[error(UnitError)] +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Default)] pub struct FileSize(Unit); +impl_unit_wrapper!(FileSize); impl FileSize { pub fn new(bytes: u64) -> Self { diff --git a/src/app/utils/netspeed.rs b/src/app/utils/netspeed.rs index fe99b2e..ae0d6e3 100644 --- a/src/app/utils/netspeed.rs +++ b/src/app/utils/netspeed.rs @@ -1,10 +1,10 @@ use super::unit::{Unit, UnitDisplay}; -use derive_macro::UnitConversions; +use crate::impl_unit_wrapper; use std::fmt::Display; -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Default, UnitConversions)] -#[error(UnitError)] +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Default)] pub struct NetSpeed(Unit); +impl_unit_wrapper!(NetSpeed); impl NetSpeed { pub fn new(bytes_per_second: u64) -> Self { diff --git a/src/app/utils/unit.rs b/src/app/utils/unit.rs index 1fa9e82..ebf95fc 100644 --- a/src/app/utils/unit.rs +++ b/src/app/utils/unit.rs @@ -1,23 +1,73 @@ -use derive_macro::UnitConversions; -use std::fmt::Display; -use thiserror::Error; +use std::fmt::{Debug, Display}; -#[derive(Error, Debug, PartialEq)] -pub enum UnitError { - #[error("Value cannot be negative: {value}")] - NegativeValue { value: i64 }, - #[error("Value is too large: {value}")] - ValueTooLarge { value: f64 }, - #[error("Value is invalid: {reason}")] - InvalidValue { reason: String }, +mod sealed { + pub trait Sealed {} + + macro_rules! impl_sealed { + ($($t:ty),*) => { + $(impl Sealed for $t {})* + }; + } + + impl_sealed!(u8, u16, u32, u64, usize, i8, i16, i32, i64, isize); } -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Default, UnitConversions)] -#[error(UnitError)] -pub struct Unit(u64); +pub trait IntoU64: sealed::Sealed { + fn into_u64(self) -> u64; +} + +macro_rules! impl_into_u64 { + ($($t:ty),*) => {$( + impl IntoU64 for $t { + fn into_u64(self) -> u64 { + self.try_into().unwrap_or(0) + } + } + )*}; +} + +impl_into_u64!(u8, u16, u32, u64, usize, i8, i16, i32, i64, isize); + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct UnitWrapper(T); + +impl Default for UnitWrapper +where + T: From, +{ + fn default() -> Self { + Self(T::from(0)) + } +} + +impl From for UnitWrapper +where + U: IntoU64, + T: From, +{ + fn from(value: U) -> Self { + Self(T::from(value.into_u64())) + } +} + +impl UnitWrapper { + pub fn new(inner: T) -> Self { + Self(inner) + } + + pub fn inner(&self) -> &T { + &self.0 + } + + pub fn into_inner(self) -> T { + self.0 + } +} + +pub type Unit = UnitWrapper; impl Unit { - pub const fn new(value: u64) -> Self { + pub const fn from_raw(value: u64) -> Self { Self(value) } @@ -40,21 +90,47 @@ impl<'a> UnitDisplay<'a> { impl<'a> Display for UnitDisplay<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - const THREASHOLD: f64 = 1024.0; + const THRESHOLD: f64 = 1024.0; let value = self.unit.0 as f64; - if value < THREASHOLD { + if value < THRESHOLD { return write!(f, "{} {}", self.unit.0, self.units[0]); } let mut size = value; let mut unit_index = 0; - while size >= THREASHOLD && unit_index < self.units.len() - 1 { - size /= THREASHOLD; + while size >= THRESHOLD && unit_index < self.units.len() - 1 { + size /= THRESHOLD; unit_index += 1; } write!(f, "{:.2} {}", size, self.units[unit_index]) } } + +#[macro_export] +macro_rules! impl_unit_wrapper { + ($wrapper:ident) => { + impl From for $wrapper { + fn from(unit: $crate::app::utils::unit::Unit) -> Self { + Self(unit) + } + } + + impl From for $wrapper + where + T: $crate::app::utils::unit::IntoU64, + { + fn from(value: T) -> Self { + Self($crate::app::utils::unit::Unit::from(value)) + } + } + + impl $wrapper { + pub fn unit(&self) -> &Unit { + &self.0 + } + } + }; +}