Skip to main content

diesel/connection/
database_version.rs

1use core::fmt;
2use core::str::FromStr;
3
4/// The version of the database a connection talks to, ordered by major, then minor, then patch.
5#[derive(#[automatically_derived]impl ::core::fmt::Debug for DatabaseVersion {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field3_finish(f,
            "DatabaseVersion major minor patch", &self.major, &self.minor,
            &&self.patch)
    }
}Debug, #[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for DatabaseVersion {
}
#[automatically_derived]impl ::core::clone::Clone for DatabaseVersion {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<u32>;
        *self
    }
}Clone, #[automatically_derived]impl ::core::marker::Copy for DatabaseVersion { }Copy, #[automatically_derived]impl ::core::marker::StructuralPartialEq for
    DatabaseVersion {
}
#[automatically_derived]impl ::core::cmp::PartialEq for DatabaseVersion {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        self.major == other.major && self.minor == other.minor &&
            self.patch == other.patch
    }
}PartialEq, #[automatically_derived]impl ::core::cmp::Eq for DatabaseVersion {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<u32>;
    }
}Eq, #[automatically_derived]impl ::core::cmp::PartialOrd for DatabaseVersion {
    #[inline]
    fn partial_cmp(&self, other: &Self)
        -> ::core::option::Option<::core::cmp::Ordering> {
        ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other))
    }
}PartialOrd, #[automatically_derived]impl ::core::cmp::Ord for DatabaseVersion {
    #[inline]
    fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
        match ::core::cmp::Ord::cmp(&self.major, &other.major) {
            ::core::cmp::Ordering::Equal =>
                match ::core::cmp::Ord::cmp(&self.minor, &other.minor) {
                    ::core::cmp::Ordering::Equal =>
                        ::core::cmp::Ord::cmp(&self.patch, &other.patch),
                    cmp => cmp,
                },
            cmp => cmp,
        }
    }
}Ord, #[automatically_derived]impl ::core::hash::Hash for DatabaseVersion {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
        ::core::hash::Hash::hash(&self.major, state);
        ::core::hash::Hash::hash(&self.minor, state);
        ::core::hash::Hash::hash(&self.patch, state)
    }
}Hash)]
6pub struct DatabaseVersion {
7    major: u32,
8    minor: u32,
9    patch: u32,
10}
11
12impl DatabaseVersion {
13    /// Creates the version `major.minor.patch`.
14    pub const fn new(major: u32, minor: u32, patch: u32) -> Self {
15        Self {
16            major,
17            minor,
18            patch,
19        }
20    }
21
22    /// The major version.
23    pub const fn major(&self) -> u32 {
24        self.major
25    }
26
27    /// The minor version.
28    pub const fn minor(&self) -> u32 {
29        self.minor
30    }
31
32    /// The patch version.
33    pub const fn patch(&self) -> u32 {
34        self.patch
35    }
36}
37
38impl fmt::Display for DatabaseVersion {
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        f.write_fmt(format_args!("{0}.{1}.{2}", self.major, self.minor, self.patch))write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
41    }
42}
43
44/// Parses `major.minor.patch`, the format [`Display`](fmt::Display) writes, with every component
45/// made of decimal digits only.
46///
47/// ```rust
48/// use diesel::connection::DatabaseVersion;
49///
50/// assert_eq!("8.4.11".parse(), Ok(DatabaseVersion::new(8, 4, 11)));
51/// assert!("8.4".parse::<DatabaseVersion>().is_err());
52/// ```
53impl FromStr for DatabaseVersion {
54    type Err = ParseDatabaseVersionError;
55
56    fn from_str(version: &str) -> Result<Self, Self::Err> {
57        let mut components = version.split('.').map(|component| {
58            if component.bytes().all(|byte| byte.is_ascii_digit()) {
59                component.parse().ok()
60            } else {
61                None
62            }
63        });
64        match (
65            components.next(),
66            components.next(),
67            components.next(),
68            components.next(),
69        ) {
70            (Some(Some(major)), Some(Some(minor)), Some(Some(patch)), None) => {
71                Ok(Self::new(major, minor, patch))
72            }
73            _ => Err(ParseDatabaseVersionError),
74        }
75    }
76}
77
78/// The error returned when a string is not a [`DatabaseVersion`] in `major.minor.patch` form.
79#[derive(#[automatically_derived]impl ::core::fmt::Debug for ParseDatabaseVersionError
    {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "ParseDatabaseVersionError")
    }
}Debug, #[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for ParseDatabaseVersionError {
}
#[automatically_derived]impl ::core::clone::Clone for
    ParseDatabaseVersionError {
    #[inline]
    fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]impl ::core::marker::Copy for
    ParseDatabaseVersionError {
}Copy, #[automatically_derived]impl ::core::marker::StructuralPartialEq for
    ParseDatabaseVersionError {
}
#[automatically_derived]impl ::core::cmp::PartialEq for
    ParseDatabaseVersionError {
    #[inline]
    fn eq(&self, other: &Self) -> bool { true }
}PartialEq, #[automatically_derived]impl ::core::cmp::Eq for ParseDatabaseVersionError { }Eq, #[automatically_derived]impl ::core::hash::Hash for ParseDatabaseVersionError
    {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {}
}Hash)]
80pub struct ParseDatabaseVersionError;
81
82impl fmt::Display for ParseDatabaseVersionError {
83    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84        f.write_str("invalid database version, expected `major.minor.patch`")
85    }
86}
87
88impl core::error::Error for ParseDatabaseVersionError {}
89
90#[cfg(test)]
91mod tests {
92    use super::{DatabaseVersion, ParseDatabaseVersionError};
93
94    #[diesel_test_helper::test]
95    fn versions_order_by_major_then_minor_then_patch() {
96        assert!(DatabaseVersion::new(10, 0, 0) > DatabaseVersion::new(9, 6, 20));
97        assert!(DatabaseVersion::new(3, 10, 0) > DatabaseVersion::new(3, 9, 9));
98        assert!(DatabaseVersion::new(3, 9, 10) > DatabaseVersion::new(3, 9, 9));
99    }
100
101    #[diesel_test_helper::test]
102    fn parsing_inverts_display() {
103        for version in [
104            DatabaseVersion::new(0, 0, 0),
105            DatabaseVersion::new(8, 4, 11),
106            DatabaseVersion::new(u32::MAX, u32::MAX, u32::MAX),
107        ] {
108            assert_eq!(version.to_string().parse(), Ok(version));
109        }
110    }
111
112    #[diesel_test_helper::test]
113    fn parsing_rejects_anything_but_three_decimal_components() {
114        for input in [
115            "",
116            "8",
117            "8.4",
118            "8.4.11.2",
119            "8..4",
120            "8..4.11",
121            ".8.4.11",
122            "8.4.11.",
123            "+8.4.11",
124            "8.+4.11",
125            "8.4.-1",
126            " 8.4.11",
127            "8.4.11 ",
128            "8.4.4294967296",
129            "8.4.11-MariaDB",
130        ] {
131            assert_eq!(
132                input.parse::<DatabaseVersion>(),
133                Err(ParseDatabaseVersionError),
134                "{input:?}"
135            );
136        }
137    }
138}