provenance-cli 0.2.3

Command-line interface for Provenance requirements traceability
pub(super) fn tokens(text: &str) -> Vec<(usize, &str)> {
    let mut tokens = Vec::new();
    let mut start = None;
    for (index, ch) in text.char_indices() {
        if ch.is_whitespace() {
            if let Some(begin) = start {
                let candidate = &text[begin..index];
                let continues_line_group = text[index..]
                    .trim_start()
                    .chars()
                    .next()
                    .is_some_and(|next| next.is_ascii_digit());
                if candidate.contains(':')
                    && candidate.trim_end().ends_with(',')
                    && continues_line_group
                {
                    continue;
                }
                start = None;
                tokens.push((begin, &text[begin..index]));
            }
        } else if start.is_none() {
            start = Some(index);
        }
    }
    if let Some(begin) = start {
        tokens.push((begin, &text[begin..]));
    }
    tokens
}

pub(super) fn trim_token(token: &str) -> (usize, &str) {
    const LEADING: &[char] = &['(', '[', '{', '"', '\'', '<'];
    const TRAILING: &[char] = &['.', ',', ';', ':', '!', '?', ')', ']', '}', '"', '\'', '>'];
    let trimmed = token.trim_start_matches(LEADING);
    let offset = token.len() - trimmed.len();
    (offset, trimmed.trim_end_matches(TRAILING))
}

pub(super) fn is_test_name(token: &str) -> bool {
    token.strip_prefix("test").is_some_and(|rest| {
        rest.starts_with(|ch: char| ch.is_ascii_uppercase())
            && rest
                .chars()
                .all(|ch| ch.is_ascii_alphanumeric() || ch == '_')
    })
}