Files
huskies/server/src/service/common/item_id.rs
T

87 lines
3.1 KiB
Rust
Raw Normal View History

//! Pure helpers for pipeline item ID parsing.
//!
//! Pipeline item IDs are numeric strings, e.g. `"42"`, `"730"`. Legacy items
//! may use the old `{number}_{type}_{slug}` format (e.g. `"42_story_foo"`).
//! The functions here extract or validate the leading numeric segment without
//! performing any I/O.
/// Extract the numeric prefix from a pipeline item ID.
///
/// Works for both numeric-only IDs (`"42"` → `"42"`) and legacy slug-format
/// IDs (`"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` starts with a numeric prefix.
///
/// Valid: `"42"`, `"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_works_for_numeric_only_ids() {
// Numeric-only IDs (the new canonical format).
assert_eq!(extract_item_number("42"), Some("42"));
assert_eq!(extract_item_number("730"), Some("730"));
assert_eq!(extract_item_number("1"), Some("1"));
}
#[test]
fn has_valid_id_prefix_returns_true_for_numeric_only() {
assert!(has_valid_id_prefix("42"));
assert!(has_valid_id_prefix("730"));
}
#[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(""));
}
}