// Package security: Identifier security profiles (UTS #39 Section 3)
///|
pub(all) struct IdentifierProfile {
allow_mixed_script : Bool
allow_invisible : Bool
max_length : Int32
} derive(Eq, Debug)
///|
pub fn IdentifierProfile::default() -> IdentifierProfile {
{ allow_mixed_script: false, allow_invisible: false, max_length: 128, }
}
///|
pub fn IdentifierProfile::permissive() -> IdentifierProfile {
{ allow_mixed_script: true, allow_invisible: false, max_length: 256, }
}
///|
pub fn validate_identifier(
input : String,
profile~ : IdentifierProfile = IdentifierProfile::default(),
) -> Result[Unit, String] {
if input.length() == 0 {
return Err("Identifier cannot be empty")
}
let mut length = 0
for ch in input {
length += 1
if length > profile.max_length {
return Err("Identifier length exceeds maximum allowed length")
}
let cp = ch.to_int()
if cp < 0x20 || (cp >= 0x7F && cp <= 0x9F) {
return Err("Identifier contains disallowed control character")
}
if !profile.allow_invisible {
if @unicode.is_default_ignorable(cp) {
return Err("Identifier contains disallowed invisible format character")
}
if @unicode.is_bidi_control_codepoint(cp) {
return Err(
"Identifier contains disallowed bidirectional control character",
)
}
}
}
if !profile.allow_mixed_script {
let script_res = @script.analyze_scripts(input)
if script_res.is_mixed {
return Err("Identifier contains disallowed mixed scripts")
}
}
Ok(())
}
///|
pub fn is_allowed_identifier(
input : String,
profile~ : IdentifierProfile = IdentifierProfile::default(),
) -> Bool {
match validate_identifier(input, profile~) {
Ok(_) => true
Err(_) => false
}
}