#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MarkerPhase {
Arrange,
Act,
Assert,
}
pub struct TestMarker {
pub line: usize,
pub label: String,
pub phases: Vec<MarkerPhase>,
}
impl TestMarker {
pub const FORMS: [&'static str; 6] = [
"Arrange & Act & Assert",
"Arrange & Act",
"Act & Assert",
"Arrange",
"Assert",
"Act",
];
pub fn parse(text: &str, line: usize) -> Option<Self> {
let comment = text.trim_start().strip_prefix("//")?.trim_start();
let form = Self::form_of(comment)?;
Some(Self {
line,
label: format!("// {form}"),
phases: Self::phases_of(form),
})
}
fn form_of(comment: &str) -> Option<&'static str> {
Self::FORMS
.into_iter()
.find(|form| Self::is_form(comment, form))
}
fn is_form(comment: &str, form: &str) -> bool {
comment.strip_prefix(form).is_some_and(|rest| {
rest.chars()
.next()
.is_none_or(|next| !next.is_alphanumeric() && next != '_')
})
}
fn phases_of(form: &str) -> Vec<MarkerPhase> {
match form {
"Arrange & Act & Assert" => {
vec![MarkerPhase::Arrange, MarkerPhase::Act, MarkerPhase::Assert]
}
"Arrange & Act" => vec![MarkerPhase::Arrange, MarkerPhase::Act],
"Act & Assert" => vec![MarkerPhase::Act, MarkerPhase::Assert],
"Arrange" => vec![MarkerPhase::Arrange],
"Assert" => vec![MarkerPhase::Assert],
_ => vec![MarkerPhase::Act],
}
}
}