mod change_id;
mod error;
mod module_id;
mod spec_id;
pub(crate) mod sub_module_id;
pub use change_id::parse_change_id;
pub use change_id::{ChangeId, ParsedChangeId};
pub use error::IdParseError;
pub use module_id::parse_module_id;
pub use module_id::{ModuleId, ParsedModuleId};
pub use spec_id::parse_spec_id;
pub use spec_id::{ParsedSpecId, SpecId};
pub use sub_module_id::parse_sub_module_id;
pub use sub_module_id::{ParsedSubModuleId, SubModuleId};
pub(crate) fn is_all_ascii_digits(s: &str) -> bool {
if s.is_empty() {
return false;
}
for c in s.chars() {
if !c.is_ascii_digit() {
return false;
}
}
true
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ItoIdKind {
ModuleId,
SubModuleId,
ModuleChangeId,
SubModuleChangeId,
}
pub fn classify_id(input: &str) -> ItoIdKind {
let prefix = match input.split_once('_') {
Some((left, _)) => left,
None => input,
};
let has_dot = prefix.contains('.');
let has_hyphen = prefix.contains('-');
if has_dot && has_hyphen {
ItoIdKind::SubModuleChangeId
} else if has_dot {
ItoIdKind::SubModuleId
} else if has_hyphen {
ItoIdKind::ModuleChangeId
} else {
ItoIdKind::ModuleId
}
}
pub fn looks_like_change_id(input: &str) -> bool {
let input = input.trim();
if input.is_empty() {
return false;
}
let mut digit_prefix_len = 0usize;
let mut has_hyphen = false;
let mut has_underscore = false;
for ch in input.chars() {
if ch.is_ascii_digit() && digit_prefix_len == 0 {
digit_prefix_len = 1;
continue;
}
if ch.is_ascii_digit() && digit_prefix_len > 0 {
digit_prefix_len += 1;
continue;
}
if digit_prefix_len == 0 {
break;
}
match ch {
'-' => has_hyphen = true,
'_' => has_underscore = true,
'.' => {}
_ => {}
}
}
digit_prefix_len > 0 && has_hyphen && has_underscore
}
pub fn looks_like_module_id(input: &str) -> bool {
let input = input.trim();
let Some(first) = input.chars().next() else {
return false;
};
first.is_ascii_digit()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn looks_like_change_id_requires_digits_hyphen_and_underscore() {
assert!(looks_like_change_id("001-02_hello"));
assert!(!looks_like_change_id("-02_hello"));
assert!(!looks_like_change_id("001_hello"));
assert!(!looks_like_change_id("001-02hello"));
assert!(!looks_like_change_id("abc-02_hello"));
}
#[test]
fn looks_like_change_id_recognizes_sub_module_format() {
assert!(looks_like_change_id("005.01-03_my-change"));
assert!(looks_like_change_id("5.1-2_foo"));
}
#[test]
fn looks_like_module_id_is_digit_prefixed() {
assert!(looks_like_module_id("001"));
assert!(looks_like_module_id("001_demo"));
assert!(looks_like_module_id(" 001_demo "));
assert!(!looks_like_module_id(""));
assert!(!looks_like_module_id("demo"));
assert!(!looks_like_module_id("_001_demo"));
}
#[test]
fn classify_id_module_change_id() {
assert_eq!(classify_id("005-01_my-change"), ItoIdKind::ModuleChangeId);
assert_eq!(classify_id("1-2_foo"), ItoIdKind::ModuleChangeId);
}
#[test]
fn classify_id_sub_module_change_id() {
assert_eq!(
classify_id("005.01-03_my-change"),
ItoIdKind::SubModuleChangeId
);
assert_eq!(classify_id("5.1-2_foo"), ItoIdKind::SubModuleChangeId);
}
#[test]
fn classify_id_sub_module_id() {
assert_eq!(classify_id("005.01"), ItoIdKind::SubModuleId);
assert_eq!(classify_id("005.01_core-api"), ItoIdKind::SubModuleId);
}
#[test]
fn classify_id_module_id() {
assert_eq!(classify_id("005"), ItoIdKind::ModuleId);
assert_eq!(classify_id("005_dev-tooling"), ItoIdKind::ModuleId);
assert_eq!(classify_id("1"), ItoIdKind::ModuleId);
}
#[test]
fn classify_id_hyphen_without_underscore_is_module_change_id() {
assert_eq!(classify_id("005-01"), ItoIdKind::ModuleChangeId);
}
}