71 lines
2.5 KiB
Rust
71 lines
2.5 KiB
Rust
|
|
//! Pure helpers for pipeline item ID parsing.
|
||
|
|
//!
|
||
|
|
//! Pipeline item IDs share the format `{number}_{type}_{slug}`, e.g.
|
||
|
|
//! `"42_story_foo"`, `"7_bug_bar"`, `"100_refactor_baz"`. The functions here
|
||
|
|
//! extract or validate the leading numeric segment without performing any I/O.
|
||
|
|
|
||
|
|
/// Extract the numeric prefix from a pipeline item ID.
|
||
|
|
///
|
||
|
|
/// Returns the leading digit sequence from IDs like `"42_story_foo"` → `"42"`.
|
||
|
|
/// Returns `None` if the ID has no leading digit sequence.
|
||
|
|
pub fn extract_item_number(item_id: &str) -> Option<&str> {
|
||
|
|
item_id
|
||
|
|
.split('_')
|
||
|
|
.next()
|
||
|
|
.filter(|s| !s.is_empty() && s.chars().all(|c| c.is_ascii_digit()))
|
||
|
|
}
|
||
|
|
|
||
|
|
#[allow(dead_code)]
|
||
|
|
/// Return `true` if `item_id` has a valid `{digits}_` prefix format.
|
||
|
|
///
|
||
|
|
/// Valid: `"42_story_foo"`, `"1_bug_bar"`.
|
||
|
|
/// Invalid: `"story_without_number"`, `""`, `"abc_story"`.
|
||
|
|
pub fn has_valid_id_prefix(item_id: &str) -> bool {
|
||
|
|
extract_item_number(item_id).is_some()
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── Tests ─────────────────────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
#[cfg(test)]
|
||
|
|
mod tests {
|
||
|
|
use super::*;
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn extract_item_number_extracts_prefix() {
|
||
|
|
assert_eq!(extract_item_number("42_story_foo"), Some("42"));
|
||
|
|
assert_eq!(extract_item_number("1_bug_bar"), Some("1"));
|
||
|
|
assert_eq!(extract_item_number("100_refactor_baz"), Some("100"));
|
||
|
|
assert_eq!(
|
||
|
|
extract_item_number("261_story_bot_notifications"),
|
||
|
|
Some("261")
|
||
|
|
);
|
||
|
|
assert_eq!(extract_item_number("1_spike_research"), Some("1"));
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn extract_item_number_returns_none_for_no_numeric_prefix() {
|
||
|
|
assert_eq!(extract_item_number("story_without_number"), None);
|
||
|
|
assert_eq!(extract_item_number("abc_story"), None);
|
||
|
|
assert_eq!(extract_item_number("abc_story_thing"), None);
|
||
|
|
assert_eq!(extract_item_number(""), None);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn extract_item_number_returns_none_for_empty_first_segment() {
|
||
|
|
// Leading underscore: first segment is "".
|
||
|
|
assert_eq!(extract_item_number("_story_thing"), None);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn has_valid_id_prefix_returns_true_for_valid_ids() {
|
||
|
|
assert!(has_valid_id_prefix("42_story_foo"));
|
||
|
|
assert!(has_valid_id_prefix("1_bug_bar"));
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn has_valid_id_prefix_returns_false_for_invalid_ids() {
|
||
|
|
assert!(!has_valid_id_prefix("story_no_number"));
|
||
|
|
assert!(!has_valid_id_prefix(""));
|
||
|
|
}
|
||
|
|
}
|