Skip to main content

geopackage_core/
version.rs

1//! GeoPackage file identity: SQLite `application_id` and `user_version` pragmas.
2
3use std::fmt;
4
5/// `application_id` for GeoPackage 1.2+: "GPKG".
6pub const APPLICATION_ID_GPKG: u32 = 0x4750_4B47;
7/// Legacy `application_id` for GeoPackage 1.0: "GP10".
8pub const APPLICATION_ID_GP10: u32 = 0x4750_3130;
9/// Legacy `application_id` for GeoPackage 1.1: "GP11".
10pub const APPLICATION_ID_GP11: u32 = 0x4750_3131;
11
12/// GeoPackage specification version, as declared by a file's pragmas.
13#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
14#[non_exhaustive]
15pub enum GpkgVersion {
16    /// GeoPackage 1.0 (`application_id` "GP10").
17    V1_0,
18    /// GeoPackage 1.1 (`application_id` "GP11").
19    V1_1,
20    /// GeoPackage 1.2.x (`user_version` 102xx).
21    V1_2,
22    /// GeoPackage 1.3.x (`user_version` 103xx).
23    V1_3,
24    /// GeoPackage 1.4.x (`user_version` 104xx).
25    V1_4,
26}
27
28impl GpkgVersion {
29    /// Classifies a file from its `application_id` and `user_version` pragmas.
30    ///
31    /// Returns `None` if the pragmas do not identify a GeoPackage.
32    /// `user_version` values above the newest known 104xx range map to
33    /// `V1_4`, because the spec says clients should attempt to read newer minor
34    /// versions.
35    ///
36    /// # Examples
37    ///
38    /// ```
39    /// use geopackage_core::GpkgVersion;
40    /// use geopackage_core::version::APPLICATION_ID_GPKG;
41    ///
42    /// let version = GpkgVersion::from_pragmas(APPLICATION_ID_GPKG, 10400);
43    /// assert_eq!(version, Some(GpkgVersion::V1_4));
44    /// ```
45    pub fn from_pragmas(application_id: u32, user_version: u32) -> Option<Self> {
46        match application_id {
47            APPLICATION_ID_GP10 => Some(Self::V1_0),
48            APPLICATION_ID_GP11 => Some(Self::V1_1),
49            APPLICATION_ID_GPKG => match user_version / 100 {
50                102 => Some(Self::V1_2),
51                103 => Some(Self::V1_3),
52                v if v >= 104 => Some(Self::V1_4),
53                _ => None,
54            },
55            _ => None,
56        }
57    }
58
59    /// Returns the `user_version` this crate writes for the given version, or
60    /// `None` for 1.0 and 1.1, which predate `user_version` use.
61    pub fn user_version(self) -> Option<u32> {
62        match self {
63            Self::V1_0 | Self::V1_1 => None,
64            Self::V1_2 => Some(10200),
65            Self::V1_3 => Some(10300),
66            Self::V1_4 => Some(10400),
67        }
68    }
69
70    /// Returns the version as written in the specification, such as `"1.4"`.
71    ///
72    /// The patch level is not included. `user_version` records it, but the
73    /// classification drops it: a `user_version` of 10401 and one of 10400
74    /// both classify as [`Self::V1_4`].
75    pub fn as_str(self) -> &'static str {
76        match self {
77            Self::V1_0 => "1.0",
78            Self::V1_1 => "1.1",
79            Self::V1_2 => "1.2",
80            Self::V1_3 => "1.3",
81            Self::V1_4 => "1.4",
82        }
83    }
84}
85
86impl fmt::Display for GpkgVersion {
87    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88        f.write_str(self.as_str())
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95
96    #[test]
97    fn classification() {
98        assert_eq!(
99            GpkgVersion::from_pragmas(APPLICATION_ID_GPKG, 10400),
100            Some(GpkgVersion::V1_4)
101        );
102        assert_eq!(
103            GpkgVersion::from_pragmas(APPLICATION_ID_GPKG, 10201),
104            Some(GpkgVersion::V1_2)
105        );
106        // future minor versions read as newest-known
107        assert_eq!(
108            GpkgVersion::from_pragmas(APPLICATION_ID_GPKG, 10500),
109            Some(GpkgVersion::V1_4)
110        );
111        assert_eq!(
112            GpkgVersion::from_pragmas(APPLICATION_ID_GP10, 0),
113            Some(GpkgVersion::V1_0)
114        );
115        assert_eq!(GpkgVersion::from_pragmas(0x1234_5678, 10400), None);
116        assert_eq!(GpkgVersion::from_pragmas(APPLICATION_ID_GPKG, 0), None);
117    }
118}