forked from DioxusLabs/dioxus
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathplatform.rs
More file actions
374 lines (329 loc) · 11.8 KB
/
platform.rs
File metadata and controls
374 lines (329 loc) · 11.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
use anyhow::Result;
use clap::{arg, Arg, ArgMatches, Args, FromArgMatches};
use serde::{Deserialize, Serialize};
use std::fmt::Display;
use std::str::FromStr;
use target_lexicon::{Environment, OperatingSystem, Triple};
#[derive(
Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Debug, Default,
)]
#[non_exhaustive]
pub(crate) enum Platform {
/// Alias for `--target wasm32-unknown-unknown --renderer websys --bundle-format web`
#[serde(rename = "web")]
Web,
/// Alias for `--target <host> --renderer webview --bundle-format macos`
#[serde(rename = "macos")]
MacOS,
/// Alias for `--target <host> --renderer webview --bundle-format windows`
#[serde(rename = "windows")]
Windows,
/// Alias for `--target <host> --renderer webview --bundle-format linux`
#[serde(rename = "linux")]
Linux,
/// Alias for `--target <aarch64-apple-ios/sim> --renderer webview --bundle-format ios`
#[serde(rename = "ios")]
Ios,
/// Alias for `--target <device-triple> --renderer webview --bundle-format android`
#[serde(rename = "android")]
Android,
/// Alias for `--target <host> --renderer ssr --bundle-format server`
#[serde(rename = "server")]
Server,
/// Alias for `--target <host> --renderer liveview --bundle-format server`
#[serde(rename = "liveview")]
Liveview,
/// No platform was specified, so the CLI is free to choose the best one.
#[default]
Unknown,
}
impl Platform {
/// Parse a platform from a string identifier (e.g., "web", "macos", "desktop").
pub(crate) fn from_identifier(identifier: &str) -> std::result::Result<Self, clap::Error> {
match identifier {
"web" => Ok(Self::Web),
"macos" => Ok(Self::MacOS),
"windows" => Ok(Self::Windows),
"linux" => Ok(Self::Linux),
"ios" => Ok(Self::Ios),
"android" => Ok(Self::Android),
"server" => Ok(Self::Server),
"liveview" => Ok(Self::Liveview),
"desktop" => {
if cfg!(target_os = "macos") {
Ok(Self::MacOS)
} else if cfg!(target_os = "windows") {
Ok(Self::Windows)
} else if cfg!(unix) {
Ok(Self::Linux)
} else {
Err(clap::Error::raw(
clap::error::ErrorKind::InvalidValue,
"Desktop alias is not supported on this platform",
))
}
}
_ => Err(clap::Error::raw(
clap::error::ErrorKind::InvalidValue,
format!("Unknown platform: {identifier}"),
)),
}
}
}
impl Args for Platform {
fn augment_args_for_update(cmd: clap::Command) -> clap::Command {
Self::augment_args(cmd)
}
fn augment_args(cmd: clap::Command) -> clap::Command {
const HELP_HEADING: &str = "Platform";
cmd.arg(arg!(--web "Target a web app").help_heading(HELP_HEADING))
.arg(arg!(--desktop "Target a desktop app").help_heading(HELP_HEADING))
.arg(arg!(--macos "Target a macos desktop app").help_heading(HELP_HEADING))
.arg(arg!(--windows "Target a windows desktop app").help_heading(HELP_HEADING))
.arg(arg!(--linux "Target a linux desktop app").help_heading(HELP_HEADING))
.arg(arg!(--ios "Target an ios app").help_heading(HELP_HEADING))
.arg(arg!(--android "Target an android app").help_heading(HELP_HEADING))
.arg(arg!(--server "Target a server build").help_heading(HELP_HEADING))
.arg(arg!(--liveview "Target a liveview build").help_heading(HELP_HEADING))
.arg(
Arg::new("platform")
.long("platform")
.value_name("PLATFORM")
.help("Manually set the platform (web, macos, windows, linux, ios, android, server, liveview)")
.help_heading(HELP_HEADING)
.value_parser([
"web", "macos", "windows", "linux", "ios", "android", "server", "liveview", "desktop",
])
.conflicts_with("target_alias"),
)
.group(
clap::ArgGroup::new("target_alias")
.args([
"web", "desktop", "macos", "windows", "linux", "ios", "android", "server",
"liveview",
])
.multiple(false)
.required(false),
)
}
}
impl FromArgMatches for Platform {
fn from_arg_matches(matches: &ArgMatches) -> Result<Self, clap::Error> {
if let Some(identifier) = matches.get_one::<String>("platform") {
Self::from_identifier(identifier)
} else if let Some(platform) = matches.get_one::<clap::Id>("target_alias") {
Self::from_identifier(platform.as_str())
} else {
Ok(Self::Unknown)
}
}
fn update_from_arg_matches(&mut self, matches: &ArgMatches) -> Result<(), clap::Error> {
*self = Self::from_arg_matches(matches)?;
Ok(())
}
}
#[derive(
Copy,
Clone,
Hash,
PartialEq,
Eq,
PartialOrd,
Ord,
Serialize,
Deserialize,
Debug,
clap::ValueEnum,
)]
#[non_exhaustive]
pub(crate) enum Renderer {
/// Targeting webview renderer
Webview,
/// Targeting native renderer
Native,
/// Targeting the server platform using Axum and Dioxus-Fullstack
///
/// This is implicitly passed if `fullstack` is enabled as a feature. Using this variant simply
/// means you're only building the server variant without the `.wasm` to serve.
Server,
/// Targeting the static generation platform using SSR and Dioxus-Fullstack
Liveview,
/// Targeting the web-sys renderer
Web,
}
impl Renderer {
/// Get the feature name for the platform in the dioxus crate
pub(crate) fn feature_name(&self, target: &Triple) -> &str {
match self {
Renderer::Webview => match (target.environment, target.operating_system) {
(Environment::Android, _) | (_, OperatingSystem::IOS(_)) => "mobile",
_ => "desktop",
},
Renderer::Native => "native",
Renderer::Server => "server",
Renderer::Liveview => "liveview",
Renderer::Web => "web",
}
}
pub(crate) fn autodetect_from_cargo_feature(feature: &str) -> Option<Self> {
match feature {
"web" => Some(Self::Web),
"desktop" | "mobile" => Some(Self::Webview),
"native" => Some(Self::Native),
"liveview" => Some(Self::Liveview),
"server" => Some(Self::Server),
_ => None,
}
}
}
#[derive(Debug)]
pub(crate) struct UnknownRendererError;
impl std::error::Error for UnknownRendererError {}
impl std::fmt::Display for UnknownRendererError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Unknown renderer")
}
}
impl FromStr for Renderer {
type Err = UnknownRendererError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"webview" => Ok(Self::Webview),
"native" => Ok(Self::Native),
"server" => Ok(Self::Server),
"liveview" => Ok(Self::Liveview),
"web" => Ok(Self::Web),
_ => Err(UnknownRendererError),
}
}
}
impl Display for Renderer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
Renderer::Webview => "webview",
Renderer::Native => "native",
Renderer::Server => "server",
Renderer::Liveview => "liveview",
Renderer::Web => "web",
})
}
}
#[derive(
Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Debug, Default,
)]
#[non_exhaustive]
pub(crate) enum BundleFormat {
/// Targeting the web bundle structure
#[serde(rename = "web")]
#[default]
Web,
/// Targeting the macos desktop bundle structure
#[cfg_attr(target_os = "macos", serde(alias = "desktop"))]
#[serde(rename = "macos")]
MacOS,
/// Targeting the windows desktop bundle structure
#[cfg_attr(target_os = "windows", serde(alias = "desktop"))]
#[serde(rename = "windows")]
Windows,
/// Targeting the linux desktop bundle structure
#[cfg_attr(target_os = "linux", serde(alias = "desktop"))]
#[serde(rename = "linux")]
Linux,
/// Targeting the server bundle structure (a single binary placed next to the web build)
#[serde(rename = "server")]
Server,
/// Targeting the ios bundle structure
///
/// Can't work properly if you're not building from an Apple device.
#[serde(rename = "ios")]
Ios,
/// Targeting the android bundle structure
#[serde(rename = "android")]
Android,
}
impl BundleFormat {
/// The native "desktop" host app format.
pub(crate) fn host() -> Self {
if cfg!(target_os = "macos") {
Self::MacOS
} else if cfg!(target_os = "windows") {
Self::Windows
} else if cfg!(target_os = "linux") {
Self::Linux
} else {
Self::Web
}
}
/// Get the name of the folder we need to generate for this platform
///
/// Note that web and server share the same platform folder since we'll export the web folder as a bundle on its own
pub(crate) fn build_folder_name(&self) -> &'static str {
match self {
Self::Web => "web",
Self::Server => "web",
Self::Ios => "ios",
Self::Android => "android",
Self::Windows => "windows",
Self::Linux => "linux",
Self::MacOS => "macos",
}
}
pub(crate) fn profile_name(&self, release: bool) -> String {
let base_profile = match self {
Self::MacOS | Self::Windows | Self::Linux => "desktop",
Self::Web => "wasm",
Self::Ios => "ios",
Self::Android => "android",
Self::Server => "server",
};
let opt_level = if release { "release" } else { "dev" };
format!("{base_profile}-{opt_level}")
}
pub(crate) fn expected_name(&self) -> &'static str {
match self {
Self::Web => "Web",
Self::MacOS => "MacOS",
Self::Windows => "Windows",
Self::Linux => "Linux",
Self::Ios => "iOS",
Self::Android => "Android",
Self::Server => "Server",
}
}
}
#[derive(Debug)]
pub(crate) struct UnknownBundleFormatError;
impl std::error::Error for UnknownBundleFormatError {}
impl std::fmt::Display for UnknownBundleFormatError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Unknown bundle format")
}
}
impl FromStr for BundleFormat {
type Err = UnknownBundleFormatError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"web" => Ok(Self::Web),
"macos" => Ok(Self::MacOS),
"windows" => Ok(Self::Windows),
"linux" => Ok(Self::Linux),
"server" => Ok(Self::Server),
"ios" => Ok(Self::Ios),
"android" => Ok(Self::Android),
_ => Err(UnknownBundleFormatError),
}
}
}
impl Display for BundleFormat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
BundleFormat::Web => "web",
BundleFormat::MacOS => "macos",
BundleFormat::Windows => "windows",
BundleFormat::Linux => "linux",
BundleFormat::Server => "server",
BundleFormat::Ios => "ios",
BundleFormat::Android => "android",
})
}
}