1use std::str::FromStr;
4
5use target_lexicon::{
6 Aarch64Architecture, Architecture, DefaultToHost, Environment, OperatingSystem,
7 Riscv32Architecture, Triple, Vendor,
8};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19pub enum TargetPlatform {
20 MacOS,
23 IOS,
25 IOSSimulator,
27 TvOS,
29 TvOSSimulator,
31 WatchOS,
33 WatchOSSimulator,
35 VisionOS,
37 VisionOSSimulator,
39
40 Android,
43 Linux,
45 Windows,
47 Web,
49 Esp32S3,
51 Esp32C3,
53 Esp32P4,
55}
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
59pub enum TargetBackend {
60 Apple,
62 Android,
64 Gtk4,
66 Hydrolysis,
68 Dew,
70}
71
72impl TargetPlatform {
73 #[must_use]
78 pub fn triple(&self) -> Triple {
79 match self {
80 Self::MacOS => Triple {
81 architecture: DefaultToHost::default().0.architecture,
82 vendor: Vendor::Apple,
83 operating_system: OperatingSystem::Darwin(None),
84 environment: Environment::Unknown,
85 binary_format: target_lexicon::BinaryFormat::Macho,
86 },
87 Self::IOS => Triple {
88 architecture: Architecture::Aarch64(Aarch64Architecture::Aarch64),
89 vendor: Vendor::Apple,
90 operating_system: OperatingSystem::IOS(None),
91 environment: Environment::Unknown,
92 binary_format: target_lexicon::BinaryFormat::Macho,
93 },
94 Self::IOSSimulator => {
95 let arch = DefaultToHost::default().0.architecture;
96 let env = match arch {
97 Architecture::X86_64 => Environment::Unknown,
98 _ => Environment::Sim,
99 };
100 Triple {
101 architecture: arch,
102 vendor: Vendor::Apple,
103 operating_system: OperatingSystem::IOS(None),
104 environment: env,
105 binary_format: target_lexicon::BinaryFormat::Macho,
106 }
107 }
108 Self::TvOS => Triple {
109 architecture: Architecture::Aarch64(Aarch64Architecture::Aarch64),
110 vendor: Vendor::Apple,
111 operating_system: OperatingSystem::TvOS(None),
112 environment: Environment::Unknown,
113 binary_format: target_lexicon::BinaryFormat::Macho,
114 },
115 Self::TvOSSimulator => Triple {
116 architecture: DefaultToHost::default().0.architecture,
117 vendor: Vendor::Apple,
118 operating_system: OperatingSystem::TvOS(None),
119 environment: Environment::Sim,
120 binary_format: target_lexicon::BinaryFormat::Macho,
121 },
122 Self::WatchOS => Triple {
123 architecture: Architecture::Aarch64(Aarch64Architecture::Aarch64),
124 vendor: Vendor::Apple,
125 operating_system: OperatingSystem::WatchOS(None),
126 environment: Environment::Unknown,
127 binary_format: target_lexicon::BinaryFormat::Macho,
128 },
129 Self::WatchOSSimulator => Triple {
130 architecture: DefaultToHost::default().0.architecture,
131 vendor: Vendor::Apple,
132 operating_system: OperatingSystem::WatchOS(None),
133 environment: Environment::Sim,
134 binary_format: target_lexicon::BinaryFormat::Macho,
135 },
136 Self::VisionOS => Triple {
137 architecture: Architecture::Aarch64(Aarch64Architecture::Aarch64),
138 vendor: Vendor::Apple,
139 operating_system: OperatingSystem::VisionOS(None),
140 environment: Environment::Unknown,
141 binary_format: target_lexicon::BinaryFormat::Macho,
142 },
143 Self::VisionOSSimulator => Triple {
144 architecture: DefaultToHost::default().0.architecture,
145 vendor: Vendor::Apple,
146 operating_system: OperatingSystem::VisionOS(None),
147 environment: Environment::Sim,
148 binary_format: target_lexicon::BinaryFormat::Macho,
149 },
150 Self::Android => Triple {
151 architecture: Architecture::Aarch64(Aarch64Architecture::Aarch64),
152 vendor: Vendor::Unknown,
153 operating_system: OperatingSystem::Linux,
154 environment: Environment::Android,
155 binary_format: target_lexicon::BinaryFormat::Elf,
156 },
157 Self::Linux | Self::Windows => Triple::host(),
158 Self::Web => Triple::from_str("wasm32-unknown-unknown")
159 .expect("web target triple must remain valid"),
160 Self::Esp32S3 => Triple::from_str("xtensa-esp32s3-espidf")
161 .expect("esp32s3 target triple must remain valid"),
162 Self::Esp32C3 => Triple::from_str("riscv32imc-esp-espidf")
163 .expect("esp32c3 target triple must remain valid"),
164 Self::Esp32P4 => Triple::from_str("riscv32imafc-esp-espidf")
165 .expect("esp32p4 target triple must remain valid"),
166 }
167 }
168
169 #[must_use]
171 pub const fn available_backends(&self) -> &[TargetBackend] {
172 match self {
173 Self::MacOS => &[TargetBackend::Apple, TargetBackend::Hydrolysis],
174 Self::IOS
175 | Self::IOSSimulator
176 | Self::TvOS
177 | Self::TvOSSimulator
178 | Self::WatchOS
179 | Self::WatchOSSimulator
180 | Self::VisionOS
181 | Self::VisionOSSimulator => &[TargetBackend::Apple],
182 Self::Android => &[TargetBackend::Android],
183 Self::Linux => &[TargetBackend::Gtk4, TargetBackend::Hydrolysis],
184 Self::Windows | Self::Web => &[TargetBackend::Hydrolysis],
185 Self::Esp32S3 | Self::Esp32C3 | Self::Esp32P4 => &[TargetBackend::Dew],
186 }
187 }
188
189 #[must_use]
191 pub const fn default_backend(&self) -> TargetBackend {
192 match self {
193 Self::MacOS
194 | Self::IOS
195 | Self::IOSSimulator
196 | Self::TvOS
197 | Self::TvOSSimulator
198 | Self::WatchOS
199 | Self::WatchOSSimulator
200 | Self::VisionOS
201 | Self::VisionOSSimulator => TargetBackend::Apple,
202 Self::Android => TargetBackend::Android,
203 Self::Linux => TargetBackend::Gtk4,
204 Self::Windows | Self::Web => TargetBackend::Hydrolysis,
205 Self::Esp32S3 | Self::Esp32C3 | Self::Esp32P4 => TargetBackend::Dew,
206 }
207 }
208
209 #[must_use]
211 pub const fn is_simulator(&self) -> bool {
212 matches!(
213 self,
214 Self::IOSSimulator
215 | Self::TvOSSimulator
216 | Self::WatchOSSimulator
217 | Self::VisionOSSimulator
218 )
219 }
220
221 #[must_use]
223 pub const fn sdk_name(&self) -> Option<&'static str> {
224 match self {
225 Self::MacOS => Some("macosx"),
226 Self::IOS => Some("iphoneos"),
227 Self::IOSSimulator => Some("iphonesimulator"),
228 Self::TvOS => Some("appletvos"),
229 Self::TvOSSimulator => Some("appletvsimulator"),
230 Self::WatchOS => Some("watchos"),
231 Self::WatchOSSimulator => Some("watchsimulator"),
232 Self::VisionOS => Some("xros"),
233 Self::VisionOSSimulator => Some("xrsimulator"),
234 Self::Android
235 | Self::Linux
236 | Self::Windows
237 | Self::Web
238 | Self::Esp32S3
239 | Self::Esp32C3
240 | Self::Esp32P4 => None,
241 }
242 }
243
244 #[must_use]
246 pub fn arch(&self) -> Architecture {
247 match self {
248 Self::MacOS
249 | Self::IOSSimulator
250 | Self::TvOSSimulator
251 | Self::WatchOSSimulator
252 | Self::VisionOSSimulator
253 | Self::Linux
254 | Self::Windows => DefaultToHost::default().0.architecture,
255 Self::IOS | Self::TvOS | Self::WatchOS | Self::VisionOS | Self::Android => {
256 Architecture::Aarch64(Aarch64Architecture::Aarch64)
257 }
258 Self::Web => Architecture::Wasm32,
259 Self::Esp32S3 => Architecture::XTensa,
260 Self::Esp32C3 => Architecture::Riscv32(Riscv32Architecture::Riscv32imc),
261 Self::Esp32P4 => Architecture::Riscv32(Riscv32Architecture::Riscv32imafc),
262 }
263 }
264}
265
266#[derive(Debug, Clone)]
275pub struct PackageOptions {
276 distribution: bool,
289
290 debug: bool,
301
302 shared_rust_runtime: bool,
304
305 web_frontend: WebFrontendMode,
307}
308
309#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
317pub enum WebFrontendMode {
318 #[default]
320 Stage,
321 DevServer,
323}
324
325impl PackageOptions {
326 #[must_use]
328 pub const fn development() -> Self {
329 Self {
330 distribution: false,
331 debug: true,
332 shared_rust_runtime: true,
333 web_frontend: WebFrontendMode::Stage,
334 }
335 }
336
337 #[must_use]
339 pub const fn packaging(distribution: bool, debug: bool) -> Self {
340 Self {
341 distribution,
342 debug,
343 shared_rust_runtime: false,
344 web_frontend: WebFrontendMode::Stage,
345 }
346 }
347
348 #[must_use]
350 pub const fn with_debug(mut self, debug: bool) -> Self {
351 self.debug = debug;
352 self
353 }
354
355 #[must_use]
357 pub const fn with_dev_server(mut self, dev_server: bool) -> Self {
358 self.web_frontend = if dev_server {
359 WebFrontendMode::DevServer
360 } else {
361 WebFrontendMode::Stage
362 };
363 self
364 }
365
366 #[must_use]
368 pub const fn is_distribution(&self) -> bool {
369 self.distribution
370 }
371
372 #[must_use]
374 pub const fn is_debug(&self) -> bool {
375 self.debug
376 }
377
378 #[must_use]
380 pub const fn uses_shared_rust_runtime(&self) -> bool {
381 self.shared_rust_runtime
382 }
383
384 #[must_use]
386 pub const fn uses_dev_server(&self) -> bool {
387 matches!(self.web_frontend, WebFrontendMode::DevServer)
388 }
389}
390
391#[cfg(test)]
392mod package_options_tests {
393 use super::PackageOptions;
394
395 #[test]
396 fn development_embeds_shared_runtime_and_packaging_does_not() {
397 let development = PackageOptions::development();
398 assert!(development.is_debug());
399 assert!(!development.is_distribution());
400 assert!(development.uses_shared_rust_runtime());
401
402 for options in [
403 PackageOptions::packaging(false, true),
404 PackageOptions::packaging(true, false),
405 ] {
406 assert!(!options.uses_shared_rust_runtime());
407 }
408 }
409}