pub(crate) fn parse_rewind_target(arg: Option<&str>) -> Result<ParsedRewindTarget, String> {
let Some(arg) = arg.map(str::trim).filter(|arg| !arg.is_empty()) else {
return Ok(ParsedRewindTarget::NeedsSelection);
};
let mut dry_run = false;
let mut target = None;
let mut mode = super::RewindMode::default();
let mut tokens = arg.split_whitespace();
while let Some(token) = tokens.next() {
match token {
"--dry-run" => dry_run = true,
"--mode" => {
mode = match tokens.next() {
Some("conversation") => super::RewindMode::Conversation,
Some("files") => super::RewindMode::Files,
Some("both") => super::RewindMode::Both,
_ => return Err("mode must be conversation, files, or both".to_string()),
};
}
"--to" => {
let Some(value) = tokens.next() else {
return Err("missing turn after --to".to_string());
};
target = Some(parse_positive_turn(value)?);
}
value if target.is_none() => target = Some(parse_positive_turn(value)?),
_ => return Err("unexpected rewind argument".to_string()),
}
}
let Some(target_turn) = target else {
return Err("missing rewind target turn".to_string());
};
Ok(ParsedRewindTarget::Target {
target_turn,
dry_run,
mode,
})
}
fn parse_positive_turn(value: &str) -> Result<u64, String> {
if value.contains("..") || value.starts_with('-') {
return Err("rewind target must be a positive turn number".to_string());
}
let turn = value
.parse::<u64>()
.map_err(|_| "rewind target must be a positive turn number".to_string())?;
if turn == 0 {
return Err("rewind target must be positive".to_string());
}
Ok(turn)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum ParsedRewindTarget {
NeedsSelection,
Target {
target_turn: u64,
dry_run: bool,
mode: super::RewindMode,
},
}