ubuntu_version/
codename.rs

1use std::{
2    fmt::{self, Display, Formatter},
3    str::FromStr,
4};
5
6#[derive(Debug, Error)]
7pub enum CodenameParseError {
8    #[error("unknown codename string")]
9    NotFound,
10}
11
12/// The codename associated with an Ubuntu version.
13#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
14pub enum Codename {
15    Bionic,
16    Cosmic,
17    Disco,
18    Eoan,
19    Focal,
20    Groovy,
21    Hirsute,
22    Impish,
23    Jammy,
24}
25
26impl Codename {
27    /// The date when this release is to be, or was, EOL'd.
28    pub fn eol_date(self) -> (u32, u32, u32) {
29        let (y, m, d) = self.release_date();
30
31        if y % 2 == 0 && m == 4 {
32            (y + 10, m, d)
33        } else {
34            (y + 1, if m == 4 { 1 } else { 7 }, d)
35        }
36    }
37
38    /// Returns the release date in a `(year, month, date)` format
39    pub fn release_date(self) -> (u32, u32, u32) {
40        match self {
41            Codename::Bionic => (2018, 4, 26),
42            Codename::Cosmic => (2018, 10, 18),
43            Codename::Disco => (2019, 4, 18),
44            Codename::Eoan => (2019, 10, 17),
45            Codename::Focal => (2020, 4, 23),
46            Codename::Groovy => (2020, 10, 22),
47            Codename::Hirsute => (2021, 4, 22),
48            Codename::Impish => (2021, 10, 14),
49            Codename::Jammy => (2022, 4, 21),
50        }
51    }
52
53    /// When this was released, as the time in seconds since the Unix Epoch
54    pub fn release_timestamp(self) -> u64 {
55        // Create with `date "+%s" -d 10/14/2021`
56        match self {
57            Codename::Bionic => 1_524_700_800,
58            Codename::Cosmic => 1_539_820_800,
59            Codename::Disco => 1_555_545_600,
60            Codename::Eoan => 1_571_270_400,
61            Codename::Focal => 1_587_600_000,
62            Codename::Groovy => 1_603_324_800,
63            Codename::Hirsute => 1_619_071_200,
64            Codename::Impish => 1_634_191_200,
65            Codename::Jammy => 1_650_492_000,
66        }
67    }
68}
69
70impl Display for Codename {
71    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
72        fmt.write_str(<&'static str>::from(*self))
73    }
74}
75
76impl FromStr for Codename {
77    type Err = CodenameParseError;
78
79    fn from_str(input: &str) -> Result<Self, Self::Err> {
80        let release = match input {
81            "bionic" => Codename::Bionic,
82            "cosmic" => Codename::Cosmic,
83            "disco" => Codename::Disco,
84            "eoan" => Codename::Eoan,
85            "focal" => Codename::Focal,
86            "groovy" => Codename::Groovy,
87            "hirsute" => Codename::Hirsute,
88            "impish" => Codename::Impish,
89            "jammy" => Codename::Jammy,
90            _ => return Err(CodenameParseError::NotFound),
91        };
92
93        Ok(release)
94    }
95}
96
97impl From<Codename> for &'static str {
98    fn from(codename: Codename) -> Self {
99        match codename {
100            Codename::Bionic => "bionic",
101            Codename::Cosmic => "cosmic",
102            Codename::Disco => "disco",
103            Codename::Eoan => "eoan",
104            Codename::Focal => "focal",
105            Codename::Groovy => "groovy",
106            Codename::Hirsute => "hirsute",
107            Codename::Impish => "impish",
108            Codename::Jammy => "jammy",
109        }
110    }
111}