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
#![allow(missing_docs)] // Prevents double-documenting some things

use crate::PERSEUS_VERSION;
use clap::Parser;

// The documentation for the `Opts` struct will appear in the help page, hence
// the lack of punctuation and the lowercasing in places

/// The command-line interface for Perseus, a super-fast WebAssembly frontend
/// development framework!
#[derive(Parser, Clone)]
#[clap(version = PERSEUS_VERSION)]
// #[clap(setting = AppSettings::ColoredHelp)]
pub struct Opts {
    #[clap(subcommand)]
    pub subcmd: Subcommand,
    // All the following arguments are global, and can provided to any subcommand
    /// The path to `cargo` when used for engine builds
    #[clap(long, default_value = "cargo", global = true)]
    pub cargo_engine_path: String,
    /// The path to `cargo` when used for browser builds
    #[clap(long, default_value = "cargo", global = true)]
    pub cargo_browser_path: String,
    /// A path to `wasm-bindgen`, if you want to use a local installation (note
    /// that the CLI will install it locally for you by default)
    #[clap(long, global = true)]
    pub wasm_bindgen_path: Option<String>,
    /// A path to `wasm-opt`, if you want to use a local installation (note that
    /// the CLI will install it locally for you by default)
    #[clap(long, global = true)]
    pub wasm_opt_path: Option<String>,
    /// The path to `rustup`
    #[clap(long, default_value = "rustup", global = true)]
    pub rustup_path: String,
    /// The value of `RUSTFLAGS` when building for Wasm in release mode (this
    /// will not impact internal target-gating)
    #[clap(
        long,
        default_value = "-C opt-level=z -C codegen-units=1",
        global = true
    )]
    pub wasm_release_rustflags: String,
    /// Any arguments to `cargo` when building for the engine-side
    #[clap(long, default_value = "", global = true)]
    pub cargo_engine_args: String,
    /// Any arguments to `cargo` when building for the browser-side
    #[clap(long, default_value = "", global = true)]
    pub cargo_browser_args: String,
    /// Any arguments to `wasm-bindgen`
    #[clap(long, default_value = "", global = true)]
    pub wasm_bindgen_args: String,
    /// Any arguments to `wasm-opt` (only run in release builds)
    #[clap(long, default_value = "-Oz", global = true)]
    pub wasm_opt_args: String,
    /// The path to `git` (for downloading custom templates for `perseus new`)
    #[clap(long, default_value = "git", global = true)]
    pub git_path: String,
    /// The host for the reload server (you should almost never change this)
    #[clap(long, default_value = "localhost", global = true)]
    pub reload_server_host: String,
    /// The port for the reload server (you should almost never change this)
    #[clap(long, default_value = "3100", global = true)]
    pub reload_server_port: u16,
    /// If this is set, commands will be run sequentially rather than in
    /// parallel (slows down operations, but reduces memory usage)
    #[clap(long, global = true)]
    pub sequential: bool,
    /// Disable automatic browser reloading
    #[clap(long, global = true)]
    pub no_browser_reload: bool,
    /// A custom version of `wasm-bindgen` to use (defaults to the latest
    /// installed version, and after that the latest available from GitHub;
    /// update to latest can be forced with `latest`)
    #[clap(long, global = true)]
    pub wasm_bindgen_version: Option<String>,
    /// A custom version of `wasm-opt` to use (defaults to the latest installed
    /// version, and after that the latest available from GitHub; update to
    /// latest can be forced with `latest`)
    #[clap(long, global = true)]
    pub wasm_opt_version: Option<String>,
    /// Disables the system-wide tools cache in `~/.cargo/perseus_tools/` (you
    /// should set this for CI)
    #[clap(long, global = true)]
    pub no_system_tools_cache: bool,
    /// Shows the logs from building and serving your app no matter what (the
    /// default is to only show them on a compilation/build failure); this
    /// is intended mainly for end-to-end debugging, although the `snoop`
    /// commands are more useful for targeted debugging
    #[clap(long, global = true)]
    pub verbose: bool,
}

#[derive(Parser, Clone)]
pub enum Subcommand {
    Build(BuildOpts),
    ExportErrorPage(ExportErrorPageOpts),
    Export(ExportOpts),
    Serve(ServeOpts),
    Test(TestOpts),
    /// Removes build artifacts in the `dist/` directory
    Clean,
    Deploy(DeployOpts),
    Tinker(TinkerOpts),
    /// Runs one of the underlying commands that builds your app, allowing you
    /// to see more detailed logs
    #[clap(subcommand)]
    Snoop(SnoopSubcommand),
    New(NewOpts),
    Init(InitOpts),
    /// Checks if your app builds properly for both the engine-side and the
    /// browser-side
    Check(CheckOpts),
}
/// Builds your app
#[derive(Parser, Clone)]
pub struct BuildOpts {
    /// Build for production
    #[clap(long)]
    pub release: bool,
    /// Watch the files in your working directory for changes (excluding
    /// `target/` and `dist/`)
    #[clap(short, long)]
    pub watch: bool,
    /// Marks a specific file/directory to be watched (directories will be
    /// recursively watched)
    #[clap(long)]
    pub custom_watch: Vec<String>,
}
/// Exports your app to purely static files
#[derive(Parser, Clone)]
pub struct ExportOpts {
    /// Export for production
    #[clap(long)]
    pub release: bool,
    /// Serve the generated static files locally
    #[clap(short, long)]
    pub serve: bool,
    /// Where to host your exported app
    #[clap(long, default_value = "127.0.0.1")]
    pub host: String,
    /// The port to host your exported app on
    #[clap(long, default_value = "8080")]
    pub port: u16,
    /// Watch the files in your working directory for changes (excluding
    /// `target/` and `dist/`)
    #[clap(short, long)]
    pub watch: bool,
    /// Marks a specific file/directory to be watched (directories will be
    /// recursively watched)
    #[clap(long)]
    pub custom_watch: Vec<String>,
}
/// Exports an error page for the given HTTP status code
#[derive(Parser, Clone)]
pub struct ExportErrorPageOpts {
    #[clap(short, long)]
    pub code: String,
    #[clap(short, long)]
    pub output: String,
}
/// Serves your app
#[derive(Parser, Clone)]
pub struct ServeOpts {
    /// Don't run the final binary, but print its location instead as the last
    /// line of output
    #[clap(long)]
    pub no_run: bool,
    /// Only build the server, and use the results of a previous `perseus build`
    #[clap(long)]
    pub no_build: bool,
    /// Build and serve for production
    #[clap(long)]
    pub release: bool,
    /// Make the final binary standalone (this is used in `perseus deploy` only,
    /// don't manually invoke it unless you have a good reason!)
    #[clap(long)]
    pub standalone: bool,
    /// Watch the files in your working directory for changes (excluding
    /// `target/` and `dist/`)
    #[clap(short, long)]
    pub watch: bool,
    /// Marks a specific file/directory to be watched (directories will be
    /// recursively watched)
    #[clap(long)]
    pub custom_watch: Vec<String>,
    /// Where to host your exported app
    #[clap(long, default_value = "127.0.0.1")]
    pub host: String,
    /// The port to host your exported app on
    #[clap(long, default_value = "8080")]
    pub port: u16,
}
/// Serves your app as `perseus serve` does, but puts it in testing mode
#[derive(Parser, Clone)]
pub struct TestOpts {
    /// Only build the testing server, and use the results of a previous
    /// `perseus build`
    #[clap(long)]
    pub no_build: bool,
    /// Show the browser window when testing (by default, the browser is used in
    /// 'headless' mode); this can be useful for debugging failing tests in
    /// some cases
    #[clap(long)]
    pub show_browser: bool,
    /// Watch the files in your working directory for changes (excluding
    /// `target/` and `dist/`)
    #[clap(short, long)]
    pub watch: bool,
    /// Marks a specific file/directory to be watched (directories will be
    /// recursively watched)
    #[clap(long)]
    pub custom_watch: Vec<String>,
    /// Where to host your exported app
    #[clap(long, default_value = "127.0.0.1")]
    pub host: String,
    /// The port to host your exported app on
    #[clap(long, default_value = "8080")]
    pub port: u16,
}
/// Packages your app for deployment
#[derive(Parser, Clone)]
pub struct DeployOpts {
    /// Change the output from `pkg/` to somewhere else
    #[clap(short, long, default_value = "pkg")]
    pub output: String,
    /// Export your app to purely static files (see `export`)
    #[clap(short, long)]
    pub export_static: bool,
    /// Don't minify JavaScript (this will decrease performance)
    #[clap(long)]
    pub no_minify_js: bool,
}
/// Runs the `tinker` action of plugins, which lets them modify the Perseus
/// engine
#[derive(Parser, Clone)]
pub struct TinkerOpts {
    /// Don't remove and recreate the `dist/` directory
    #[clap(long)]
    pub no_clean: bool,
}
/// Creates a new Perseus project in a directory of the given name, which will
/// be created in the current path
#[derive(Parser, Clone)]
pub struct NewOpts {
    /// The name of the new project, which will also be used for the directory
    #[clap(value_parser)]
    pub name: String,
    /// An optional custom URL to a Git repository to be used as a custom
    /// template (note that custom templates will not respect your project's
    /// name). This can be followed with `@branch` to fetch from `branch`
    /// rather than the default
    #[clap(short, long)]
    pub template: Option<String>,
    /// The path to a custom directory to create (if this is not provided, the
    /// project name will be used by default)
    #[clap(long)]
    pub dir: Option<String>,
}
/// Initializes a new Perseus project in the current directory
#[derive(Parser, Clone)]
pub struct InitOpts {
    /// The name of the new project
    #[clap(value_parser)]
    pub name: String,
}

#[derive(Parser, Clone)]
pub enum SnoopSubcommand {
    /// Snoops on the static generation process (this will let you see `dbg!`
    /// calls and the like)
    Build {
        /// Watch the files in your working directory for changes (excluding
        /// `target/` and `dist/`)
        #[clap(short, long)]
        watch: bool,
        /// Marks a specific file/directory to be watched (directories will be
        /// recursively watched)
        #[clap(long)]
        custom_watch: Vec<String>,
    },
    /// Snoops on the Wasm building process (mostly for debugging errors)
    WasmBuild {
        /// Watch the files in your working directory for changes (excluding
        /// `target/` and `dist/`)
        #[clap(short, long)]
        watch: bool,
        /// Marks a specific file/directory to be watched (directories will be
        /// recursively watched)
        #[clap(long)]
        custom_watch: Vec<String>,
    },
    /// Snoops on the server process (run `perseus build` before this)
    Serve(SnoopServeOpts),
}

#[derive(Parser, Clone)]
pub struct SnoopServeOpts {
    /// Where to host your exported app
    #[clap(long, default_value = "127.0.0.1")]
    pub host: String,
    /// The port to host your exported app on
    #[clap(long, default_value = "8080")]
    pub port: u16,
    /// Watch the files in your working directory for changes (excluding
    /// `target/` and `dist/`)
    #[clap(short, long)]
    pub watch: bool,
    /// Marks a specific file/directory to be watched (directories will be
    /// recursively watched)
    #[clap(long)]
    pub custom_watch: Vec<String>,
}

#[derive(Parser, Clone)]
pub struct CheckOpts {
    /// Watch the files in your working directory for changes (excluding
    /// `target/` and `dist/`)
    #[clap(short, long)]
    pub watch: bool,
    /// Marks a specific file/directory to be watched (directories will be
    /// recursively watched)
    #[clap(long)]
    pub custom_watch: Vec<String>,
    /// Make sure the app's page generation works properly (this will take much
    /// longer, but almost guarantees that your app will actually build);
    /// use this to catch errors in build state and the like
    #[clap(short, long)]
    pub generate: bool,
}