Skip to main content

blue_build_utils/
platform.rs

1use std::{
2    path::{Path, PathBuf},
3    str::FromStr,
4};
5
6use clap::ValueEnum;
7use miette::bail;
8use oci_client::Reference;
9use serde::{Deserialize, Serialize};
10
11#[derive(Debug, Clone, Copy, ValueEnum, PartialEq, Eq, Hash)]
12pub enum Platform {
13    #[value(name = "linux/amd64")]
14    LinuxAmd64,
15
16    #[value(name = "linux/amd64/v2")]
17    LinuxAmd64V2,
18
19    #[value(name = "linux/arm64")]
20    LinuxArm64,
21
22    #[value(name = "linux/arm")]
23    LinuxArm,
24
25    #[value(name = "linux/arm/v6")]
26    LinuxArmV6,
27
28    #[value(name = "linux/arm/v7")]
29    LinuxArmV7,
30
31    #[value(name = "linux/386")]
32    Linux386,
33
34    #[value(name = "linux/loong64")]
35    LinuxLoong64,
36
37    #[value(name = "linux/mips")]
38    LinuxMips,
39
40    #[value(name = "linux/mipsle")]
41    LinuxMipsle,
42
43    #[value(name = "linux/mips64")]
44    LinuxMips64,
45
46    #[value(name = "linux/mips64le")]
47    LinuxMips64le,
48
49    #[value(name = "linux/ppc64")]
50    LinuxPpc64,
51
52    #[value(name = "linux/ppc64le")]
53    LinuxPpc64le,
54
55    #[value(name = "linux/riscv64")]
56    LinuxRiscv64,
57
58    #[value(name = "linux/s390x")]
59    LinuxS390x,
60}
61
62impl Platform {
63    /// The architecture of the platform.
64    #[must_use]
65    pub const fn arch(&self) -> &str {
66        match *self {
67            Self::LinuxAmd64 | Self::LinuxAmd64V2 => "amd64",
68            Self::LinuxArm64 => "arm64",
69            Self::LinuxArm | Self::LinuxArmV6 | Self::LinuxArmV7 => "arm",
70            Self::Linux386 => "386",
71            Self::LinuxLoong64 => "loong64",
72            Self::LinuxMips => "mips",
73            Self::LinuxMipsle => "mipsle",
74            Self::LinuxMips64 => "mips64",
75            Self::LinuxMips64le => "mips64le",
76            Self::LinuxPpc64 => "ppc64",
77            Self::LinuxPpc64le => "ppc64le",
78            Self::LinuxRiscv64 => "riscv64",
79            Self::LinuxS390x => "s390x",
80        }
81    }
82
83    /// The variant of the platform.
84    #[must_use]
85    pub const fn variant(&self) -> Option<&str> {
86        match *self {
87            Self::LinuxAmd64V2 => Some("v2"),
88            Self::LinuxArmV6 => Some("v6"),
89            Self::LinuxArmV7 => Some("v7"),
90            _ => None,
91        }
92    }
93
94    /// Get a tag friendly version of the platform.
95    #[must_use]
96    pub fn tagged_image(&self, image: &Reference) -> Reference {
97        Reference::with_tag(
98            image.registry().to_string(),
99            image.repository().to_string(),
100            format!("{}_{self}", image.tag().unwrap_or("latest")).replace('/', "_"),
101        )
102    }
103
104    /// Get a tagged path.
105    #[must_use]
106    pub fn tagged_path(&self, path: &Path) -> Option<PathBuf> {
107        if let Some(file_stem) = path.file_stem()
108            && let Some(extension) = path.extension()
109        {
110            Some(
111                path.with_file_name(format!(
112                    "{}_{}",
113                    file_stem.display(),
114                    self.to_string().replace('/', "_")
115                ))
116                .with_extension(extension),
117            )
118        } else {
119            None
120        }
121    }
122}
123
124impl Default for Platform {
125    fn default() -> Self {
126        match std::env::consts::ARCH {
127            "x86_64" => Self::LinuxAmd64,
128            "aarch64" => Self::LinuxArm64,
129            "x86" => Self::Linux386,
130            "arm" => Self::LinuxArm,
131            "mips" => Self::LinuxMips,
132            "mips32r6" => Self::LinuxMipsle,
133            "mips64" => Self::LinuxMips64,
134            "mips64r6" => Self::LinuxMips64le,
135            "powerpc64" => Self::LinuxPpc64,
136            "riscv64" => Self::LinuxRiscv64,
137            "s390x" => Self::LinuxS390x,
138            "loongarch64" => Self::LinuxLoong64,
139            arch => unimplemented!("Arch {arch} is unsupported"),
140        }
141    }
142}
143
144impl std::fmt::Display for Platform {
145    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
146        write!(
147            f,
148            "{}",
149            match *self {
150                Self::LinuxAmd64 => "linux/amd64",
151                Self::LinuxAmd64V2 => "linux/amd64/v2",
152                Self::LinuxArm64 => "linux/arm64",
153                Self::LinuxArm => "linux/arm",
154                Self::LinuxArmV6 => "linux/arm/v6",
155                Self::LinuxArmV7 => "linux/arm/v7",
156                Self::Linux386 => "linux/386",
157                Self::LinuxLoong64 => "linux/loong64",
158                Self::LinuxMips => "linux/mips",
159                Self::LinuxMipsle => "linux/mipsle",
160                Self::LinuxMips64 => "linux/mips64",
161                Self::LinuxMips64le => "linux/mips64le",
162                Self::LinuxPpc64 => "linux/ppc64",
163                Self::LinuxPpc64le => "linux/ppc64le",
164                Self::LinuxRiscv64 => "linux/riscv64",
165                Self::LinuxS390x => "linux/s390x",
166            }
167        )
168    }
169}
170
171impl FromStr for Platform {
172    type Err = miette::Error;
173
174    fn from_str(s: &str) -> Result<Self, Self::Err> {
175        Ok(match s {
176            "linux/amd64" => Self::LinuxAmd64,
177            "linux/amd64/v2" => Self::LinuxAmd64V2,
178            "linux/arm64" => Self::LinuxArm64,
179            "linux/arm" => Self::LinuxArm,
180            "linux/arm/v6" => Self::LinuxArmV6,
181            "linux/arm/v7" => Self::LinuxArmV7,
182            "linux/386" => Self::Linux386,
183            "linux/loong64" => Self::LinuxLoong64,
184            "linux/mips" => Self::LinuxMips,
185            "linux/mipsle" => Self::LinuxMipsle,
186            "linux/mips64" => Self::LinuxMips64,
187            "linux/mips64le" => Self::LinuxMips64le,
188            "linux/ppc64" => Self::LinuxPpc64,
189            "linux/ppc64le" => Self::LinuxPpc64le,
190            "linux/riscv64" => Self::LinuxRiscv64,
191            "linux/s390x" => Self::LinuxS390x,
192            platform => bail!("Platform {platform} unsupported"),
193        })
194    }
195}
196
197impl<'de> Deserialize<'de> for Platform {
198    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
199    where
200        D: serde::Deserializer<'de>,
201    {
202        let value = String::deserialize(deserializer)?;
203        value.parse().map_err(serde::de::Error::custom)
204    }
205}
206
207impl Serialize for Platform {
208    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
209    where
210        S: serde::Serializer,
211    {
212        serializer.serialize_str(&self.to_string())
213    }
214}