use std::collections::HashMap;
pub use crate::review_policy::ReviewMode;
#[derive(Debug, Clone)]
pub struct Setting {
pub mode: ReviewMode,
pub set_by: String,
}
pub fn command(text: &str) -> Option<Option<ReviewMode>> {
let mut words = text.split_whitespace();
if !words.next()?.eq_ignore_ascii_case("review") {
return None;
}
match (words.next(), words.next()) {
(None, _) => Some(None),
(Some(mode), None) => ReviewMode::parse(mode).map(Some),
_ => None,
}
}
pub fn channel_scope_key(channel: &str) -> String {
format!("channel:{channel}")
}
pub fn scope_for(channel: &str, in_thread: Option<&str>) -> (String, &'static str) {
match in_thread {
Some(ts) => (super::threads::key_for(channel, ts), "this thread"),
None => (
channel_scope_key(channel),
"top-level prompts in this channel",
),
}
}
pub fn effective<'a>(
settings: &'a HashMap<String, Setting>,
thread_key: &str,
channel: &str,
) -> Option<&'a Setting> {
settings
.get(thread_key)
.or_else(|| settings.get(&channel_scope_key(channel)))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_mode_is_set_by_the_exact_command_word_and_nothing_longer() {
assert_eq!(command("review auto"), Some(Some(ReviewMode::Auto)));
assert_eq!(command(" REVIEW Now "), Some(Some(ReviewMode::Now)));
assert_eq!(command("review later"), Some(Some(ReviewMode::Later)));
assert_eq!(command("review"), Some(None), "the bare word asks");
for prose in [
"please review auto tomorrow",
"review auto now",
"set review to auto",
"review the design doc",
"auto",
"reviewauto",
"",
] {
assert_eq!(command(prose), None, "{prose:?} must not set a mode");
}
assert_eq!(command("review always"), None);
}
#[test]
fn every_mode_names_itself_and_round_trips_through_the_command_word() {
for mode in [ReviewMode::Now, ReviewMode::Later, ReviewMode::Auto] {
assert_eq!(
command(&format!("review {}", mode.name())),
Some(Some(mode))
);
assert!(!mode.describe().is_empty());
}
}
#[test]
fn a_top_level_setting_scopes_to_the_channel_and_later_top_level_runs_see_it() {
let channel = "D1";
let mut settings: HashMap<String, Setting> = HashMap::new();
let (key, scope) = scope_for(channel, None);
assert_eq!(key, channel_scope_key(channel));
assert!(
scope.contains("channel"),
"the confirmation names the real scope: {scope}"
);
settings.insert(
key,
Setting {
mode: ReviewMode::Auto,
set_by: "U_OWNER".into(),
},
);
let later_run_key = super::super::threads::key_for(channel, "1755200000.000100");
let seen = effective(&settings, &later_run_key, channel).expect("the mode governs");
assert_eq!(seen.mode, ReviewMode::Auto);
}
#[test]
fn a_threads_own_setting_wins_over_its_channels() {
let channel = "D1";
let mut settings: HashMap<String, Setting> = HashMap::new();
settings.insert(
channel_scope_key(channel),
Setting {
mode: ReviewMode::Auto,
set_by: "U_OWNER".into(),
},
);
let (thread_key, scope) = scope_for(channel, Some("1755100000.000200"));
assert_eq!(scope, "this thread");
settings.insert(
thread_key.clone(),
Setting {
mode: ReviewMode::Now,
set_by: "U_OWNER".into(),
},
);
assert_eq!(
effective(&settings, &thread_key, channel).unwrap().mode,
ReviewMode::Now,
"the narrower gesture wins"
);
let other = super::super::threads::key_for(channel, "1755100001.000300");
assert_eq!(
effective(&settings, &other, channel).unwrap().mode,
ReviewMode::Auto
);
assert!(!thread_key.contains(':'));
assert!(channel_scope_key(channel).contains(':'));
}
}