cassandra_cpp/cassandra/
util.rs

1//! Assorted helper functions used throughout the code.
2
3use crate::Session;
4
5/// `ProtectedInner` is a trait for types that hold an inner value `T` and
6/// provide a method to access it.
7pub(crate) trait ProtectedInner<T> {
8    fn inner(&self) -> T;
9}
10
11/// Interconvert between external and internal representation.
12/// We can freely do this within this crate, but must not allow
13/// our clients to do this.
14pub(crate) trait Protected<T>: ProtectedInner<T> {
15    fn build(inner: T) -> Self;
16}
17
18/// `ProtectedWithSession` is a trait for types that wrap an inner value `T` and provide additional protection via a `Session`.
19/// Types that implement this trait must also implement the `ProtectedInner` trait.
20pub(crate) trait ProtectedWithSession<T>: ProtectedInner<T> {
21    fn build(inner: T, session: Session) -> Self;
22    fn session(&self) -> &Session;
23}
24
25/// Enhance a nullary enum as follows:
26///
27/// * `Display` / `to_string`
28/// * `FromStr` / `parse` (with just a simple `String` as error type)
29/// * Interconvert with another type (via `Protected`).
30/// * `variants` yields an array of all variants.
31///
32/// Only works for nullary enums, i.e., ones where no variants have any arguments.
33///
34/// # Syntax
35///
36/// ```ignore
37/// enhance_nullary_enum(ThisEnum, ThatEnum, {
38///     (ThisVariant1, ThatVariant1, "StringName1"),
39///     (ThisVariant2, ThatVariant2, "StringName2"),
40///     ...
41/// }, omit { ThatVariantOmit1, ThatVariantOmit2 });
42/// ```
43/// where
44///
45/// * `ThisEnum` is the name of the type being enhanced.
46/// * `ThatEnum` is the name of the inner type wrapped by `ThisEnum`.
47/// * Then all variants of `ThisEnum` are listed:
48///   * `ThisVariant`i is the name of the variant.
49///   * `ThatVariant`i is the name of the corresponding variant of `ThatEnum`.
50///   * `StringName`i is the desired string representation of the enum for parsing and printing.
51/// * The `omit` section is optional; any variants of `ThatEnum` listed here
52///   cause `Protected::build` to panic.
53///
54
55// We attempted to use the `macro-attr` crate to achieve this more naturally, but sadly
56// identifier-concatenation functionality is not yet available in stable Rust
57// per https://github.com/rust-lang/rust/issues/29599. We really don't want to use the verbose
58// identifiers (e.g., `CASS_CONSISTENCY_ANY`), so without concatenation we are forced to
59// provide an explicit list of identifiers. However with `macro-attr` there's nowhere to
60// hang them; we can't add them in a custom attribute because custom attributes are unstable.
61// In the end the best approach is just the direct one, as exemplified here.
62
63macro_rules! enhance_nullary_enum {
64    ( $this_name:ident, $that_name: ident, {
65        $( ($this:ident, $that:ident, $name:expr), )*
66        } $( , omit { $( $not_that:ident ),* } )* ) => {
67        impl ::std::fmt::Display for $this_name {
68            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::result::Result<(), ::std::fmt::Error> {
69                write!(f, "{}", match *self {
70                    $( $this_name::$this => $name, )*
71                })
72            }
73        }
74
75        impl ::std::str::FromStr for $this_name {
76            type Err = String;
77
78            fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> {
79                match s {
80                    $( $name => Ok($this_name::$this), )*
81                    _ => Err(format!("Unrecognized {}: {}", stringify!($this_name), s)),
82                }
83            }
84        }
85
86        impl $crate::cassandra::util::ProtectedInner<$that_name> for $this_name {
87            fn inner(&self) -> $that_name {
88                match *self {
89                    $( $this_name::$this => $that_name::$that ),*
90                }
91            }
92        }
93
94        impl $crate::cassandra::util::Protected<$that_name> for $this_name {
95            fn build(inner: $that_name) -> Self {
96                match inner {
97                    $( $that_name::$that => $this_name::$this, )*
98                    $($( $that_name::$not_that => panic!(stringify!(Unexpected variant $that_name::$not_that)), )*)*
99                }
100            }
101        }
102
103        impl $this_name {
104            /// List all the possible values of this enumeration.
105            pub fn variants() -> &'static [$this_name] {
106                // Nasty trick to calculate the length of the iteration - we must mention
107                // a variable inside the layer, even though we never actually use it.
108                static VARIANTS: [ $this_name; 0 $( + ($this_name::$this, 1).1 )* ] =
109                  [ $( $this_name::$this ),* ];
110                &VARIANTS
111            }
112        }
113    };
114}