Skip to content

Commit 5d7834f

Browse files
committed
refactor: cli structure for more clarity
1 parent 5bb1ead commit 5d7834f

File tree

4 files changed

+198
-120
lines changed

4 files changed

+198
-120
lines changed

src/build.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use console::style;
1818
use indicatif::{ProgressBar, ProgressStyle};
1919
use log::log_enabled;
2020
use serde::Serialize;
21+
use std::ffi::OsString;
2122
use std::fmt;
2223
use std::fs::File;
2324
use std::io::{stdout, Write};
@@ -508,7 +509,7 @@ pub fn build(
508509
}
509510
}
510511

511-
pub fn pass_through_legacy(args: Vec<String>) -> i32 {
512+
pub fn pass_through_legacy(args: Vec<OsString>) -> i32 {
512513
let project_root = helpers::get_abs_path(".");
513514
let workspace_root = helpers::get_workspace_root(&project_root);
514515

src/cli.rs

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
use std::ffi::OsString;
2+
3+
use clap::{Args, Parser, Subcommand};
4+
use clap_verbosity_flag::InfoLevel;
5+
6+
/// Rewatch is an alternative build system for the Rescript Compiler bsb (which uses Ninja internally). It strives
7+
/// to deliver consistent and faster builds in monorepo setups with multiple packages, where the
8+
/// default build system fails to pick up changed interfaces across multiple packages.
9+
#[derive(Parser, Debug)]
10+
#[command(version)]
11+
pub struct Cli {
12+
/// Verbosity:
13+
/// -v -> Debug
14+
/// -vv -> Trace
15+
/// -q -> Warn
16+
/// -qq -> Error
17+
/// -qqq -> Off.
18+
/// Default (/ no argument given): 'info'
19+
#[command(flatten)]
20+
pub verbose: clap_verbosity_flag::Verbosity<InfoLevel>,
21+
22+
/// The command to run. If not provided it will default to build.
23+
#[command(subcommand)]
24+
pub command: Option<Command>,
25+
26+
/// The relative path to where the main rescript.json resides. IE - the root of your project.
27+
#[arg(default_value = ".")]
28+
pub folder: String,
29+
30+
#[command(flatten)]
31+
pub build_args: BuildArgs,
32+
}
33+
34+
#[derive(Args, Debug)]
35+
pub struct BuildArgs {
36+
/// Filter files by regex
37+
///
38+
/// Filter allows for a regex to be supplied which will filter the files to be compiled. For
39+
/// instance, to filter out test files for compilation while doing feature work.
40+
#[arg(short, long)]
41+
pub filter: Option<String>,
42+
43+
/// Action after build
44+
///
45+
/// This allows one to pass an additional command to the watcher, which allows it to run when
46+
/// finished. For instance, to play a sound when done compiling, or to run a test suite.
47+
/// NOTE - You may need to add '--color=always' to your subcommand in case you want to output
48+
/// colour as well
49+
#[arg(short, long)]
50+
pub after_build: Option<String>,
51+
52+
/// Create source_dirs.json
53+
///
54+
/// This creates a source_dirs.json file at the root of the monorepo, which is needed when you
55+
/// want to use Reanalyze
56+
#[arg(short, long, default_value_t = false, num_args = 0..=1)]
57+
pub create_sourcedirs: bool,
58+
59+
/// Build development dependencies
60+
///
61+
/// This is the flag to also compile development dependencies
62+
/// It's important to know that we currently do not discern between project src, and
63+
/// dependencies. So enabling this flag will enable building _all_ development dependencies of
64+
/// _all_ packages
65+
#[arg(long, default_value_t = false, num_args = 0..=1)]
66+
pub dev: bool,
67+
68+
/// Disable timing on the output
69+
#[arg(short, long, default_value = "false", num_args = 0..=1)]
70+
pub no_timing: bool,
71+
72+
/// Path to bsc
73+
#[arg(long)]
74+
pub bsc_path: Option<String>,
75+
}
76+
77+
#[derive(Args, Debug)]
78+
pub struct WatchArgs {
79+
/// Filter files by regex
80+
///
81+
/// Filter allows for a regex to be supplied which will filter the files to be compiled. For
82+
/// instance, to filter out test files for compilation while doing feature work.
83+
#[arg(short, long)]
84+
pub filter: Option<String>,
85+
86+
/// Action after build
87+
///
88+
/// This allows one to pass an additional command to the watcher, which allows it to run when
89+
/// finished. For instance, to play a sound when done compiling, or to run a test suite.
90+
/// NOTE - You may need to add '--color=always' to your subcommand in case you want to output
91+
/// colour as well
92+
#[arg(short, long)]
93+
pub after_build: Option<String>,
94+
95+
/// Create source_dirs.json
96+
///
97+
/// This creates a source_dirs.json file at the root of the monorepo, which is needed when you
98+
/// want to use Reanalyze
99+
#[arg(short, long, default_value_t = false, num_args = 0..=1)]
100+
pub create_sourcedirs: bool,
101+
102+
/// Build development dependencies
103+
///
104+
/// This is the flag to also compile development dependencies
105+
/// It's important to know that we currently do not discern between project src, and
106+
/// dependencies. So enabling this flag will enable building _all_ development dependencies of
107+
/// _all_ packages
108+
#[arg(long, default_value_t = false, num_args = 0..=1)]
109+
pub dev: bool,
110+
}
111+
112+
#[derive(Subcommand, Debug)]
113+
pub enum Command {
114+
/// Build using Rewatch
115+
Build(BuildArgs),
116+
/// Build, then start a watcher
117+
Watch(WatchArgs),
118+
/// Clean the build artifacts
119+
Clean {
120+
/// Path to bsc
121+
#[arg(long)]
122+
bsc_path: Option<String>,
123+
},
124+
/// This prints the compiler arguments. It expects the path to a rescript.json file.
125+
CompilerArgs {
126+
/// Path to a rescript.json file
127+
#[command()]
128+
path: String,
129+
130+
#[arg(long, default_value_t = false, num_args = 0..=1)]
131+
dev: bool,
132+
133+
/// To be used in conjunction with compiler_args
134+
#[arg(long)]
135+
rescript_version: Option<String>,
136+
137+
/// A custom path to bsc
138+
#[arg(long)]
139+
bsc_path: Option<String>,
140+
},
141+
/// Use the legacy build system.
142+
///
143+
/// After this command is encountered, the rest of the arguments are passed to the legacy build system.
144+
#[command(disable_help_flag = true)]
145+
Legacy {
146+
#[arg(allow_hyphen_values = true, num_args = 0..)]
147+
legacy_args: Vec<OsString>,
148+
},
149+
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub mod build;
2+
pub mod cli;
23
pub mod cmd;
34
pub mod config;
45
pub mod helpers;

src/main.rs

Lines changed: 46 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,13 @@
11
use anyhow::Result;
2-
use clap::{Parser, ValueEnum};
3-
use clap_verbosity_flag::InfoLevel;
2+
use clap::Parser;
43
use log::LevelFilter;
54
use regex::Regex;
65
use std::io::Write;
76

8-
use rewatch::{build, cmd, lock, watcher};
9-
10-
#[derive(Debug, Clone, ValueEnum)]
11-
enum Command {
12-
/// Build using Rewatch
13-
Build,
14-
/// Build, then start a watcher
15-
Watch,
16-
/// Clean the build artifacts
17-
Clean,
18-
/// Format the code
19-
Format,
20-
/// Dump
21-
Dump,
22-
}
23-
24-
/// Rewatch is an alternative build system for the Rescript Compiler bsb (which uses Ninja internally). It strives
25-
/// to deliver consistent and faster builds in monorepo setups with multiple packages, where the
26-
/// default build system fails to pick up changed interfaces across multiple packages.
27-
#[derive(Parser, Debug)]
28-
#[command(version)]
29-
struct Args {
30-
#[arg(value_enum, default_value_t = Command::Build)]
31-
command: Command,
32-
33-
/// The relative path to where the main rescript.json resides. IE - the root of your project.
34-
#[arg(default_value = ".")]
35-
folder: String,
36-
37-
/// Filter allows for a regex to be supplied which will filter the files to be compiled. For
38-
/// instance, to filter out test files for compilation while doing feature work.
39-
#[arg(short, long)]
40-
filter: Option<String>,
41-
42-
/// This allows one to pass an additional command to the watcher, which allows it to run when
43-
/// finished. For instance, to play a sound when done compiling, or to run a test suite.
44-
/// NOTE - You may need to add '--color=always' to your subcommand in case you want to output
45-
/// colour as well
46-
#[arg(short, long)]
47-
after_build: Option<String>,
48-
49-
// Disable timing on the output
50-
#[arg(short, long, default_value = "false", num_args = 0..=1)]
51-
no_timing: bool,
52-
53-
/// Verbosity:
54-
/// -v -> Debug
55-
/// -vv -> Trace
56-
/// -q -> Warn
57-
/// -qq -> Error
58-
/// -qqq -> Off.
59-
/// Default (/ no argument given): 'info'
60-
#[command(flatten)]
61-
verbose: clap_verbosity_flag::Verbosity<InfoLevel>,
62-
63-
/// This creates a source_dirs.json file at the root of the monorepo, which is needed when you
64-
/// want to use Reanalyze
65-
#[arg(short, long, default_value_t = false, num_args = 0..=1)]
66-
create_sourcedirs: bool,
67-
68-
/// This prints the compiler arguments. It expects the path to a rescript.json file.
69-
/// This also requires --bsc-path and --rescript-version to be present
70-
#[arg(long)]
71-
compiler_args: Option<String>,
72-
73-
/// This is the flag to also compile development dependencies
74-
/// It's important to know that we currently do not discern between project src, and
75-
/// dependencies. So enabling this flag will enable building _all_ development dependencies of
76-
/// _all_ packages
77-
#[arg(long, default_value_t = false, num_args = 0..=1)]
78-
dev: bool,
79-
80-
/// To be used in conjunction with compiler_args
81-
#[arg(long)]
82-
rescript_version: Option<String>,
83-
84-
/// A custom path to bsc
85-
#[arg(long)]
86-
bsc_path: Option<String>,
87-
88-
/// Use the legacy build system.
89-
///
90-
/// After this flag is encountered, the rest of the command line arguments are passed to the legacy build system.
91-
#[arg(long, allow_hyphen_values = true, num_args = 0..)]
92-
legacy: Option<Vec<String>>,
93-
}
7+
use rewatch::{build, cli, cmd, lock, watcher};
948

959
fn main() -> Result<()> {
96-
let args = Args::parse();
97-
98-
if let Some(legacy_args) = args.legacy {
99-
let code = build::pass_through_legacy(legacy_args);
100-
std::process::exit(code);
101-
}
10+
let args = cli::Cli::parse();
10211

10312
let log_level_filter = args.verbose.log_level_filter();
10413

@@ -108,19 +17,27 @@ fn main() -> Result<()> {
10817
.target(env_logger::fmt::Target::Stdout)
10918
.init();
11019

111-
let filter = args
112-
.filter
113-
.map(|filter| Regex::new(filter.as_ref()).expect("Could not parse regex"));
20+
let command = args.command.unwrap_or(cli::Command::Build(args.build_args));
11421

115-
match args.compiler_args {
116-
None => (),
117-
Some(path) => {
22+
// handle legacy and compiler args early, because we don't need a lock for them
23+
match command {
24+
cli::Command::Legacy { legacy_args } => {
25+
let code = build::pass_through_legacy(legacy_args);
26+
std::process::exit(code);
27+
}
28+
cli::Command::CompilerArgs {
29+
path,
30+
dev,
31+
rescript_version,
32+
bsc_path,
33+
} => {
11834
println!(
11935
"{}",
120-
build::get_compiler_args(&path, args.rescript_version, args.bsc_path, args.dev)?
36+
build::get_compiler_args(&path, rescript_version, bsc_path, dev)?
12137
);
12238
std::process::exit(0);
12339
}
40+
_ => (),
12441
}
12542

12643
// The 'normal run' mode will show the 'pretty' formatted progress. But if we turn off the log
@@ -132,48 +49,58 @@ fn main() -> Result<()> {
13249
println!("Could not start Rewatch: {e}");
13350
std::process::exit(1)
13451
}
135-
lock::Lock::Aquired(_) => match args.command {
136-
Command::Clean => build::clean::clean(&args.folder, show_progress, args.bsc_path),
137-
Command::Build => {
52+
lock::Lock::Aquired(_) => match command {
53+
cli::Command::Clean { bsc_path } => build::clean::clean(&args.folder, show_progress, bsc_path),
54+
cli::Command::Build(build_args) => {
55+
let filter = build_args
56+
.filter
57+
.map(|filter| Regex::new(filter.as_ref()).expect("Could not parse regex"));
13858
match build::build(
13959
&filter,
14060
&args.folder,
14161
show_progress,
142-
args.no_timing,
143-
args.create_sourcedirs,
144-
args.bsc_path,
145-
args.dev,
62+
build_args.no_timing,
63+
build_args.create_sourcedirs,
64+
build_args.bsc_path,
65+
build_args.dev,
14666
) {
14767
Err(e) => {
14868
println!("{e}");
14969
std::process::exit(1)
15070
}
15171
Ok(_) => {
152-
if let Some(args_after_build) = args.after_build {
72+
if let Some(args_after_build) = build_args.after_build {
15373
cmd::run(args_after_build)
15474
}
15575
std::process::exit(0)
15676
}
15777
};
15878
}
159-
Command::Watch => {
79+
cli::Command::Watch(watch_args) => {
80+
let filter = watch_args
81+
.filter
82+
.map(|filter| Regex::new(filter.as_ref()).expect("Could not parse regex"));
16083
watcher::start(
16184
&filter,
16285
show_progress,
16386
&args.folder,
164-
args.after_build,
165-
args.create_sourcedirs,
166-
args.dev,
87+
watch_args.after_build,
88+
watch_args.create_sourcedirs,
89+
watch_args.dev,
16790
);
16891

16992
Ok(())
17093
}
171-
Command::Format => {
172-
todo!("Format not implemented yet");
173-
}
174-
Command::Dump => {
175-
todo!("Dump not implemented yet");
176-
}
94+
cli::Command::CompilerArgs { .. } | cli::Command::Legacy { .. } => {
95+
unreachable!("command already handled")
96+
} // Command::Format => {
97+
// let code = build::pass_through_legacy(vec!["format".to_owned()]);
98+
// std::process::exit(code);
99+
// }
100+
// Command::Dump => {
101+
// let code = build::pass_through_legacy(vec!["dump".to_owned()]);
102+
// std::process::exit(code);
103+
// }
177104
},
178105
}
179106
}

0 commit comments

Comments
 (0)