Skip to content

Commit f37f9c8

Browse files
committed
Some documentation
1 parent faca94b commit f37f9c8

File tree

1 file changed

+58
-56
lines changed

1 file changed

+58
-56
lines changed

src/lib.rs

Lines changed: 58 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
#![warn(clippy::pedantic)]
22
#![warn(clippy::complexity)]
3-
//! Sap - a Small Argument Parser
3+
//! # Sap - a Small Argument Parser
44
//!
5-
//! The only upside (currently) is being very minimal.
5+
//! A minimal Unix command-line argument parser with zero dependencies.
66
//!
7-
//! Unix only.
7+
//! ## Features
8+
//!
9+
//! - Parse short options (`-a`, `-b`), long options (`--verbose`), and option values
10+
//! - Combined short options (`-abc` = `-a -b -c`)
11+
//! - Support for option values (`--name=value`)
12+
//! - Error handling with descriptive messages
813
914
use std::{
1015
env,
@@ -15,25 +20,24 @@ use std::{
1520
os::unix::ffi::{OsStrExt, OsStringExt},
1621
};
1722

23+
/// Result type specific to Sap, using `ParsingError` as the default error type
1824
pub type Result<T, E = ParsingError> = core::result::Result<T, E>;
1925

2026
/// Represents a command-line argument
2127
#[derive(Debug, PartialEq, PartialOrd, Ord, Eq, Hash, Clone, Copy)]
2228
pub enum Argument<'a> {
23-
/// Represents an long option like
24-
/// `--example`.
29+
/// A long option like `--example`
2530
Long(&'a str),
2631

27-
/// Represets a singular character
28-
/// option, as in: `-q`
32+
/// A single character option like `-q`
2933
Short(char),
3034

31-
/// Regular argument
32-
/// like `/proc/meminfo`
35+
/// A positional argument like `file.txt` or `/path/to/file`
3336
Value(&'a str),
3437
}
3538

3639
impl<'a> Argument<'a> {
40+
/// Converts this argument into an error, optionally with a value
3741
pub fn into_error<A>(self, value: A) -> ParsingError
3842
where
3943
A: Into<Option<&'a str>>,
@@ -45,43 +49,39 @@ impl<'a> Argument<'a> {
4549
offender: arg.to_string(),
4650
value: value.into().map(String::from),
4751
format: "=",
48-
4952
prefix: "--",
5053
},
5154

5255
Short(arg) => ParsingError::UnexpectedArg {
5356
offender: arg.to_string(),
5457
value: value.into().map(String::from),
55-
56-
// cheap trick
57-
// omit it one day.
5858
format: " ",
59-
6059
prefix: "-",
6160
},
6261

6362
Value(arg) => ParsingError::UnexpectedArg {
6463
offender: arg.to_string(),
6564
value: None,
6665
format: "",
67-
6866
prefix: "",
6967
},
7068
}
7169
}
7270
}
7371

72+
/// Internal parser state
7473
enum State {
74+
/// Normal state, no special processing needed
7575
NotInteresting,
76+
/// Contains a value from previous option
7677
LeftoverValue(OsString),
78+
/// Combined short options state (-abc)
7779
Combined(usize, OsString),
80+
/// End of arguments (after --)
7881
End,
7982
}
8083

81-
/// Parser of the command-line arguments
82-
/// internally uses the `ArgsOs` iterator
83-
/// when created via the `parser_from_env`
84-
/// function.
84+
/// Parser for command-line arguments
8585
pub struct Parser<I> {
8686
iter: I,
8787
state: State,
@@ -94,12 +94,12 @@ pub struct Parser<I> {
9494
}
9595

9696
impl Parser<env::ArgsOs> {
97-
/// Creates a `Parser` using the `ArgsOs` iterator
98-
/// provided by the standard library.
97+
/// Creates a `Parser` using the program's command-line arguments.
9998
///
10099
/// # Errors
101100
///
102-
/// See `from_arbitrary` for details
101+
/// Returns an error if no arguments are available or the program name
102+
/// contains invalid UTF-8.
103103
pub fn from_env() -> Result<Self> {
104104
Self::from_arbitrary(env::args_os())
105105
}
@@ -109,20 +109,16 @@ impl<I> Parser<I>
109109
where
110110
I: Iterator<Item = OsString>,
111111
{
112-
/// Creates a `Parser` from an arbitrary `Iterator` with
113-
/// `Item` as `OsString`.
112+
/// Creates a `Parser` from any iterator that yields `OsString` items.
114113
///
115114
/// # Errors
116115
///
117-
/// At creation it checks the created `Iterator`
118-
/// if it contains the first value (the process name),
119-
/// if it does not contain it and/or the value is malformed
120-
/// the function will return `Err`
116+
/// Returns an error if the iterator is empty or the first item (program name)
117+
/// can't be converted to a valid UTF-8 string.
121118
pub fn from_arbitrary(mut iter: I) -> Result<Parser<I>> {
122119
let name = match iter.next() {
123120
None => {
124121
let err = ParsingError::Empty;
125-
126122
return Err(err);
127123
}
128124
Some(val) => match val.into_string() {
@@ -141,10 +137,13 @@ where
141137
})
142138
}
143139

144-
/// Moves the parser one option forward
145-
/// if it hits a `--`, it will start returning `None`
140+
/// Moves to the next argument, parsing it into an `Argument` enum.
141+
///
142+
/// # Returns
146143
///
147-
/// This happens because `--` signifies the end of arguments.
144+
/// - `Some(Ok(arg))` - Successfully parsed the next argument
145+
/// - `Some(Err(e))` - Found an argument but encountered an error parsing it
146+
/// - `None` - No more arguments or reached `--` separator
148147
pub fn forward(&mut self) -> Option<Result<Argument<'_>>> {
149148
if matches!(self.state, State::End) {
150149
return None;
@@ -155,7 +154,6 @@ where
155154
State::Combined(ref mut pos, ref str) => match str.as_bytes().get(*pos) {
156155
None => {
157156
self.state = State::NotInteresting;
158-
159157
return self.forward();
160158
}
161159

@@ -170,7 +168,6 @@ where
170168

171169
Some(ch) => {
172170
*pos += 1;
173-
174171
return Some(Ok(Argument::Short(*ch as char)));
175172
}
176173
},
@@ -182,7 +179,6 @@ where
182179
None => return None,
183180
Some(arg) => {
184181
self.last_long = Some(arg);
185-
186182
// Safety:
187183
//
188184
// We just placed the value in the variable
@@ -215,7 +211,6 @@ where
215211
let str_arg = match str::from_utf8(arg) {
216212
Err(_e) => {
217213
let err = ParsingError::InvalidString;
218-
219214
return Some(Err(err));
220215
}
221216
Ok(val) => val,
@@ -250,7 +245,6 @@ where
250245
let str_arg = match str::from_utf8(arg.as_encoded_bytes()) {
251246
Err(_e) => {
252247
let err = ParsingError::InvalidString;
253-
254248
return Some(Err(err));
255249
}
256250
Ok(val) => val,
@@ -261,9 +255,14 @@ where
261255
}
262256
}
263257

264-
/// Retrieves the value stored in the Parser
265-
/// without converting it to a UTF-8 string.
266-
/// not retrieving this value becomes an error.
258+
/// Retrieves the value associated with the previous option.
259+
///
260+
/// Returns the raw `OsString` value without UTF-8 conversion.
261+
///
262+
/// # Returns
263+
///
264+
/// - `Some(value)` - The option has a value (e.g., `--name=value`)
265+
/// - `None` - The option has no value or the value has already been consumed
267266
pub fn raw_value(&mut self) -> Option<OsString> {
268267
match self.state {
269268
State::LeftoverValue(_) => match mem::replace(&mut self.state, State::NotInteresting) {
@@ -275,18 +274,27 @@ where
275274
}
276275
}
277276

278-
/// Retrieves the value. This performs lossy conversion if the vlaue is not valid utf8
277+
/// Retrieves the value associated with the previous option as a UTF-8 string.
278+
///
279+
/// Performs lossy UTF-8 conversion if the value contains invalid UTF-8.
280+
///
281+
/// # Returns
282+
///
283+
/// - `Some(value)` - The option has a value (e.g., `--name=value`)
284+
/// - `None` - The option has no value or the value has already been consumed
279285
pub fn value(&mut self) -> Option<String> {
280286
self.raw_value().map(|v| v.to_string_lossy().into_owned())
281287
}
282288

283-
/// Ignore the value
284-
/// to not error out the parser
289+
/// Ignores any value associated with the current option.
290+
///
291+
/// Call this when you want to acknowledge but discard a value
292+
/// without causing an error.
285293
pub fn ignore_value(&mut self) {
286294
let _ = self.raw_value();
287295
}
288296

289-
/// Retrieve the name of the process
297+
/// Returns the program name (first argument).
290298
pub fn name(&self) -> &str {
291299
&self.name
292300
}
@@ -298,31 +306,25 @@ struct ParserIter<I: Iterator<Item = OsString>> {
298306
inner: Parser<I>,
299307
}
300308

301-
/// Error type describing the various ways
302-
/// this algorithm can fail
303-
/// and/or be induced to fail.
309+
/// Parsing error types with descriptive messages
304310
#[derive(Debug)]
305311
pub enum ParsingError {
306-
/// Something was wrong with a provided option.
312+
/// Invalid option syntax or format
307313
InvalidOption {
308314
reason: &'static str,
309315
offender: Option<OsString>,
310316
},
311317

312-
/// The value left after processing a long option
313-
/// was not consumed.
318+
/// A value was provided but not consumed by calling `value()` or `ignore_value()`
314319
UnconsumedValue { value: OsString },
315320

316-
/// The initial iterator was empty.
321+
/// The arguments iterator was empty (no program name)
317322
Empty,
318323

319-
/// The string is invalid.
324+
/// A string containing invalid UTF-8 was encountered
320325
InvalidString,
321326

322-
/// This error is not used by the parser,
323-
/// however it is there to let users
324-
/// create errors from
325-
/// arguments that they deem unexpected
327+
/// An unexpected or unrecognized argument was provided
326328
UnexpectedArg {
327329
offender: String,
328330
value: Option<String>,

0 commit comments

Comments
 (0)