Skip to content

Commit 81dea3b

Browse files
committed
fix: Use longer lifetime in clap_lex ParsedArg
1 parent eabbc6b commit 81dea3b

File tree

2 files changed

+76
-10
lines changed

2 files changed

+76
-10
lines changed

clap_lex/src/lib.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -190,24 +190,24 @@ impl RawArgs {
190190
}
191191

192192
/// Advance the cursor, returning the next [`ParsedArg`]
193-
pub fn next(&self, cursor: &mut ArgCursor) -> Option<ParsedArg<'_>> {
193+
pub fn next<'s>(&'s self, cursor: &mut ArgCursor) -> Option<ParsedArg<'s>> {
194194
self.next_os(cursor).map(ParsedArg::new)
195195
}
196196

197197
/// Advance the cursor, returning a raw argument value.
198-
pub fn next_os(&self, cursor: &mut ArgCursor) -> Option<&OsStr> {
198+
pub fn next_os<'s>(&'s self, cursor: &mut ArgCursor) -> Option<&'s OsStr> {
199199
let next = self.items.get(cursor.cursor).map(|s| s.as_os_str());
200200
cursor.cursor = cursor.cursor.saturating_add(1);
201201
next
202202
}
203203

204204
/// Return the next [`ParsedArg`]
205-
pub fn peek(&self, cursor: &ArgCursor) -> Option<ParsedArg<'_>> {
205+
pub fn peek<'s>(&'s self, cursor: &ArgCursor) -> Option<ParsedArg<'s>> {
206206
self.peek_os(cursor).map(ParsedArg::new)
207207
}
208208

209209
/// Return a raw argument value.
210-
pub fn peek_os(&self, cursor: &ArgCursor) -> Option<&OsStr> {
210+
pub fn peek_os<'s>(&'s self, cursor: &ArgCursor) -> Option<&'s OsStr> {
211211
self.items.get(cursor.cursor).map(|s| s.as_os_str())
212212
}
213213

@@ -224,7 +224,7 @@ impl RawArgs {
224224
/// let mut paths = raw.remaining(&mut cursor).map(PathBuf::from).collect::<Vec<_>>();
225225
/// println!("{paths:?}");
226226
/// ```
227-
pub fn remaining(&self, cursor: &mut ArgCursor) -> impl Iterator<Item = &OsStr> {
227+
pub fn remaining<'s>(&'s self, cursor: &mut ArgCursor) -> impl Iterator<Item = &'s OsStr> {
228228
let remaining = self.items[cursor.cursor..].iter().map(|s| s.as_os_str());
229229
cursor.cursor = self.items.len();
230230
remaining
@@ -321,7 +321,7 @@ impl<'s> ParsedArg<'s> {
321321
}
322322

323323
/// Treat as a long-flag
324-
pub fn to_long(&self) -> Option<(Result<&str, &OsStr>, Option<&OsStr>)> {
324+
pub fn to_long(&self) -> Option<(Result<&'s str, &'s OsStr>, Option<&'s OsStr>)> {
325325
let raw = self.inner;
326326
let remainder = raw.strip_prefix("--")?;
327327
if remainder.is_empty() {
@@ -344,7 +344,7 @@ impl<'s> ParsedArg<'s> {
344344
}
345345

346346
/// Treat as a short-flag
347-
pub fn to_short(&self) -> Option<ShortFlags<'_>> {
347+
pub fn to_short(&self) -> Option<ShortFlags<'s>> {
348348
if let Some(remainder_os) = self.inner.strip_prefix("-") {
349349
if remainder_os.starts_with("-") {
350350
None
@@ -371,7 +371,7 @@ impl<'s> ParsedArg<'s> {
371371
/// **NOTE:** May return a flag or an escape.
372372
///
373373
/// </div>
374-
pub fn to_value_os(&self) -> &OsStr {
374+
pub fn to_value_os(&self) -> &'s OsStr {
375375
self.inner
376376
}
377377

@@ -382,14 +382,14 @@ impl<'s> ParsedArg<'s> {
382382
/// **NOTE:** May return a flag or an escape.
383383
///
384384
/// </div>
385-
pub fn to_value(&self) -> Result<&str, &OsStr> {
385+
pub fn to_value(&self) -> Result<&'s str, &'s OsStr> {
386386
self.inner.to_str().ok_or(self.inner)
387387
}
388388

389389
/// Safely print an argument that may contain non-UTF8 content
390390
///
391391
/// This may perform lossy conversion, depending on the platform. If you would like an implementation which escapes the path please use Debug instead.
392-
pub fn display(&self) -> impl std::fmt::Display + '_ {
392+
pub fn display(&self) -> impl std::fmt::Display + 's {
393393
self.inner.to_string_lossy()
394394
}
395395
}

clap_lex/tests/testsuite/lexer.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,69 @@ fn insert() {
1919
.collect::<Vec<_>>();
2020
assert_eq!(rest, vec!["bin", "a", "1", "2", "3", "b", "c"]);
2121
}
22+
23+
#[test]
24+
fn zero_copy_parsing() {
25+
use clap_lex::RawArgs;
26+
use std::ffi::OsStr;
27+
#[derive(Debug, PartialEq)]
28+
struct Args<'s> {
29+
bin_name: &'s OsStr,
30+
verbose_flag: bool,
31+
remainder: Vec<&'s OsStr>,
32+
}
33+
fn parse(raw: &RawArgs) -> Result<Args<'_>, &'static str> {
34+
let mut cursor = raw.cursor();
35+
let Some(bin_arg) = raw.next(&mut cursor) else {
36+
return Err("missing bin name");
37+
};
38+
let bin_name = bin_arg.to_value_os();
39+
let Some(first_arg) = raw.next(&mut cursor) else {
40+
return Ok(Args {
41+
bin_name,
42+
verbose_flag: false,
43+
remainder: Vec::new(),
44+
});
45+
};
46+
let verbose_flag = if let Some(flag) = first_arg.to_long() {
47+
match flag {
48+
(Ok("verbose"), None) => true,
49+
_ => return Err("unexpected flag"),
50+
}
51+
} else {
52+
false
53+
};
54+
let mut remainder = Vec::new();
55+
if !verbose_flag {
56+
remainder.push(first_arg.to_value_os());
57+
}
58+
remainder.extend(raw.remaining(&mut cursor));
59+
Ok(Args {
60+
bin_name,
61+
verbose_flag,
62+
remainder,
63+
})
64+
}
65+
66+
let raw1 = RawArgs::new(["bin", "--verbose", "a", "b", "c"]);
67+
let parsed1 = parse(&raw1).unwrap();
68+
assert_eq!(
69+
parsed1,
70+
Args {
71+
bin_name: OsStr::new("bin"),
72+
verbose_flag: true,
73+
remainder: vec![OsStr::new("a"), OsStr::new("b"), OsStr::new("c")]
74+
}
75+
);
76+
77+
let raw2 = RawArgs::new(["bin", "a", "b", "c"]);
78+
let parsed2 = parse(&raw2).unwrap();
79+
assert_eq!(
80+
parsed2,
81+
Args {
82+
bin_name: OsStr::new("bin"),
83+
verbose_flag: false,
84+
remainder: vec![OsStr::new("a"), OsStr::new("b"), OsStr::new("c")]
85+
}
86+
);
87+
}

0 commit comments

Comments
 (0)