Skip to main content

apk/types/
sdk.rs

1use nutype::nutype;
2
3/// An Android API level, as used by `minSdkVersion` and `targetSdkVersion`.
4///
5/// Android stores API levels as `int`, so valid values are `1..=i32::MAX`.
6/// Named constants follow `android.os.Build.VERSION_CODES`.
7#[nutype(
8    const_fn,
9    validate(greater_or_equal = 1, less_or_equal = 2_147_483_647),
10    derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Display, TryFrom, Into, FromStr)
11)]
12pub struct ApiLevel(u32);
13
14macro_rules! api_levels {
15    ($($name:ident = $level:literal,)+) => {
16        impl ApiLevel {
17            $(pub const $name: Self = match Self::try_new($level) {
18                Ok(level) => level,
19                Err(_) => panic!("invalid API level constant"),
20            };)+
21        }
22    };
23}
24
25api_levels! {
26    BASE = 1,
27    GINGERBREAD = 9,
28    JELLY_BEAN_MR2 = 18,
29    KITKAT = 19,
30    LOLLIPOP = 21,
31    M = 23,
32    N = 24,
33    O = 26,
34    P = 28,
35    Q = 29,
36    R = 30,
37    S = 31,
38    TIRAMISU = 33,
39    UPSIDE_DOWN_CAKE = 34,
40    VANILLA_ICE_CREAM = 35,
41    BAKLAVA = 36,
42}
43
44impl ApiLevel {
45    pub const fn get(self) -> u32 {
46        self.into_inner()
47    }
48}
49
50/// An `android:versionCode` accepted by Google Play: `1..=2_100_000_000`.
51#[nutype(
52    const_fn,
53    validate(greater_or_equal = 1, less_or_equal = 2_100_000_000),
54    derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Display, TryFrom, Into, FromStr)
55)]
56pub struct VersionCode(u32);
57
58impl VersionCode {
59    pub const ONE: Self = match Self::try_new(1) {
60        Ok(code) => code,
61        Err(_) => panic!("invalid version code constant"),
62    };
63
64    pub const fn get(self) -> u32 {
65        self.into_inner()
66    }
67}
68
69impl Default for VersionCode {
70    fn default() -> Self {
71        Self::ONE
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78
79    #[test]
80    fn bounds() {
81        assert!(matches!(ApiLevel::try_new(0), Err(ApiLevelError::GreaterOrEqualViolated)));
82        assert!(matches!(ApiLevel::try_new(i32::MAX as u32 + 1), Err(ApiLevelError::LessOrEqualViolated)));
83        assert_eq!(ApiLevel::try_new(24).unwrap(), ApiLevel::N);
84        assert!(VersionCode::try_new(0).is_err());
85        assert!(VersionCode::try_new(2_100_000_001).is_err());
86        assert_eq!(VersionCode::try_new(2_100_000_000).unwrap().get(), 2_100_000_000);
87    }
88}