mirror of
https://github.com/kristoferssolo/traxor.git
synced 2025-10-21 20:10:35 +00:00
refactor(units): remove macros
This commit is contained in:
parent
c54647c877
commit
9153e27ce5
@ -1,5 +1,4 @@
|
|||||||
mod merge;
|
mod merge;
|
||||||
mod unit;
|
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use syn::{DeriveInput, parse_macro_input};
|
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);
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
merge::impl_merge_derive(input)
|
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)
|
|
||||||
}
|
|
||||||
|
|||||||
@ -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<Type> = 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<String> 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<Self, Self::Error> {
|
|
||||||
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<Vec<Type>, Error> {
|
|
||||||
let mut types = Vec::new();
|
|
||||||
|
|
||||||
attr.parse_nested_meta(|meta| {
|
|
||||||
if let Ok(ty) = meta.value()?.parse::<Type>() {
|
|
||||||
types.push(ty);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
})?;
|
|
||||||
Ok(types)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_error_types(attr: &Attribute) -> Result<Type, Error> {
|
|
||||||
let mut error_type = None;
|
|
||||||
|
|
||||||
attr.parse_nested_meta(|meta| {
|
|
||||||
if let Ok(ty) = meta.value()?.parse::<Type>() {
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
@ -1,10 +1,10 @@
|
|||||||
use super::unit::{Unit, UnitDisplay};
|
use super::unit::{Unit, UnitDisplay};
|
||||||
use derive_macro::UnitConversions;
|
use crate::impl_unit_wrapper;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Default, UnitConversions)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
|
||||||
#[error(UnitError)]
|
|
||||||
pub struct FileSize(Unit);
|
pub struct FileSize(Unit);
|
||||||
|
impl_unit_wrapper!(FileSize);
|
||||||
|
|
||||||
impl FileSize {
|
impl FileSize {
|
||||||
pub fn new(bytes: u64) -> Self {
|
pub fn new(bytes: u64) -> Self {
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
use super::unit::{Unit, UnitDisplay};
|
use super::unit::{Unit, UnitDisplay};
|
||||||
use derive_macro::UnitConversions;
|
use crate::impl_unit_wrapper;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Default, UnitConversions)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
|
||||||
#[error(UnitError)]
|
|
||||||
pub struct NetSpeed(Unit);
|
pub struct NetSpeed(Unit);
|
||||||
|
impl_unit_wrapper!(NetSpeed);
|
||||||
|
|
||||||
impl NetSpeed {
|
impl NetSpeed {
|
||||||
pub fn new(bytes_per_second: u64) -> Self {
|
pub fn new(bytes_per_second: u64) -> Self {
|
||||||
|
|||||||
@ -1,23 +1,73 @@
|
|||||||
use derive_macro::UnitConversions;
|
use std::fmt::{Debug, Display};
|
||||||
use std::fmt::Display;
|
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
#[derive(Error, Debug, PartialEq)]
|
mod sealed {
|
||||||
pub enum UnitError {
|
pub trait Sealed {}
|
||||||
#[error("Value cannot be negative: {value}")]
|
|
||||||
NegativeValue { value: i64 },
|
macro_rules! impl_sealed {
|
||||||
#[error("Value is too large: {value}")]
|
($($t:ty),*) => {
|
||||||
ValueTooLarge { value: f64 },
|
$(impl Sealed for $t {})*
|
||||||
#[error("Value is invalid: {reason}")]
|
};
|
||||||
InvalidValue { reason: String },
|
}
|
||||||
|
|
||||||
|
impl_sealed!(u8, u16, u32, u64, usize, i8, i16, i32, i64, isize);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Default, UnitConversions)]
|
pub trait IntoU64: sealed::Sealed {
|
||||||
#[error(UnitError)]
|
fn into_u64(self) -> u64;
|
||||||
pub struct Unit(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 = u64>(T);
|
||||||
|
|
||||||
|
impl<T> Default for UnitWrapper<T>
|
||||||
|
where
|
||||||
|
T: From<u64>,
|
||||||
|
{
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(T::from(0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, U> From<U> for UnitWrapper<T>
|
||||||
|
where
|
||||||
|
U: IntoU64,
|
||||||
|
T: From<u64>,
|
||||||
|
{
|
||||||
|
fn from(value: U) -> Self {
|
||||||
|
Self(T::from(value.into_u64()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> UnitWrapper<T> {
|
||||||
|
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<u64>;
|
||||||
|
|
||||||
impl Unit {
|
impl Unit {
|
||||||
pub const fn new(value: u64) -> Self {
|
pub const fn from_raw(value: u64) -> Self {
|
||||||
Self(value)
|
Self(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,21 +90,47 @@ impl<'a> UnitDisplay<'a> {
|
|||||||
|
|
||||||
impl<'a> Display for UnitDisplay<'a> {
|
impl<'a> Display for UnitDisplay<'a> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
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;
|
let value = self.unit.0 as f64;
|
||||||
|
|
||||||
if value < THREASHOLD {
|
if value < THRESHOLD {
|
||||||
return write!(f, "{} {}", self.unit.0, self.units[0]);
|
return write!(f, "{} {}", self.unit.0, self.units[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut size = value;
|
let mut size = value;
|
||||||
let mut unit_index = 0;
|
let mut unit_index = 0;
|
||||||
|
|
||||||
while size >= THREASHOLD && unit_index < self.units.len() - 1 {
|
while size >= THRESHOLD && unit_index < self.units.len() - 1 {
|
||||||
size /= THREASHOLD;
|
size /= THRESHOLD;
|
||||||
unit_index += 1;
|
unit_index += 1;
|
||||||
}
|
}
|
||||||
write!(f, "{:.2} {}", size, self.units[unit_index])
|
write!(f, "{:.2} {}", size, self.units[unit_index])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! impl_unit_wrapper {
|
||||||
|
($wrapper:ident) => {
|
||||||
|
impl From<Unit> for $wrapper {
|
||||||
|
fn from(unit: $crate::app::utils::unit::Unit) -> Self {
|
||||||
|
Self(unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<T> 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user