pub struct Args<'a> {
rest: &'a str,
}
impl<'a> Args<'a> {
#[must_use]
pub fn new(tail: &'a str) -> Self {
Args {
rest: tail.trim_start(),
}
}
#[must_use]
pub fn next_token(&mut self) -> Option<&'a str> {
self.rest = self.rest.trim_start();
if self.rest.is_empty() {
return None;
}
let end = self
.rest
.find(char::is_whitespace)
.unwrap_or(self.rest.len());
let (token, rest) = self.rest.split_at(end);
self.rest = rest;
Some(token)
}
#[must_use]
pub fn rest(self) -> &'a str {
self.rest.trim()
}
#[must_use]
pub fn rest_tokens(self) -> Vec<String> {
self.rest.split_whitespace().map(str::to_string).collect()
}
}
#[cfg(test)]
mod tests {
use super::Args;
#[test]
fn next_token_yields_words_in_order() {
let mut args = Args::new("alpha beta gamma");
assert_eq!(args.next_token(), Some("alpha"));
assert_eq!(args.next_token(), Some("beta"));
assert_eq!(args.next_token(), Some("gamma"));
assert_eq!(args.next_token(), None);
}
#[test]
fn next_token_collapses_runs_of_whitespace() {
let mut args = Args::new(" alpha \t beta ");
assert_eq!(args.next_token(), Some("alpha"));
assert_eq!(args.next_token(), Some("beta"));
assert_eq!(args.next_token(), None);
}
#[test]
fn empty_input_yields_no_tokens() {
let mut args = Args::new(" ");
assert_eq!(args.next_token(), None);
}
#[test]
fn rest_returns_remaining_text_verbatim_but_trimmed() {
let mut args = Args::new("set topic hello world ");
assert_eq!(args.next_token(), Some("set"));
assert_eq!(args.rest(), "topic hello world");
}
#[test]
fn rest_is_empty_when_input_exhausted() {
let mut args = Args::new("only");
assert_eq!(args.next_token(), Some("only"));
assert_eq!(args.rest(), "");
}
#[test]
fn rest_tokens_collects_remaining_words() {
let mut args = Args::new("a b c d");
assert_eq!(args.next_token(), Some("a"));
assert_eq!(args.rest_tokens(), vec!["b", "c", "d"]);
}
}