1
#![cfg_attr(docsrs, feature(doc_cfg))]
2
#![doc = include_str!("../README.md")]
3
// @@ begin lint list maintained by maint/add_warning @@
4
#![allow(renamed_and_removed_lints)] // @@REMOVE_WHEN(ci_arti_stable)
5
#![allow(unknown_lints)] // @@REMOVE_WHEN(ci_arti_nightly)
6
#![warn(missing_docs)]
7
#![warn(noop_method_call)]
8
#![warn(unreachable_pub)]
9
#![warn(clippy::all)]
10
#![deny(clippy::await_holding_lock)]
11
#![deny(clippy::cargo_common_metadata)]
12
#![deny(clippy::cast_lossless)]
13
#![deny(clippy::checked_conversions)]
14
#![allow(clippy::cognitive_complexity)] // See arti#2556
15
#![deny(clippy::debug_assert_with_mut_call)]
16
#![deny(clippy::exhaustive_enums)]
17
#![deny(clippy::exhaustive_structs)]
18
#![deny(clippy::expl_impl_clone_on_copy)]
19
#![deny(clippy::fallible_impl_from)]
20
#![deny(clippy::implicit_clone)]
21
#![deny(clippy::large_stack_arrays)]
22
#![warn(clippy::manual_ok_or)]
23
#![deny(clippy::missing_docs_in_private_items)]
24
#![warn(clippy::needless_borrow)]
25
#![warn(clippy::needless_pass_by_value)]
26
#![warn(clippy::option_option)]
27
#![warn(clippy::or_fun_call)]
28
#![deny(clippy::print_stderr)]
29
#![deny(clippy::print_stdout)]
30
#![warn(clippy::rc_buffer)]
31
#![deny(clippy::ref_option_ref)]
32
#![warn(clippy::semicolon_if_nothing_returned)]
33
#![warn(clippy::trait_duplication_in_bounds)]
34
#![deny(clippy::unchecked_time_subtraction)]
35
#![deny(clippy::unnecessary_wraps)]
36
#![warn(clippy::unseparated_literal_suffix)]
37
#![deny(clippy::unwrap_used)]
38
#![deny(clippy::mod_module_files)]
39
#![allow(clippy::let_unit_value)] // This can reasonably be done for explicitness
40
#![allow(clippy::uninlined_format_args)]
41
#![allow(clippy::significant_drop_in_scrutinee)] // arti/-/merge_requests/588/#note_2812945
42
#![allow(clippy::result_large_err)] // temporary workaround for arti#587
43
#![allow(clippy::needless_raw_string_hashes)] // complained-about code is fine, often best
44
#![allow(clippy::needless_lifetimes)] // See arti#1765
45
#![allow(mismatched_lifetime_syntaxes)] // temporary workaround for arti#2060
46
#![allow(clippy::collapsible_if)] // See arti#2342
47
#![deny(clippy::unused_async)]
48
#![deny(clippy::string_slice)] // See arti#2571
49
#![allow(recursion_depth_exceeding_limit)] // arti#2715, rust/issues/159228
50
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
51

            
52
/// Declare an integer type with some named elements.
53
///
54
/// This macro declares a struct that wraps an integer
55
/// type, and allows any integer type as a value.  Some values of this type
56
/// have names, and others do not, but they are all allowed.
57
///
58
/// This macro is suitable for protocol implementations that accept
59
/// any integer on the wire, and have definitions for some of those
60
/// integers.  For example, Tor cell commands are 8-bit integers, but
61
/// not every u8 is a currently recognized Tor command.
62
///
63
/// # Examples
64
/// ```
65
/// use caret::caret_int;
66
/// caret_int! {
67
///     pub struct FruitID(u8) {
68
///         AVOCADO = 7,
69
///         PERSIMMON = 8,
70
///         LONGAN = 99
71
///     }
72
/// }
73
///
74
/// // Known fruits work the way we would expect...
75
/// let a_num: u8 = FruitID::AVOCADO.into();
76
/// assert_eq!(a_num, 7);
77
/// let a_fruit: FruitID = 8.into();
78
/// assert_eq!(a_fruit, FruitID::PERSIMMON);
79
/// assert_eq!(format!("I'd like a {}", FruitID::PERSIMMON),
80
///            "I'd like a PERSIMMON");
81
///
82
/// // And we can construct unknown fruits, if we encounter any.
83
/// let weird_fruit: FruitID = 202.into();
84
/// assert_eq!(format!("I'd like a {}", weird_fruit),
85
///            "I'd like a 202");
86
/// ```
87
#[macro_export]
88
macro_rules! caret_int {
89
    {
90
       $(#[$meta:meta])*
91
       $v:vis struct $name:ident ( $numtype:ty ) {
92
           $(
93
               $(#[$item_meta:meta])*
94
               $id:ident = $num:literal
95
           ),*
96
           $(,)?
97
      }
98
    } => {
99
        #[derive(PartialEq,Eq,Copy,Clone)]
100
        $(#[$meta])*
101
        $v struct $name($numtype);
102

            
103
        impl From<$name> for $numtype {
104
13869033
            fn from(val: $name) -> $numtype { val.0 }
105
        }
106
        impl From<$numtype> for $name {
107
705681
            fn from(num: $numtype) -> $name { $name(num) }
108
        }
109
        impl $name {
110
            $(
111
                $( #[$item_meta] )*
112
                pub const $id: $name = $name($num) ; )*
113
145059
            fn to_str(self) -> Option<&'static str> {
114
145059
                match self {
115
16936
                    $( $name::$id => Some(stringify!($id)), )*
116
948
                    _ => None,
117
                }
118
145059
            }
119
            /// Return true if this value is one that we recognize.
120
2
            $v fn is_recognized(self) -> bool {
121
2
                match self {
122
                    $( $name::$id  => true, )*
123
1
                    _ => false
124
                }
125
2
            }
126
            /// Try to convert this value from one of the recognized names.
127
5791826
            $v fn from_name(name: &str) -> Option<Self> {
128
5791826
                match name {
129
299307
                    $( stringify!($id) => Some($name::$id), )*
130
1671
                    _ => None
131
                }
132
5791826
            }
133
            /// Return the underlying integer that this value represents.
134
13394209
            fn get(self) -> $numtype {
135
13394209
                self.into()
136
13394209
            }
137
        }
138
        impl std::fmt::Display for $name {
139
145056
            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
140
145056
                match self.to_str() {
141
144109
                    Some(s) => write!(f, "{}", s),
142
947
                    None => write!(f, "{}", self.0),
143
                }
144
145056
            }
145
        }
146
        // `#[educe(Debug)]` could do this for us, but let's not deepen this macrology
147
        impl std::fmt::Debug for $name {
148
40226
            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149
40226
                write!(f, "{}({})", stringify!($name), self)
150
40226
            }
151
        }
152
    };
153

            
154
}