pub(crate) fn parse(action: &str) -> Option<(&str, &str)> {
let rest = action.strip_prefix("setting:")?;
let (key, verb) = rest.rsplit_once(':')?;
(!key.is_empty()).then_some((key, verb))
}
pub(crate) fn key_with_verb<'a>(action: &'a str, verb: &str) -> Option<&'a str> {
parse(action)
.filter(|&(_, v)| v == verb)
.map(|(key, _)| key)
}
pub(crate) fn cycle_key(action: &str) -> Option<&str> {
parse(action)
.filter(|&(_, verb)| verb == "next" || verb == "open")
.map(|(key, _)| key)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_splits_key_and_verb() {
assert_eq!(
parse("setting:render_scale:drag"),
Some(("render_scale", "drag"))
);
assert_eq!(parse("setting:jump:rebind"), Some(("jump", "rebind")));
}
#[test]
fn parse_rejects_other_actions_and_empty_keys() {
for action in ["screen:show:pause", "menu:quit", "", "drag", "setting:"] {
assert_eq!(parse(action), None, "{action}");
}
assert_eq!(parse("setting:no_verb"), None);
assert_eq!(parse("setting::drag"), None);
}
#[test]
fn key_with_verb_matches_only_its_verb() {
assert_eq!(
key_with_verb("setting:render_scale:drag", "drag"),
Some("render_scale")
);
assert_eq!(key_with_verb("setting:render_scale:next", "drag"), None);
assert_eq!(key_with_verb("setting::rebind", "rebind"), None);
}
#[test]
fn cycle_key_accepts_a_stepper_next_and_a_dropdown_open() {
assert_eq!(cycle_key("setting:shadows:next"), Some("shadows"));
assert_eq!(cycle_key("setting:resolution:open"), Some("resolution"));
assert_eq!(cycle_key("setting:shadows:prev"), None);
assert_eq!(cycle_key("screen:hide"), None);
assert_eq!(cycle_key("setting::next"), None);
assert_eq!(cycle_key("setting::open"), None);
}
}