helgoboss_midi/
newtype_macros.rs

1/// An error which can occur when converting from a type with a greater value range to one with a
2/// smaller one.
3#[derive(Clone, Eq, PartialEq, Debug, derive_more::Display)]
4#[display(fmt = "converting to type with smaller value range failed")]
5pub struct TryFromGreaterError(pub(crate) ());
6
7#[cfg(feature = "std")]
8impl std::error::Error for TryFromGreaterError {}
9
10/// An error which can occur when parsing a string to one of the MIDI integer types.
11#[derive(Clone, Eq, PartialEq, Debug, derive_more::Display)]
12#[display(fmt = "parsing string to MIDI type failed")]
13pub struct ParseIntError(pub(crate) ());
14
15#[cfg(feature = "std")]
16impl std::error::Error for ParseIntError {}
17
18/// Creates a new type which is represented by a primitive type but has a restricted value range.
19macro_rules! newtype {
20    (
21        $(#[$outer:meta])*
22        name = $name: ident,
23        repr = $repr: ty,
24        max = $max: literal
25    ) => {
26        $(#[$outer])*
27        #[derive(
28            Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, derive_more::Display,
29        )]
30        #[cfg_attr(
31            feature = "serde",
32            derive(serde::Serialize, serde::Deserialize),
33            serde(try_from = "u16")
34        )]
35        pub struct $name(pub(crate) $repr);
36
37        impl $name {
38            /// The smallest value that can be represented by this type.
39            pub const MIN: $name = $name(0);
40
41            /// The largest value that can be represented by this type.
42            pub const MAX: $name = $name($max);
43
44            fn is_valid<T: PartialOrd + From<$repr>>(number: T) -> bool {
45                number >= 0.into() && number <= $max.into()
46            }
47
48            doc_comment::doc_comment! {
49                concat!(
50"Creates a ", stringify!($name), ".
51
52# Panics
53
54This function panics if `value` is greater than ", $max, "."
55                ),
56                pub fn new(value: $repr) -> $name {
57                    #[cfg(feature = "std")]
58                    {
59                        assert!($name::is_valid(value), concat!("{} is not a valid ", stringify!($name), " value"), value);
60                    }
61                    #[cfg(feature = "no_std")]
62                    {
63                        assert!($name::is_valid(value), concat!("not a valid ", stringify!($name), " value"));
64                    }
65                    $name(value)
66                }
67            }
68
69            doc_comment::doc_comment! {
70                concat!(
71"Creates a ", stringify!($name), " without checking `value`.
72
73# Safety
74
75`value` must not be greater than ", $max, "."
76                ),
77                pub const unsafe fn new_unchecked(value: $repr) -> $name {
78                    $name(value)
79                }
80            }
81
82            /// Returns the value as a primitive type.
83            pub const fn get(self) -> $repr {
84                self.0
85            }
86        }
87
88        impl core::str::FromStr for $name {
89            type Err = $crate::ParseIntError;
90
91            fn from_str(source: &str) -> Result<Self, Self::Err> {
92                let primitive = <$repr>::from_str(source).map_err(|_| $crate::ParseIntError(()))?;
93                if !$name::is_valid(primitive) {
94                    return Err($crate::ParseIntError(()));
95                }
96                Ok($name(primitive))
97            }
98        }
99    };
100}
101
102/// Creates a `From` trait implementation from another newtype which has the same or smaller value
103/// range.
104macro_rules! impl_from_newtype_to_newtype {
105    ($from: ty, $into: ty) => {
106        impl From<$from> for $into {
107            fn from(value: $from) -> Self {
108                Self(value.0 as _)
109            }
110        }
111    };
112}
113
114/// Creates a `From` trait implementation from a newtype to a primitive type with a higher
115/// value range.
116macro_rules! impl_from_newtype_to_primitive {
117    ($from: ty, $into: ty) => {
118        impl From<$from> for $into {
119            fn from(value: $from) -> Self {
120                value.0 as _
121            }
122        }
123    };
124}
125
126/// Creates a `From` trait implementation from a primitive with a lower value range to a newtype.
127macro_rules! impl_from_primitive_to_newtype {
128    ($from: ty, $into: ty) => {
129        impl From<$from> for $into {
130            fn from(value: $from) -> Self {
131                Self(value as _)
132            }
133        }
134    };
135}
136
137/// Creates a `TryFrom` trait implementation from a newtype with a higher value range to a newtype.
138macro_rules! impl_try_from_newtype_to_newtype {
139    ($from: ty, $into: ty) => {
140        impl core::convert::TryFrom<$from> for $into {
141            type Error = $crate::TryFromGreaterError;
142
143            fn try_from(value: $from) -> Result<Self, Self::Error> {
144                if !Self::is_valid(value.0) {
145                    return Err($crate::TryFromGreaterError(()));
146                }
147                Ok(Self(value.0 as _))
148            }
149        }
150    };
151}
152
153/// Creates a `TryFrom` trait implementation from a primitive type with a higher value range to a
154/// newtype.
155macro_rules! impl_try_from_primitive_to_newtype {
156    ($from: ty, $into: ty) => {
157        impl core::convert::TryFrom<$from> for $into {
158            type Error = $crate::TryFromGreaterError;
159
160            fn try_from(value: $from) -> Result<Self, Self::Error> {
161                if !Self::is_valid(value) {
162                    return Err($crate::TryFromGreaterError(()));
163                }
164                Ok(Self(value as _))
165            }
166        }
167    };
168}