10
10
//!
11
11
//! This module provides `Attrs`, a type-safe dictionary that can store heterogeneous values
12
12
//! and serialize/deserialize them using serde. All stored values must implement
13
- //! `Serialize + DeserializeOwned ` to ensure the entire dictionary can be serialized.
13
+ //! `AttrValue ` to ensure the entire dictionary can be serialized.
14
14
//!
15
15
//! Keys are automatically registered at compile time using the `declare_attrs!` macro and the
16
16
//! inventory crate, eliminating the need for manual registry management.
@@ -100,6 +100,8 @@ use std::ops::Index;
100
100
use std:: ops:: IndexMut ;
101
101
use std:: sync:: LazyLock ;
102
102
103
+ use chrono:: DateTime ;
104
+ use chrono:: Utc ;
103
105
use erased_serde:: Deserializer as ErasedDeserializer ;
104
106
use erased_serde:: Serialize as ErasedSerialize ;
105
107
use serde:: Deserialize ;
@@ -125,8 +127,12 @@ pub struct AttrKeyInfo {
125
127
/// Deserializer function that deserializes directly from any deserializer
126
128
pub deserialize_erased :
127
129
fn ( & mut dyn ErasedDeserializer ) -> Result < Box < dyn SerializableValue > , erased_serde:: Error > ,
128
- // Meta-attributes.
130
+ /// Meta-attributes.
129
131
pub meta : & ' static LazyLock < Attrs > ,
132
+ /// Display an attribute value using AttrValue::display.
133
+ pub display : fn ( & dyn SerializableValue ) -> String ,
134
+ /// Parse an attribute value using AttrValue::parse.
135
+ pub parse : fn ( & str ) -> Result < Box < dyn SerializableValue > , anyhow:: Error > ,
130
136
}
131
137
132
138
inventory:: collect!( AttrKeyInfo ) ;
@@ -192,7 +198,7 @@ impl<T: 'static> Clone for Key<T> {
192
198
impl < T : ' static > Copy for Key < T > { }
193
199
194
200
// Enable attr[key] syntax.
195
- impl < T : Send + Sync + Serialize + DeserializeOwned + Named + ' static > Index < Key < T > > for Attrs {
201
+ impl < T : AttrValue > Index < Key < T > > for Attrs {
196
202
type Output = T ;
197
203
198
204
fn index ( & self , key : Key < T > ) -> & Self :: Output {
@@ -202,14 +208,113 @@ impl<T: Send + Sync + Serialize + DeserializeOwned + Named + 'static> Index<Key<
202
208
203
209
// TODO: separately type keys with defaults, so that we can statically enforce that indexmut is only
204
210
// called on keys with defaults.
205
- impl < T : Send + Sync + Serialize + DeserializeOwned + Named + Clone + ' static > IndexMut < Key < T > >
206
- for Attrs
207
- {
211
+ impl < T : AttrValue > IndexMut < Key < T > > for Attrs {
208
212
fn index_mut ( & mut self , key : Key < T > ) -> & mut Self :: Output {
209
213
self . get_mut ( key) . unwrap ( )
210
214
}
211
215
}
212
216
217
+ /// This trait must be implemented by all attribute values. In addition to enforcing
218
+ /// the supertrait `Named + Sized + Serialize + DeserializeOwned + Send + Sync + Clone`,
219
+ /// `AttrValue` requires that the type be representable in "display" format.
220
+ ///
221
+ /// `AttrValue` includes its own `display` and `parse` so that behavior can be tailored
222
+ /// for attribute purposes specifically, allowing common types like `Duration` to be used
223
+ /// without modification.
224
+ ///
225
+ /// This crate includes a derive macro for AttrValue, which uses the type's
226
+ /// `std::string::ToString` for display, and `std::str::FromStr` for parsing.
227
+ pub trait AttrValue :
228
+ Named + Sized + Serialize + DeserializeOwned + Send + Sync + Clone + ' static
229
+ {
230
+ /// Display the value, typically using [`std::fmt::Display`].
231
+ /// This is called to show the output in human-readable form.
232
+ fn display ( & self ) -> String ;
233
+
234
+ /// Parse a value from a string, typically using [`std::str::FromStr`].
235
+ fn parse ( value : & str ) -> Result < Self , anyhow:: Error > ;
236
+ }
237
+
238
+ /// Macro to implement AttrValue for types that implement ToString and FromStr.
239
+ ///
240
+ /// This macro provides a convenient way to implement AttrValue for types that already
241
+ /// have string conversion capabilities through the standard ToString and FromStr traits.
242
+ ///
243
+ /// # Usage
244
+ ///
245
+ /// ```ignore
246
+ /// impl_attrvalue!(i32, u64, f64);
247
+ /// ```
248
+ ///
249
+ /// This will generate AttrValue implementations for i32, u64, and f64 that use
250
+ /// their ToString and FromStr implementations for display and parsing.
251
+ #[ macro_export]
252
+ macro_rules! impl_attrvalue {
253
+ ( $( $ty: ty) ,+ $( , ) ?) => {
254
+ $(
255
+ impl $crate:: attrs:: AttrValue for $ty {
256
+ fn display( & self ) -> String {
257
+ self . to_string( )
258
+ }
259
+
260
+ fn parse( value: & str ) -> Result <Self , anyhow:: Error > {
261
+ value. parse( ) . map_err( |e| anyhow:: anyhow!( "failed to parse {}: {}" , stringify!( $ty) , e) )
262
+ }
263
+ }
264
+ ) +
265
+ } ;
266
+ }
267
+
268
+ // pub use impl_attrvalue;
269
+
270
+ // Implement AttrValue for common standard library types
271
+ impl_attrvalue ! (
272
+ bool ,
273
+ i8 ,
274
+ i16 ,
275
+ i32 ,
276
+ i64 ,
277
+ i128 ,
278
+ isize ,
279
+ u8 ,
280
+ u16 ,
281
+ u32 ,
282
+ u64 ,
283
+ u128 ,
284
+ usize ,
285
+ f32 ,
286
+ f64 ,
287
+ String ,
288
+ std:: net:: IpAddr ,
289
+ std:: net:: Ipv4Addr ,
290
+ std:: net:: Ipv6Addr ,
291
+ crate :: ActorId ,
292
+ ndslice:: Shape ,
293
+ ndslice:: Point ,
294
+ ) ;
295
+
296
+ impl AttrValue for std:: time:: Duration {
297
+ fn display ( & self ) -> String {
298
+ humantime:: format_duration ( * self ) . to_string ( )
299
+ }
300
+
301
+ fn parse ( value : & str ) -> Result < Self , anyhow:: Error > {
302
+ Ok ( humantime:: parse_duration ( value) ?)
303
+ }
304
+ }
305
+
306
+ impl AttrValue for std:: time:: SystemTime {
307
+ fn display ( & self ) -> String {
308
+ let datetime: DateTime < Utc > = self . clone ( ) . into ( ) ;
309
+ datetime. to_rfc3339 ( )
310
+ }
311
+
312
+ fn parse ( value : & str ) -> Result < Self , anyhow:: Error > {
313
+ let datetime = DateTime :: parse_from_rfc3339 ( value) ?;
314
+ Ok ( datetime. into ( ) )
315
+ }
316
+ }
317
+
213
318
// Internal trait for type-erased serialization
214
319
#[ doc( hidden) ]
215
320
pub trait SerializableValue : Send + Sync {
@@ -223,7 +328,7 @@ pub trait SerializableValue: Send + Sync {
223
328
fn cloned ( & self ) -> Box < dyn SerializableValue > ;
224
329
}
225
330
226
- impl < T : Serialize + Send + Sync + Clone + ' static > SerializableValue for T {
331
+ impl < T : AttrValue > SerializableValue for T {
227
332
fn as_any ( & self ) -> & dyn Any {
228
333
self
229
334
}
@@ -245,7 +350,7 @@ impl<T: Serialize + Send + Sync + Clone + 'static> SerializableValue for T {
245
350
///
246
351
/// This dictionary stores key-value pairs where:
247
352
/// - Keys are type-safe and must be predefined with their associated types
248
- /// - Values must implement `Send + Sync + Serialize + DeserializeOwned + Named + 'static`
353
+ /// - Values must implement [`AttrValue`]
249
354
/// - The entire dictionary can be serialized to/from JSON automatically
250
355
///
251
356
/// # Type Safety
@@ -271,20 +376,11 @@ impl Attrs {
271
376
}
272
377
273
378
/// Set a value for the given key.
274
- pub fn set < T : Send + Sync + Serialize + DeserializeOwned + Named + Clone + ' static > (
275
- & mut self ,
276
- key : Key < T > ,
277
- value : T ,
278
- ) {
379
+ pub fn set < T : AttrValue > ( & mut self , key : Key < T > , value : T ) {
279
380
self . values . insert ( key. name , Box :: new ( value) ) ;
280
381
}
281
382
282
- fn maybe_set_from_default <
283
- T : Send + Sync + Serialize + DeserializeOwned + Named + Clone + ' static ,
284
- > (
285
- & mut self ,
286
- key : Key < T > ,
287
- ) {
383
+ fn maybe_set_from_default < T : AttrValue > ( & mut self , key : Key < T > ) {
288
384
if self . contains_key ( key) {
289
385
return ;
290
386
}
@@ -294,10 +390,7 @@ impl Attrs {
294
390
295
391
/// Get a value for the given key, returning None if not present. If the key has a default value,
296
392
/// that is returned instead.
297
- pub fn get < T : Send + Sync + Serialize + DeserializeOwned + Named + ' static > (
298
- & self ,
299
- key : Key < T > ,
300
- ) -> Option < & T > {
393
+ pub fn get < T : AttrValue > ( & self , key : Key < T > ) -> Option < & T > {
301
394
self . values
302
395
. get ( key. name )
303
396
. and_then ( |value| value. as_any ( ) . downcast_ref :: < T > ( ) )
@@ -306,30 +399,21 @@ impl Attrs {
306
399
307
400
/// Get a mutable reference to a value for the given key. If the key has a default value, it is
308
401
/// first set, and then returned as a mutable reference.
309
- pub fn get_mut < T : Send + Sync + Serialize + DeserializeOwned + Named + Clone + ' static > (
310
- & mut self ,
311
- key : Key < T > ,
312
- ) -> Option < & mut T > {
402
+ pub fn get_mut < T : AttrValue > ( & mut self , key : Key < T > ) -> Option < & mut T > {
313
403
self . maybe_set_from_default ( key) ;
314
404
self . values
315
405
. get_mut ( key. name )
316
406
. and_then ( |value| value. as_any_mut ( ) . downcast_mut :: < T > ( ) )
317
407
}
318
408
319
409
/// Remove a value for the given key, returning it if present.
320
- pub fn remove < T : Send + Sync + Serialize + DeserializeOwned + Named + ' static > (
321
- & mut self ,
322
- key : Key < T > ,
323
- ) -> bool {
410
+ pub fn remove < T : AttrValue > ( & mut self , key : Key < T > ) -> bool {
324
411
// TODO: return value (this is tricky because of the type erasure)
325
412
self . values . remove ( key. name ) . is_some ( )
326
413
}
327
414
328
415
/// Checks if the given key exists in the dictionary.
329
- pub fn contains_key < T : Send + Sync + Serialize + DeserializeOwned + Named + ' static > (
330
- & self ,
331
- key : Key < T > ,
332
- ) -> bool {
416
+ pub fn contains_key < T : AttrValue > ( & self , key : Key < T > ) -> bool {
333
417
self . values . contains_key ( key. name )
334
418
}
335
419
@@ -533,6 +617,19 @@ macro_rules! const_ascii_lowercase {
533
617
} } ;
534
618
}
535
619
620
+ /// Macro to check check that a trait is implemented, generating a
621
+ /// nice error message if it isn't.
622
+ #[ doc( hidden) ]
623
+ #[ macro_export]
624
+ macro_rules! assert_impl {
625
+ ( $ty: ty, $trait: path) => {
626
+ const _: fn ( ) = || {
627
+ fn check<T : $trait>( ) { }
628
+ check:: <$ty>( ) ;
629
+ } ;
630
+ } ;
631
+ }
632
+
536
633
/// Declares attribute keys using a lazy_static! style syntax.
537
634
///
538
635
/// # Syntax
@@ -595,6 +692,8 @@ macro_rules! declare_attrs {
595
692
596
693
// Handle single attribute key with default value and meta attributes
597
694
( @single $( @meta( $( $meta_key: ident = $meta_value: expr) ,* $( , ) ?) ) * $( #[ $attr: meta] ) * ; $vis: vis attr $name: ident: $type: ty = $default: expr; ) => {
695
+ $crate:: assert_impl!( $type, $crate:: attrs:: AttrValue ) ;
696
+
598
697
// Create a static default value
599
698
$crate:: paste! {
600
699
static [ <$name _DEFAULT>] : $type = $default;
@@ -611,6 +710,8 @@ macro_rules! declare_attrs {
611
710
612
711
$( #[ $attr] ) *
613
712
$vis static $name: $crate:: attrs:: Key <$type> = {
713
+ $crate:: assert_impl!( $type, $crate:: attrs:: AttrValue ) ;
714
+
614
715
const FULL_NAME : & str = concat!( std:: module_path!( ) , "::" , stringify!( $name) ) ;
615
716
const LOWER_NAME : & str = $crate:: const_ascii_lowercase!( FULL_NAME ) ;
616
717
$crate:: paste! {
@@ -635,12 +736,22 @@ macro_rules! declare_attrs {
635
736
Ok ( Box :: new( value) as Box <dyn $crate:: attrs:: SerializableValue >)
636
737
} ,
637
738
meta: $crate:: paste! { & [ <$name _META_ATTRS>] } ,
739
+ display: |value: & dyn $crate:: attrs:: SerializableValue | {
740
+ let value = value. as_any( ) . downcast_ref:: <$type>( ) . unwrap( ) ;
741
+ $crate:: attrs:: AttrValue :: display( value)
742
+ } ,
743
+ parse: |value: & str | {
744
+ let value: $type = $crate:: attrs:: AttrValue :: parse( value) ?;
745
+ Ok ( Box :: new( value) as Box <dyn $crate:: attrs:: SerializableValue >)
746
+ } ,
638
747
}
639
748
}
640
749
} ;
641
750
642
751
// Handle single attribute key without default value but with meta attributes
643
752
( @single $( @meta( $( $meta_key: ident = $meta_value: expr) ,* $( , ) ?) ) * $( #[ $attr: meta] ) * ; $vis: vis attr $name: ident: $type: ty; ) => {
753
+ $crate:: assert_impl!( $type, $crate:: attrs:: AttrValue ) ;
754
+
644
755
$crate:: paste! {
645
756
static [ <$name _META_ATTRS>] : std:: sync:: LazyLock <$crate:: attrs:: Attrs > =
646
757
std:: sync:: LazyLock :: new( || {
@@ -676,6 +787,14 @@ macro_rules! declare_attrs {
676
787
Ok ( Box :: new( value) as Box <dyn $crate:: attrs:: SerializableValue >)
677
788
} ,
678
789
meta: $crate:: paste! { & [ <$name _META_ATTRS>] } ,
790
+ display: |value: & dyn $crate:: attrs:: SerializableValue | {
791
+ let value = value. as_any( ) . downcast_ref:: <$type>( ) . unwrap( ) ;
792
+ $crate:: attrs:: AttrValue :: display( value)
793
+ } ,
794
+ parse: |value: & str | {
795
+ let value: $type = $crate:: attrs:: AttrValue :: parse( value) ?;
796
+ Ok ( Box :: new( value) as Box <dyn $crate:: attrs:: SerializableValue >)
797
+ } ,
679
798
}
680
799
}
681
800
} ;
@@ -693,7 +812,7 @@ mod tests {
693
812
attr TEST_TIMEOUT : Duration ;
694
813
attr TEST_COUNT : u32 ;
695
814
@meta( TEST_COUNT = 42 )
696
- attr TEST_NAME : String ;
815
+ pub attr TEST_NAME : String ;
697
816
}
698
817
699
818
#[ test]
0 commit comments