Skip to main content

stow_types/
platform.rs

1//! Toolchain and target descriptions: target triples, rustc versions,
2//! compile profiles, and panic strategy.
3
4use std::fmt;
5
6use serde::{Deserialize, Serialize};
7
8/// Compilation target triple (e.g., "x86_64-unknown-linux-gnu").
9#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
10pub struct Target(pub String);
11
12impl Target {
13    /// Short form for use in OCI tags (e.g., "x86_64-linux" from "x86_64-unknown-linux-gnu").
14    #[must_use]
15    pub fn short(&self) -> String {
16        let parts: Vec<&str> = self.0.split('-').collect();
17        match parts.as_slice() {
18            [arch, _vendor, os, ..] => format!("{arch}-{os}"),
19            [arch, os] => format!("{arch}-{os}"),
20            _ => self.0.clone(),
21        }
22    }
23}
24
25impl fmt::Display for Target {
26    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27        f.write_str(&self.0)
28    }
29}
30
31/// Identifies the exact rustc toolchain used for compilation.
32#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
33pub struct RustcVersion {
34    /// Semantic version (e.g., 1.83.0)
35    pub version: semver::Version,
36    /// Short commit hash from `rustc --version --verbose`
37    pub commit_hash: String,
38    /// LLVM version string (e.g., "19.1.4")
39    pub llvm_version: String,
40}
41
42impl RustcVersion {
43    /// Short form for OCI tags: "1.83.0"
44    #[must_use]
45    pub fn short(&self) -> String {
46        self.version.to_string()
47    }
48}
49
50impl fmt::Display for RustcVersion {
51    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52        write!(f, "{} ({})", self.version, self.commit_hash)
53    }
54}
55
56/// Compilation profile settings observed from actual rustc arguments.
57#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, utoipa::ToSchema)]
58pub struct Profile {
59    /// `-C opt-level` value as rustc saw it (`"0"`–`"3"`, `"s"`, `"z"`).
60    pub opt_level: String,
61    /// Debug info level normalized to 0 (none), 1 (line tables), or 2 (full).
62    pub debuginfo: u32,
63    /// Whether `-C debug-assertions` was enabled.
64    pub debug_assertions: bool,
65    /// Whether `-C overflow-checks` was enabled.
66    pub overflow_checks: bool,
67    /// `-C panic` strategy.
68    pub panic: PanicStrategy,
69    /// `-C strip` level. Stripping happens at link time, so it only changes
70    /// the bytes of linked artifacts; `normalized_cache_profile` pins it to
71    /// `None` for rlibs. The canonical JSON omits `none`, which is the only
72    /// level artifacts registered before the field existed could carry.
73    #[serde(default, skip_serializing_if = "StripLevel::is_none")]
74    pub strip: StripLevel,
75}
76
77impl Profile {
78    /// Returns true if this is a debug profile (`opt_level` "0" with `debug_assertions`).
79    #[must_use]
80    pub fn is_debug(&self) -> bool {
81        self.opt_level == "0" && self.debug_assertions
82    }
83}
84
85/// Panic strategy used during compilation.
86#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, utoipa::ToSchema)]
87pub enum PanicStrategy {
88    /// `panic=unwind` — rustc's default.
89    Unwind,
90    /// `panic=abort`.
91    Abort,
92}
93
94impl PanicStrategy {
95    /// The `-C panic` value for this strategy.
96    #[must_use]
97    pub const fn as_str(&self) -> &str {
98        match self {
99            Self::Unwind => "unwind",
100            Self::Abort => "abort",
101        }
102    }
103}
104
105impl fmt::Display for PanicStrategy {
106    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107        f.write_str(self.as_str())
108    }
109}
110
111/// `-C strip` level as cargo passes it to rustc.
112///
113/// Cargo sets `debuginfo` on its own whenever a profile turns `debug` off,
114/// so this is part of the compile identity rather than a reason to exclude
115/// an invocation.
116#[derive(
117    Debug, Clone, Copy, Default, PartialEq, Eq, Hash, Serialize, Deserialize, utoipa::ToSchema,
118)]
119#[serde(rename_all = "lowercase")]
120pub enum StripLevel {
121    /// `strip=none` — rustc's default.
122    #[default]
123    None,
124    /// `strip=debuginfo`.
125    Debuginfo,
126    /// `strip=symbols`.
127    Symbols,
128}
129
130impl StripLevel {
131    /// The `-C strip` value for this level.
132    #[must_use]
133    pub const fn as_str(&self) -> &str {
134        match self {
135            Self::None => "none",
136            Self::Debuginfo => "debuginfo",
137            Self::Symbols => "symbols",
138        }
139    }
140
141    /// Whether this is rustc's default level, omitted from canonical JSON.
142    #[must_use]
143    pub const fn is_none(&self) -> bool {
144        matches!(self, Self::None)
145    }
146}
147
148impl fmt::Display for StripLevel {
149    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150        f.write_str(self.as_str())
151    }
152}