Skip to content

Commit 15a8ab2

Browse files
committed
Fix non-utf8 error
1 parent cd61d37 commit 15a8ab2

File tree

1 file changed

+49
-29
lines changed

1 file changed

+49
-29
lines changed

rewatch/src/main.rs

Lines changed: 49 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
use anyhow::Result;
22
use clap::{CommandFactory, Parser, error::ErrorKind};
33
use log::LevelFilter;
4-
use std::{env, io::Write, path::Path};
4+
use std::{env, ffi::OsString, io::Write, path::Path};
55

66
use rescript::{build, cli, cmd, format, lock, watcher};
77

88
fn main() -> Result<()> {
9-
let raw_args: Vec<String> = env::args().collect();
9+
// Use `args_os` so non-UTF bytes still reach clap for proper error reporting on platforms that
10+
// allow arbitrary argv content.
11+
let raw_args: Vec<OsString> = env::args_os().collect();
1012
let cli = parse_cli(raw_args).unwrap_or_else(|err| err.exit());
1113

1214
let log_level_filter = cli.verbose.log_level_filter();
@@ -113,14 +115,14 @@ fn get_lock(folder: &str) -> lock::Lock {
113115
}
114116
}
115117

116-
fn parse_cli(raw_args: Vec<String>) -> Result<cli::Cli, clap::Error> {
118+
fn parse_cli(raw_args: Vec<OsString>) -> Result<cli::Cli, clap::Error> {
117119
match cli::Cli::try_parse_from(&raw_args) {
118120
Ok(cli) => Ok(cli),
119121
Err(err) => {
120122
if should_default_to_build(&err, &raw_args) {
121123
let mut fallback_args = raw_args.clone();
122124
let insert_at = index_after_global_flags(&fallback_args);
123-
fallback_args.insert(insert_at, "build".into());
125+
fallback_args.insert(insert_at, OsString::from("build"));
124126

125127
match cli::Cli::try_parse_from(&fallback_args) {
126128
Ok(cli) => Ok(cli),
@@ -133,7 +135,7 @@ fn parse_cli(raw_args: Vec<String>) -> Result<cli::Cli, clap::Error> {
133135
}
134136
}
135137

136-
fn should_default_to_build(err: &clap::Error, args: &[String]) -> bool {
138+
fn should_default_to_build(err: &clap::Error, args: &[OsString]) -> bool {
137139
match err.kind() {
138140
ErrorKind::MissingSubcommand
139141
| ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand
@@ -149,7 +151,7 @@ fn should_default_to_build(err: &clap::Error, args: &[String]) -> bool {
149151
}
150152
}
151153

152-
fn index_after_global_flags(args: &[String]) -> usize {
154+
fn index_after_global_flags(args: &[OsString]) -> usize {
153155
let mut idx = 1;
154156
while let Some(arg) = args.get(idx) {
155157
if is_global_flag(arg) {
@@ -161,44 +163,52 @@ fn index_after_global_flags(args: &[String]) -> usize {
161163
idx.min(args.len())
162164
}
163165

164-
fn is_global_flag(arg: &str) -> bool {
166+
fn is_global_flag(arg: &OsString) -> bool {
165167
matches!(
166-
arg,
167-
"-v" | "-vv"
168-
| "-vvv"
169-
| "-vvvv"
170-
| "-q"
171-
| "-qq"
172-
| "-qqq"
173-
| "-qqqq"
174-
| "--verbose"
175-
| "--quiet"
176-
| "-h"
177-
| "--help"
178-
| "-V"
179-
| "--version"
168+
arg.to_str(),
169+
Some(
170+
"-v" | "-vv"
171+
| "-vvv"
172+
| "-vvvv"
173+
| "-q"
174+
| "-qq"
175+
| "-qqq"
176+
| "-qqqq"
177+
| "--verbose"
178+
| "--quiet"
179+
| "-h"
180+
| "--help"
181+
| "-V"
182+
| "--version"
183+
)
180184
)
181185
}
182186

183-
fn first_non_global_arg(args: &[String]) -> Option<&str> {
184-
args.iter()
185-
.skip(1)
186-
.find(|arg| !is_global_flag(arg))
187-
.map(|s| s.as_str())
187+
fn first_non_global_arg(args: &[OsString]) -> Option<&OsString> {
188+
args.iter().skip(1).find(|arg| !is_global_flag(arg))
188189
}
189190

190-
fn is_known_subcommand(arg: &str) -> bool {
191+
fn is_known_subcommand(arg: &OsString) -> bool {
192+
let Some(arg_str) = arg.to_str() else {
193+
return false;
194+
};
195+
191196
cli::Cli::command().get_subcommands().any(|subcommand| {
192-
subcommand.get_name() == arg || subcommand.get_all_aliases().any(|alias| alias == arg)
197+
subcommand.get_name() == arg_str || subcommand.get_all_aliases().any(|alias| alias == arg_str)
193198
})
194199
}
195200

196201
#[cfg(test)]
197202
mod tests {
198203
use super::*;
204+
use std::ffi::OsString;
199205

200206
fn parse(args: &[&str]) -> Result<cli::Cli, clap::Error> {
201-
parse_cli(args.iter().map(|arg| arg.to_string()).collect())
207+
parse_cli(args.iter().map(OsString::from).collect())
208+
}
209+
210+
fn parse_os(args: Vec<OsString>) -> Result<cli::Cli, clap::Error> {
211+
parse_cli(args)
202212
}
203213

204214
#[test]
@@ -245,4 +255,14 @@ mod tests {
245255
let err = parse(&["rescript", "--version"]).expect_err("expected clap version error");
246256
assert_eq!(err.kind(), ErrorKind::DisplayVersion);
247257
}
258+
259+
#[cfg(unix)]
260+
#[test]
261+
fn non_utf_argument_returns_error() {
262+
use std::os::unix::ffi::OsStringExt;
263+
264+
let args = vec![OsString::from("rescript"), OsString::from_vec(vec![0xff])];
265+
let err = parse_os(args).expect_err("expected clap to report invalid utf8");
266+
assert_eq!(err.kind(), ErrorKind::InvalidUtf8);
267+
}
248268
}

0 commit comments

Comments
 (0)