Skip to main content

rustbridge_bundle/
platform.rs

1//! Platform detection and identification.
2
3use serde::{Deserialize, Serialize};
4use std::fmt;
5
6/// Supported platform targets.
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
8#[serde(rename_all = "kebab-case")]
9pub enum Platform {
10    /// Linux on x86_64.
11    LinuxX86_64,
12    /// Linux on ARM64.
13    LinuxAarch64,
14    /// macOS on x86_64 (Intel).
15    DarwinX86_64,
16    /// macOS on ARM64 (Apple Silicon).
17    DarwinAarch64,
18    /// Windows on x86_64.
19    WindowsX86_64,
20    /// Windows on ARM64.
21    WindowsAarch64,
22}
23
24impl Platform {
25    /// Detect the current platform at runtime.
26    #[must_use]
27    pub fn current() -> Option<Self> {
28        let arch = std::env::consts::ARCH;
29        let os = std::env::consts::OS;
30
31        match (os, arch) {
32            ("linux", "x86_64") => Some(Self::LinuxX86_64),
33            ("linux", "aarch64") => Some(Self::LinuxAarch64),
34            ("macos", "x86_64") => Some(Self::DarwinX86_64),
35            ("macos", "aarch64") => Some(Self::DarwinAarch64),
36            ("windows", "x86_64") => Some(Self::WindowsX86_64),
37            ("windows", "aarch64") => Some(Self::WindowsAarch64),
38            _ => None,
39        }
40    }
41
42    /// Get the platform key string (e.g., "linux-x86_64").
43    #[must_use]
44    pub fn as_str(&self) -> &'static str {
45        match self {
46            Self::LinuxX86_64 => "linux-x86_64",
47            Self::LinuxAarch64 => "linux-aarch64",
48            Self::DarwinX86_64 => "darwin-x86_64",
49            Self::DarwinAarch64 => "darwin-aarch64",
50            Self::WindowsX86_64 => "windows-x86_64",
51            Self::WindowsAarch64 => "windows-aarch64",
52        }
53    }
54
55    /// Parse a platform from its string representation.
56    #[must_use]
57    pub fn parse(s: &str) -> Option<Self> {
58        match s {
59            "linux-x86_64" => Some(Self::LinuxX86_64),
60            "linux-aarch64" => Some(Self::LinuxAarch64),
61            "darwin-x86_64" => Some(Self::DarwinX86_64),
62            "darwin-aarch64" => Some(Self::DarwinAarch64),
63            "windows-x86_64" => Some(Self::WindowsX86_64),
64            "windows-aarch64" => Some(Self::WindowsAarch64),
65            _ => None,
66        }
67    }
68
69    /// Get the expected library file extension for this platform.
70    #[must_use]
71    pub fn library_extension(&self) -> &'static str {
72        match self {
73            Self::LinuxX86_64 | Self::LinuxAarch64 => "so",
74            Self::DarwinX86_64 | Self::DarwinAarch64 => "dylib",
75            Self::WindowsX86_64 | Self::WindowsAarch64 => "dll",
76        }
77    }
78
79    /// Get the library filename prefix for this platform.
80    #[must_use]
81    pub fn library_prefix(&self) -> &'static str {
82        match self {
83            Self::LinuxX86_64 | Self::LinuxAarch64 => "lib",
84            Self::DarwinX86_64 | Self::DarwinAarch64 => "lib",
85            Self::WindowsX86_64 | Self::WindowsAarch64 => "",
86        }
87    }
88
89    /// Format a library name for this platform.
90    ///
91    /// # Example
92    ///
93    /// ```
94    /// use rustbridge_bundle::Platform;
95    ///
96    /// assert_eq!(Platform::LinuxX86_64.library_name("myplugin"), "libmyplugin.so");
97    /// assert_eq!(Platform::WindowsX86_64.library_name("myplugin"), "myplugin.dll");
98    /// ```
99    #[must_use]
100    pub fn library_name(&self, base_name: &str) -> String {
101        format!(
102            "{}{}.{}",
103            self.library_prefix(),
104            base_name,
105            self.library_extension()
106        )
107    }
108
109    /// Get the Rust target triple for this platform.
110    #[must_use]
111    pub fn rust_target(&self) -> &'static str {
112        match self {
113            Self::LinuxX86_64 => "x86_64-unknown-linux-gnu",
114            Self::LinuxAarch64 => "aarch64-unknown-linux-gnu",
115            Self::DarwinX86_64 => "x86_64-apple-darwin",
116            Self::DarwinAarch64 => "aarch64-apple-darwin",
117            Self::WindowsX86_64 => "x86_64-pc-windows-msvc",
118            Self::WindowsAarch64 => "aarch64-pc-windows-msvc",
119        }
120    }
121
122    /// Get all supported platforms.
123    #[must_use]
124    pub fn all() -> &'static [Platform] {
125        &[
126            Self::LinuxX86_64,
127            Self::LinuxAarch64,
128            Self::DarwinX86_64,
129            Self::DarwinAarch64,
130            Self::WindowsX86_64,
131            Self::WindowsAarch64,
132        ]
133    }
134}
135
136impl fmt::Display for Platform {
137    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138        write!(f, "{}", self.as_str())
139    }
140}
141
142#[cfg(test)]
143mod tests {
144    #![allow(non_snake_case)]
145
146    use super::*;
147
148    #[test]
149    fn Platform___current___returns_some_on_supported_platform() {
150        // This test will pass on any supported platform
151        let platform = Platform::current();
152        // We can't assert a specific value since it depends on the host
153        // But we can verify the round-trip works if we got a value
154        if let Some(p) = platform {
155            assert_eq!(Platform::parse(p.as_str()), Some(p));
156        }
157    }
158
159    #[test]
160    fn Platform___from_str___parses_valid_platforms() {
161        assert_eq!(Platform::parse("linux-x86_64"), Some(Platform::LinuxX86_64));
162        assert_eq!(
163            Platform::parse("darwin-aarch64"),
164            Some(Platform::DarwinAarch64)
165        );
166        assert_eq!(
167            Platform::parse("windows-x86_64"),
168            Some(Platform::WindowsX86_64)
169        );
170    }
171
172    #[test]
173    fn Platform___from_str___returns_none_for_invalid() {
174        assert_eq!(Platform::parse("invalid"), None);
175        assert_eq!(Platform::parse("linux-arm"), None);
176    }
177
178    #[test]
179    fn Platform___library_name___formats_correctly() {
180        assert_eq!(
181            Platform::LinuxX86_64.library_name("myplugin"),
182            "libmyplugin.so"
183        );
184        assert_eq!(
185            Platform::DarwinAarch64.library_name("myplugin"),
186            "libmyplugin.dylib"
187        );
188        assert_eq!(
189            Platform::WindowsX86_64.library_name("myplugin"),
190            "myplugin.dll"
191        );
192    }
193
194    #[test]
195    fn Platform___all___returns_six_platforms() {
196        assert_eq!(Platform::all().len(), 6);
197    }
198}