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}