/// Character classification functions based on RFC 3986 ABNF
///|
/// Check if character is an ASCII letter (A-Z, a-z)
fn is_alpha(c : Char) -> Bool {
(c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
}
///|
/// Check if character is an ASCII digit (0-9)
fn is_digit(c : Char) -> Bool {
c >= '0' && c <= '9'
}
///|
/// Check if character is a hexadecimal digit (0-9, A-F, a-f)
fn is_hexdig(c : Char) -> Bool {
is_digit(c) || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')
}
///|
/// Check if character is unreserved: ALPHA / DIGIT / "-" / "." / "_" / "~"
pub fn is_unreserved(c : Char) -> Bool {
is_alpha(c) || is_digit(c) || c == '-' || c == '.' || c == '_' || c == '~'
}
///|
/// Check if character is a general delimiter: ":" / "/" / "?" / "#" / "[" / "]" / "@"
pub fn is_gen_delim(c : Char) -> Bool {
match c {
':' | '/' | '?' | '#' | '[' | ']' | '@' => true
_ => false
}
}
///|
/// Check if character is a sub-delimiter: "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
pub fn is_sub_delim(c : Char) -> Bool {
match c {
'!' | '$' | '&' | '\'' | '(' | ')' | '*' | '+' | ',' | ';' | '=' => true
_ => false
}
}
///|
/// Check if character is reserved: gen-delims / sub-delims
pub fn is_reserved(c : Char) -> Bool {
is_gen_delim(c) || is_sub_delim(c)
}
///|
/// Check if character is valid for scheme: ALPHA / DIGIT / "+" / "-" / "."
pub fn is_scheme_char(c : Char) -> Bool {
is_alpha(c) || is_digit(c) || c == '+' || c == '-' || c == '.'
}
///|
/// Check if character is valid for userinfo: unreserved / pct-encoded / sub-delims / ":"
pub fn is_userinfo_char(c : Char) -> Bool {
is_unreserved(c) || is_sub_delim(c) || c == ':'
}
///|
/// Check if character is valid for reg-name: unreserved / pct-encoded / sub-delims
pub fn is_reg_name_char(c : Char) -> Bool {
is_unreserved(c) || is_sub_delim(c)
}
///|
/// Check if character is valid for pchar: unreserved / pct-encoded / sub-delims / ":" / "@"
pub fn is_pchar(c : Char) -> Bool {
is_unreserved(c) || is_sub_delim(c) || c == ':' || c == '@'
}
///|
/// Check if character is valid for query: pchar / "/" / "?"
pub fn is_query_char(c : Char) -> Bool {
is_pchar(c) || c == '/' || c == '?'
}
///|
/// Check if character is valid for fragment: pchar / "/" / "?"
pub fn is_fragment_char(c : Char) -> Bool {
is_pchar(c) || c == '/' || c == '?'
}
///|
/// Check if character is valid in segment-nz-nc: unreserved / pct-encoded / sub-delims / "@"
/// (non-zero-length segment without any colon ":")
pub fn is_segment_nz_nc_char(c : Char) -> Bool {
is_unreserved(c) || is_sub_delim(c) || c == '@'
}
///|
/// Convert hex character to its numeric value
pub fn hex_to_int(c : Char) -> Int? {
match c {
'0' => Some(0)
'1' => Some(1)
'2' => Some(2)
'3' => Some(3)
'4' => Some(4)
'5' => Some(5)
'6' => Some(6)
'7' => Some(7)
'8' => Some(8)
'9' => Some(9)
'A' | 'a' => Some(10)
'B' | 'b' => Some(11)
'C' | 'c' => Some(12)
'D' | 'd' => Some(13)
'E' | 'e' => Some(14)
'F' | 'f' => Some(15)
_ => None
}
}
///|
/// Convert integer to hex character (lowercase)
pub fn int_to_hex(n : Int) -> Char? {
if n >= 0 && n <= 15 {
match n {
0 => Some('0')
1 => Some('1')
2 => Some('2')
3 => Some('3')
4 => Some('4')
5 => Some('5')
6 => Some('6')
7 => Some('7')
8 => Some('8')
9 => Some('9')
10 => Some('a')
11 => Some('b')
12 => Some('c')
13 => Some('d')
14 => Some('e')
15 => Some('f')
_ => None
}
} else {
None
}
}