Skip to main content

diesel/types/
enum_.rs

1use core::fmt::Display;
2use core::marker::PhantomData;
3
4use crate::backend::Backend;
5use crate::deserialize::{FromSql, FromSqlRef};
6use crate::query_builder::bind_collector::RawBytesBindCollector;
7use crate::serialize::ToSql;
8use crate::sql_types::EnumSqlType;
9
10/// Metadata for an enum variant produced by `diesel-derive` in the `#[derive(Enum)]` macro
11#[derive(#[automatically_derived]
impl ::core::fmt::Debug for EnumVariant {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field3_finish(f, "EnumVariant",
            "discriminant", &self.discriminant, "rust_name", &self.rust_name,
            "sql_name", &&self.sql_name)
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for EnumVariant {
    #[inline]
    fn clone(&self) -> EnumVariant {
        let _: ::core::clone::AssertParamIsClone<i128>;
        let _: ::core::clone::AssertParamIsClone<&'static str>;
        let _: ::core::clone::AssertParamIsClone<&'static str>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for EnumVariant { }Copy)]
12pub struct EnumVariant {
13    /// The discriminant value of the given enum variant
14    pub discriminant: i128,
15    /// The rust side name of the given enum variant
16    pub rust_name: &'static str,
17    /// The sql side name of the given enum variant
18    pub sql_name: &'static str,
19}
20
21/// A helper trait to describe mapping an enum between rust values and database values
22///
23/// This is implemented for different mapping strategies, some of them might
24/// be database dependent, while others might be independent
25#[diagnostic::on_unimplemented(
26    message = "`{Self}` is no valid strategy to map an enum for backend `{DB}`",
27    note = "the `Sqlite` backend only support mapping to `Text` and `Integer` fields"
28)]
29pub trait EnumMapping<DB: Backend> {
30    /// Map an enum variant to the database representation
31    fn map_to_database_value<'b>(
32        output: &mut crate::serialize::Output<'b, '_, DB>,
33        variant: &'static EnumVariant,
34    ) -> crate::serialize::Result;
35
36    /// Construct an enum variant from the database representation
37    ///
38    /// This is expected to return the index of the variant in the `variants` array
39    fn map_from_database_value(
40        raw: DB::RawValue<'_>,
41        type_name: &'static str,
42        variants: &'static [EnumVariant],
43    ) -> crate::deserialize::Result<usize>;
44}
45
46/// Map enum variants to `TEXT` database fields by converting each variant to a string value
47#[derive(#[automatically_derived]
impl ::core::clone::Clone for StringMapping {
    #[inline]
    fn clone(&self) -> StringMapping { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for StringMapping { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for StringMapping {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "StringMapping")
    }
}Debug)]
48pub struct StringMapping;
49
50/// Map enum variants to `INTEGER` (in different sizes) by converting each variant to the relevant
51/// discriminant value
52///
53/// The generic type `T` specifies the Rust side integer type, while the generic type `ST`
54/// specifies the SQL side integer type. Both need to match
55///
56/// This mapping variant requires to specify explicit discriminant values for all enum variants
57#[derive(#[automatically_derived]
impl<T: ::core::fmt::Debug, ST: ::core::fmt::Debug> ::core::fmt::Debug for
    IntMapping<T, ST> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f, "IntMapping",
            &&self.0)
    }
}Debug)]
58pub struct IntMapping<T, ST>(PhantomData<(T, ST)>);
59
60/// Map enum variants to matching database enum variants
61#[derive(#[automatically_derived]
impl ::core::clone::Clone for EnumTypeMapping {
    #[inline]
    fn clone(&self) -> EnumTypeMapping { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for EnumTypeMapping { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for EnumTypeMapping {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "EnumTypeMapping")
    }
}Debug)]
62pub struct EnumTypeMapping;
63
64impl<const ANYWAY: bool, DB> EnumSqlType<ANYWAY, DB> for crate::sql_types::Text
65where
66    DB: Backend,
67    StringMapping: EnumMapping<DB>,
68{
69    type Strategy = crate::types::enum_::StringMapping;
70}
71
72impl<DB> EnumSqlType<true, DB> for crate::sql_types::Integer
73where
74    DB: Backend,
75    crate::types::enum_::IntMapping<i32, Self>: crate::types::enum_::EnumMapping<DB>,
76{
77    type Strategy = crate::types::enum_::IntMapping<i32, Self>;
78}
79
80impl<DB> EnumSqlType<true, DB> for crate::sql_types::BigInt
81where
82    DB: Backend,
83    crate::types::enum_::IntMapping<i64, Self>: crate::types::enum_::EnumMapping<DB>,
84{
85    type Strategy = crate::types::enum_::IntMapping<i64, Self>;
86}
87
88impl<DB> EnumSqlType<true, DB> for crate::sql_types::SmallInt
89where
90    DB: Backend,
91    crate::types::enum_::IntMapping<i16, Self>: crate::types::enum_::EnumMapping<DB>,
92{
93    type Strategy = crate::types::enum_::IntMapping<i16, Self>;
94}
95
96impl<DB> EnumSqlType<true, DB> for crate::sql_types::TinyInt
97where
98    DB: Backend,
99    IntMapping<i8, Self>: crate::types::enum_::EnumMapping<DB>,
100{
101    type Strategy = crate::types::enum_::IntMapping<i8, Self>;
102}
103
104impl<DB> EnumMapping<DB> for StringMapping
105where
106    DB: Backend,
107    for<'a> &'a str: FromSqlRef<'a, crate::sql_types::Text, DB>,
108    &'static str: ToSql<crate::sql_types::Text, DB>,
109{
110    fn map_to_database_value<'b>(
111        output: &mut crate::serialize::Output<'b, '_, DB>,
112        variant: &'static EnumVariant,
113    ) -> crate::serialize::Result {
114        <&str as crate::serialize::ToSql<crate::sql_types::Text, DB>>::to_sql(
115            &variant.sql_name,
116            output,
117        )
118    }
119
120    fn map_from_database_value(
121        mut raw: <DB as crate::backend::Backend>::RawValue<'_>,
122        type_name: &'static str,
123        variants: &'static [EnumVariant],
124    ) -> crate::deserialize::Result<usize> {
125        let s = <&str as FromSqlRef<crate::sql_types::Text, DB>>::from_sql(&mut raw)?;
126        Self::from_variant_name(type_name, variants, s)
127    }
128}
129
130impl StringMapping {
131    #[doc(hidden)]
132    pub fn from_variant_name(
133        type_name: &'static str,
134        variants: &'static [EnumVariant],
135        s: &str,
136    ) -> crate::deserialize::Result<usize> {
137        variants
138            .iter()
139            .position(|v| v.sql_name == s)
140            .ok_or_else(|| {
141                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("Invalid enum variant `{1}` for `{2}`. Allowed variants are {0}",
                variants.iter().map(|v|
                                ::alloc::__export::must_use({
                                        ::alloc::fmt::format(format_args!("`{0}`", v.sql_name))
                                    })).collect::<alloc::vec::Vec<_>>().join(", "), s,
                type_name))
    })alloc::format!(
142                    "Invalid enum variant `{s}` for `{type_name}`. Allowed variants are {}",
143                    variants
144                        .iter()
145                        .map(|v| alloc::format!("`{}`", v.sql_name))
146                        .collect::<alloc::vec::Vec<_>>()
147                        .join(", ")
148                )
149                .into()
150            })
151    }
152}
153
154impl<T, ST, DB> EnumMapping<DB> for IntMapping<T, ST>
155where
156    for<'a> DB: Backend<BindCollector<'a> = RawBytesBindCollector<DB>>,
157    T: ToSql<ST, DB> + FromSql<ST, DB>,
158    i128: TryInto<T, Error: Display> + TryFrom<T, Error: Display>,
159{
160    fn map_to_database_value<'b>(
161        output: &mut crate::serialize::Output<'b, '_, DB>,
162        variant: &'static EnumVariant,
163    ) -> crate::serialize::Result {
164        let v = variant.discriminant.try_into().map_err(|e| {
165            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("Failed to convert discriminate to {0}: {1}",
                core::any::type_name::<T>(), e))
    })alloc::format!(
166                "Failed to convert discriminate to {}: {e}",
167                core::any::type_name::<T>()
168            )
169        })?;
170        <T as ToSql<ST, DB>>::to_sql(&v, &mut output.reborrow())
171    }
172
173    fn map_from_database_value(
174        raw: <DB as crate::backend::Backend>::RawValue<'_>,
175        type_name: &'static str,
176        variants: &'static [EnumVariant],
177    ) -> crate::deserialize::Result<usize> {
178        let i = <T as FromSql<ST, DB>>::from_sql(raw)?;
179        Self::from_discriminant(type_name, variants, i)
180    }
181}
182
183impl<T, ST> IntMapping<T, ST>
184where
185    i128: TryFrom<T, Error: Display>,
186{
187    #[doc(hidden)]
188    pub fn from_discriminant(
189        type_name: &'static str,
190        variants: &'static [EnumVariant],
191        value: T,
192    ) -> crate::deserialize::Result<usize> {
193        let i: i128 = value.try_into().map_err(|e| {
194            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("Failed to convert {0} to discriminate: {1}",
                core::any::type_name::<T>(), e))
    })alloc::format!(
195                "Failed to convert {} to discriminate: {e}",
196                core::any::type_name::<T>()
197            )
198        })?;
199        variants
200            .iter()
201            .position(|v| v.discriminant == i)
202            .ok_or_else(|| {
203                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("Invalid enum variant `{1}` for `{2}`. Allowed variants are {0}",
                variants.iter().map(|v|
                                ::alloc::__export::must_use({
                                        ::alloc::fmt::format(format_args!("`{0}`", v.discriminant))
                                    })).collect::<alloc::vec::Vec<_>>().join(", "), i,
                type_name))
    })alloc::format!(
204                    "Invalid enum variant `{i}` for `{type_name}`. Allowed variants are {}",
205                    variants
206                        .iter()
207                        .map(|v| alloc::format!("`{}`", v.discriminant))
208                        .collect::<alloc::vec::Vec<_>>()
209                        .join(", ")
210                )
211                .into()
212            })
213    }
214}
215
216#[doc(hidden)]
217/// Implementing this soundly requires that a reference of the Self type
218/// can be created out of a i128, which is the case for signed integers with
219/// smaller bit sizes
220#[allow(unsafe_code)]
221pub unsafe trait IntegerMappingHelper
222where
223    Self: TryFrom<i128, Error: Display>,
224{
225    fn as_ref(v: &i128) -> Result<&Self, alloc::boxed::Box<dyn core::error::Error + Send + Sync>> {
226        let _: Self = (*v).try_into().map_err(|e| {
227            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("Failed to convert discriminate to {0}: {1}",
                core::any::type_name::<Self>(), e))
    })alloc::format!(
228                "Failed to convert discriminate to {}: {e}",
229                core::any::type_name::<Self>()
230            )
231        })?;
232        let v = core::slice::from_ref(v);
233
234        let (front, v, tail) = unsafe {
235            // SAFETY: This cast is sound as the trait requires that Self is a smaller
236            // signed integer type than i128. A i128 consists conceptually out of multiple
237            // values of a smaller integer
238            v.align_to::<Self>()
239        };
240        if true {
    if !front.is_empty() {
        ::core::panicking::panic("assertion failed: front.is_empty()")
    };
};debug_assert!(front.is_empty());
241        if true {
    if !tail.is_empty() {
        ::core::panicking::panic("assertion failed: tail.is_empty()")
    };
};debug_assert!(tail.is_empty());
242
243        if falsecfg!(target_endian = "big") {
244            Ok(v.last().expect("We get at least one slice element"))
245        } else {
246            Ok(v.first().expect("We get at least one slice element"))
247        }
248    }
249}
250
251// SAFETY: These impls are safe as the type sizes are smaller
252// than that one of i128 and also the alignment of any
253// of these types is smaller or equal to i128
254#[allow(unsafe_code)]
255unsafe impl IntegerMappingHelper for i64 {}
256#[allow(unsafe_code)]
257unsafe impl IntegerMappingHelper for i32 {}
258#[allow(unsafe_code)]
259unsafe impl IntegerMappingHelper for i16 {}
260#[allow(unsafe_code)]
261unsafe impl IntegerMappingHelper for i8 {}
262
263#[cfg(test)]
264mod tests {
265    use super::IntegerMappingHelper;
266
267    #[test]
268    fn check_as_ref_i64() {
269        assert_eq!(i64::as_ref(&1).unwrap(), &1);
270        assert_eq!(i64::as_ref(&42).unwrap(), &42);
271        assert_eq!(i64::as_ref(&0).unwrap(), &0);
272        assert_eq!(i64::as_ref(&(i64::MAX as i128)).unwrap(), &i64::MAX);
273        assert_eq!(i64::as_ref(&(i64::MIN as i128)).unwrap(), &i64::MIN);
274        assert!(i64::as_ref(&i128::MAX).is_err());
275        assert!(i64::as_ref(&i128::MIN).is_err());
276        assert!(i64::as_ref(&(i64::MAX as i128 + 1)).is_err());
277        assert!(i64::as_ref(&(i64::MIN as i128 - 1)).is_err());
278    }
279
280    #[test]
281    fn check_as_ref_i32() {
282        assert_eq!(i32::as_ref(&1).unwrap(), &1);
283        assert_eq!(i32::as_ref(&42).unwrap(), &42);
284        assert_eq!(i32::as_ref(&0).unwrap(), &0);
285        assert_eq!(i32::as_ref(&(i32::MAX as i128)).unwrap(), &i32::MAX);
286        assert_eq!(i32::as_ref(&(i32::MIN as i128)).unwrap(), &i32::MIN);
287        assert!(i32::as_ref(&i128::MAX).is_err());
288        assert!(i32::as_ref(&i128::MIN).is_err());
289        assert!(i32::as_ref(&(i64::MAX as i128)).is_err());
290        assert!(i32::as_ref(&(i64::MIN as i128)).is_err());
291        assert!(i32::as_ref(&(i32::MAX as i128 + 1)).is_err());
292        assert!(i32::as_ref(&(i32::MIN as i128 - 1)).is_err());
293    }
294
295    #[test]
296    fn check_as_ref_i16() {
297        assert_eq!(i16::as_ref(&1).unwrap(), &1);
298        assert_eq!(i16::as_ref(&42).unwrap(), &42);
299        assert_eq!(i16::as_ref(&0).unwrap(), &0);
300        assert_eq!(i16::as_ref(&(i16::MAX as i128)).unwrap(), &i16::MAX);
301        assert_eq!(i16::as_ref(&(i16::MIN as i128)).unwrap(), &i16::MIN);
302        assert!(i16::as_ref(&i128::MAX).is_err());
303        assert!(i16::as_ref(&i128::MIN).is_err());
304        assert!(i16::as_ref(&(i32::MAX as i128)).is_err());
305        assert!(i16::as_ref(&(i32::MIN as i128)).is_err());
306        assert!(i16::as_ref(&(i16::MAX as i128 + 1)).is_err());
307        assert!(i16::as_ref(&(i16::MIN as i128 - 1)).is_err());
308    }
309
310    #[test]
311    fn check_as_ref_i8() {
312        assert_eq!(i8::as_ref(&1).unwrap(), &1);
313        assert_eq!(i8::as_ref(&42).unwrap(), &42);
314        assert_eq!(i8::as_ref(&0).unwrap(), &0);
315        assert_eq!(i8::as_ref(&(i8::MAX as i128)).unwrap(), &i8::MAX);
316        assert_eq!(i8::as_ref(&(i8::MIN as i128)).unwrap(), &i8::MIN);
317        assert!(i8::as_ref(&i128::MAX).is_err());
318        assert!(i8::as_ref(&i128::MIN).is_err());
319        assert!(i8::as_ref(&(i16::MAX as i128)).is_err());
320        assert!(i8::as_ref(&(i16::MIN as i128)).is_err());
321        assert!(i8::as_ref(&(i8::MAX as i128 + 1)).is_err());
322        assert!(i8::as_ref(&(i8::MIN as i128 - 1)).is_err());
323    }
324}