pub fn is_mkdocs_auto_reference(reference: &str) -> bool {
if reference.is_empty() || reference.len() > 200 {
return false;
}
if reference.contains('/') {
return is_valid_slash_pattern(reference);
}
if reference.contains('.') {
return is_valid_dot_pattern(reference);
}
if reference.contains('-') && !reference.contains(' ') {
return is_valid_hyphen_pattern(reference);
}
false
}
fn is_valid_dot_pattern(reference: &str) -> bool {
if reference.starts_with('.') || reference.ends_with('.') {
return false;
}
let parts: Vec<&str> = reference.split('.').collect();
if parts.len() < 2 {
return false;
}
parts.iter().all(|part| {
!part.is_empty()
&& part.len() <= 50 && is_valid_identifier(part)
})
}
fn is_valid_hyphen_pattern(reference: &str) -> bool {
if reference.starts_with('-') || reference.ends_with('-') || reference.contains("--") {
return false;
}
if reference.len() < 3 {
return false;
}
reference
.chars()
.all(|c| c.is_ascii_lowercase() || c == '-' || c.is_ascii_digit())
}
fn is_valid_slash_pattern(reference: &str) -> bool {
let parts: Vec<&str> = reference.split('/').collect();
if parts.len() < 2 {
return false;
}
parts.iter().all(|part| {
!part.is_empty()
&& part.len() <= 50 && is_valid_path_component(part)
})
}
fn is_valid_identifier(s: &str) -> bool {
if s.is_empty() {
return false;
}
let first_char = s.chars().next().unwrap();
if !first_char.is_ascii_alphabetic() && first_char != '_' {
return false;
}
s.chars().all(|c| c.is_ascii_alphanumeric() || c == '_')
}
fn is_valid_path_component(s: &str) -> bool {
!s.is_empty()
&& s.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-' || c == '.')
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_valid_dot_patterns() {
assert!(is_mkdocs_auto_reference("module.Class"));
assert!(is_mkdocs_auto_reference("package.module.function"));
assert!(is_mkdocs_auto_reference("__init__.py"));
assert!(is_mkdocs_auto_reference("Class.__init__"));
assert!(is_mkdocs_auto_reference("a.b"));
assert!(!is_mkdocs_auto_reference(".")); assert!(!is_mkdocs_auto_reference("..")); assert!(!is_mkdocs_auto_reference("a.")); assert!(!is_mkdocs_auto_reference(".a")); assert!(!is_mkdocs_auto_reference("a..b")); assert!(!is_mkdocs_auto_reference("127.0.0.1")); }
#[test]
fn test_valid_hyphen_patterns() {
assert!(is_mkdocs_auto_reference("getting-started"));
assert!(is_mkdocs_auto_reference("api-reference"));
assert!(is_mkdocs_auto_reference("section-1"));
assert!(is_mkdocs_auto_reference("a-b"));
assert!(!is_mkdocs_auto_reference("-")); assert!(!is_mkdocs_auto_reference("--")); assert!(!is_mkdocs_auto_reference("-start")); assert!(!is_mkdocs_auto_reference("end-")); assert!(!is_mkdocs_auto_reference("double--hyphen")); assert!(!is_mkdocs_auto_reference("UPPER-CASE")); assert!(!is_mkdocs_auto_reference("Mixed-Case")); }
#[test]
fn test_valid_slash_patterns() {
assert!(is_mkdocs_auto_reference("api/v1"));
assert!(is_mkdocs_auto_reference("docs/reference/guide"));
assert!(is_mkdocs_auto_reference("api/module.Class"));
assert!(is_mkdocs_auto_reference("a/b"));
assert!(!is_mkdocs_auto_reference("/")); assert!(!is_mkdocs_auto_reference("//")); assert!(!is_mkdocs_auto_reference("a//b")); }
#[test]
fn test_length_limits() {
let long_input = "a".repeat(201);
assert!(!is_mkdocs_auto_reference(&long_input));
assert!(!is_mkdocs_auto_reference(""));
}
#[test]
fn test_edge_cases() {
assert!(!is_mkdocs_auto_reference("module.class-method"));
assert!(is_mkdocs_auto_reference("api/module.Class")); assert!(is_mkdocs_auto_reference("api/module.function"));
assert!(!is_mkdocs_auto_reference("module.class!")); assert!(!is_mkdocs_auto_reference("api/module?query")); assert!(!is_mkdocs_auto_reference("header#anchor"));
assert!(!is_mkdocs_auto_reference("module .class")); assert!(!is_mkdocs_auto_reference("header -anchor")); assert!(!is_mkdocs_auto_reference("api/ module")); }
}