Skip to content

rust-amplify/amplify-derive

Rust Amplify Library: derive macros

Build Tests Lints codecov

crates.io Docs unsafe forbidden Apache-2 licensed

Amplifying Rust language capabilities: multiple generic trait implementations, type wrappers, derive macros.

This is a part of Rust language amplification library providing required derive macros.

Minimum supported rust compiler version (MSRV): 1.60.0. Rust edition 2021.

Overview

Display derive

  1. Generate [Display] descriptions using other formatting trait:
     #[derive(Display, Debug)]
     #[display(Debug)]
     struct Some { /* ... */ }
  2. Use existing function for displaying descriptions:
     #[derive(Display)]
     #[display(Int::print)]
     union Int { uint: u32, int: i32 };
    
     impl Int {
         pub fn print(&self) -> String {
             s!("Integer representation")
         }
     }
    Formatting function must return [String] and take a single self argument (if you need formatting with streamed output, use one of existing formatting traits as shown in pt. 1).
  3. Custom format string:
     #[derive(Display)]
     #[display("({x}, {y})")]
     struct Point { x: u32, y: u32 }
  4. Use of doc comments for descrition representation. In this case doc comments may also contain formatting like in the case 3:
     #[macro_use] extern crate amplify;
    
     #[derive(Display)]
     #[display(doc_comments)]
     enum Variants {
         /// Letter A
         A,
    
         /// Letter B
         B,
    
         /// This comment is ignored
         #[display("Letter C")]
         C,
    
         /// Letter {_0}
         Letter(String)
     };
    
     assert_eq!(format!("{}", Variants::C), "Letter C");
     assert_eq!(format!("{}", Variants::Letter(s!("K"))), " Letter K");
    You can also mix in this mode with other fors of display tags on a specific options; in this case doc comments are ignored

Example

Advanced use with enums:

#[derive(Debug, Display)]
#[display(Debug)]
enum Test {
    Some,

    #[display = "OtherName"]
    Other,

    Named {
        x: u8,
    },

    #[display = "Custom{x}"]
    NamedCustom {
        x: u8,
    },

    Unnamed(u16),

    // NB: Use `_`-prefixed indexes for tuple values
    #[display = "Custom{_0}"]
    UnnamedCustom(String),
}

Error derive

Error derive macro works to the full extend only when other derive macros are used. With #[derive(Display)] and [display(doc_comments)] it uses doc comments for generating error descriptions; with #[derive(From)] it may automatically implement transofrations from other error types.

#[derive(Debug, Display, Error)]
#[display(doc_comments)]
enum Error {
    /// I/O operation error
    Io,
    /// Math overflow
    Overflow,
    /// Zero division with {_0}
    ZeroDivision(u16),
}

From derive

Implements [From] trait for the whole entity and/or its separate fields. Works well with #[derive(Error)] and, in many cases may require [Default] implementation (for details, pls see Examples below)

Examples

#[derive(From, Default)]
#[from(::std::io::Error)]
// Structure may contain no parameters
pub struct IoErrorUnit;

#[derive(From, Default)]
#[from(::std::io::Error)] // When no explicit binding is given, structure must implement `Default`
pub struct IoError {
    details: String,

    #[from]
    kind: IoErrorUnit,
}

#[derive(From)]
pub enum Error {
    // You can specify multiple conversions with separate attributes
    #[from(::std::io::Error)]
    #[from(IoError)]
    Io,

    #[from]
    Format(::std::fmt::Error),

    #[from]
    WithFields { details: ::std::str::Utf8Error },

    MultipleFields {
        // ...and you can also covert error type
        #[from(IoErrorUnit)]
        // rest of parameters must implement `Default`
        io: IoError,
        details: String,
    },
}

#[derive(From)]
pub struct Wrapper(u32, i16);

Wrapper derive

Creates rust new type wrapping existing type. Can be used in sturctures containing multiple named or unnamed fields; in this case the field you'd like to wrap should be marked with #[wrap] attribute; otherwise the first field is assumed to be the wrapped one.

Use with multiple fileds requires that you do From and Default derive on the main structure.

Supports automatic implementation of the following traits:

  • amplify::Wrapper
  • AsRef
  • AsMut
  • Borrow
  • BorrowMut
  • Deref
  • DerefMut

Complete usage of this derive macro is possible only with nightly rust compiler with trivial_bounds feature gate set for the crate and nightly feature set. This will give you an automatic implementation for additional traits, it they are implemented for the wrapped type:

  • Display
  • LowerHex
  • UpperHex
  • LowerExp
  • UpperExp
  • Octal
  • Index
  • IndexMut
  • Add
  • AddAssign
  • Sub
  • SubAssign
  • Mul
  • MulAssign
  • Div
  • DivAssign

Other traits, such as PartialEq, Eq, PartialOrd, Ord, Hash can be implemented using standard #[derive] attribute in the same manner as Default, Debug and From

Example

use std::marker::PhantomData;
use amplify::Wrapper;

#[derive(Clone, Wrapper, Default, From, Debug)]
struct Wrapped<T, U>(
    #[wrap]
    #[from]
    HashMap<usize, Vec<U>>,
    PhantomData<T>,
)
where
    U: Sized + Clone;

let w = Wrapped::<(), u8>::default();
assert_eq!(w.into_inner(), HashMap::<usize, Vec<u8>>::default());

Getters derive

Creates getter methods matching field names for all fields within a structure (including public and private fields). Getters return reference types.

Example

#[derive(Getters, Default)]
struct One {
    a: Vec<u8>,
    pub b: bool,
    pub(self) c: u8,
}

let one = One::default();
assert_eq!(one.a(), &Vec::<u8>::default());
assert_eq!(one.b(), &bool::default());
assert_eq!(one.c(), &u8::default());

AsAny derive

Trait [amplify::AsAny] allows simple conversion of any type into a generic "thick" pointer &dyn Any (see [::core::any::Any]), that can be later converted back to the original type with a graceful failing for all other conversions. AsAny derive macro allows to implement this trait for arbitrary time without much hussle:

Example

# #[macro_use] extern crate amplify_derive;
extern crate amplify;
use amplify::AsAny;

#[derive(AsAny, Copy, Clone, PartialEq, Eq, Debug)]
struct Point {
    pub x: u64,
    pub y: u64,
}

#[derive(AsAny, PartialEq, Debug)]
struct Circle {
    pub radius: f64,
    pub center: Point,
}

let mut point = Point { x: 1, y: 2 };
let point_ptr = point.as_any();

let mut circle = Circle {
    radius: 18.,
    center: point,
};
let circle_ptr = circle.as_any();

assert_eq!(point_ptr.downcast_ref(), Some(&point));
assert_eq!(circle_ptr.downcast_ref(), Some(&circle));
assert_eq!(circle_ptr.downcast_ref::<Point>(), None);

let p = point_ptr.downcast_ref::<Point>().unwrap();
assert_eq!(p.x, 1)