Move name escaping into its own module
This commit is contained in:
@@ -11,6 +11,9 @@ pub use self::contacts::*;
|
||||
mod contact_list;
|
||||
pub use self::contact_list::*;
|
||||
|
||||
mod escape;
|
||||
use self::escape::escape;
|
||||
|
||||
mod requests;
|
||||
pub use self::requests::*;
|
||||
|
||||
@@ -309,56 +312,3 @@ impl Connection {
|
||||
tree
|
||||
}
|
||||
}
|
||||
|
||||
fn escape_one(b: u8) -> String {
|
||||
format!("_{:0<2x}", b)
|
||||
}
|
||||
|
||||
// Some non-empty sequence of ASCII letters, digits and underscores
|
||||
fn escape(s: String) -> String {
|
||||
// Special-case the empty string
|
||||
if s.is_empty() {
|
||||
return "_".to_string();
|
||||
}
|
||||
|
||||
let bytes = s.into_bytes();
|
||||
let mut iter = bytes.iter();
|
||||
let mut out = String::new();
|
||||
|
||||
// Only alphanumeric in the first byte
|
||||
let x = *iter.next().expect("Already checked len > 0");
|
||||
let first = match x {
|
||||
b'a'..=b'z' | b'A'..=b'Z' => unsafe { String::from_utf8_unchecked(vec![x]) },
|
||||
_ => escape_one(x),
|
||||
};
|
||||
|
||||
out.push_str(&first);
|
||||
|
||||
for x in iter {
|
||||
let next = match x {
|
||||
b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' => unsafe {
|
||||
String::from_utf8_unchecked(vec![*x])
|
||||
},
|
||||
_ => escape_one(*x),
|
||||
};
|
||||
|
||||
out.push_str(&next);
|
||||
}
|
||||
|
||||
out
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_escape() {
|
||||
assert_eq!(escape("".to_string()), "_");
|
||||
assert_eq!(escape("foo".to_string()), "foo");
|
||||
assert_eq!(escape("foo@bar".to_string()), "foo_40bar");
|
||||
assert_eq!(escape("foo_bar".to_string()), "foo_5fbar");
|
||||
assert_eq!(escape("foo__@__bar".to_string()), "foo_5f_5f_40_5f_5fbar");
|
||||
assert_eq!(escape("1foo".to_string()), "_31foo");
|
||||
}
|
||||
}
|
||||
|
52
src/padfoot/connection/escape.rs
Normal file
52
src/padfoot/connection/escape.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
fn escape_one(b: u8) -> String {
|
||||
format!("_{:0<2x}", b)
|
||||
}
|
||||
|
||||
// Some non-empty sequence of ASCII letters, digits and underscores
|
||||
pub fn escape(s: String) -> String {
|
||||
// Special-case the empty string
|
||||
if s.is_empty() {
|
||||
return "_".to_string();
|
||||
}
|
||||
|
||||
let bytes = s.into_bytes();
|
||||
let mut iter = bytes.iter();
|
||||
let mut out = String::new();
|
||||
|
||||
// Only alphanumeric in the first byte
|
||||
let x = *iter.next().expect("Already checked len > 0");
|
||||
let first = match x {
|
||||
b'a'..=b'z' | b'A'..=b'Z' => unsafe { String::from_utf8_unchecked(vec![x]) },
|
||||
_ => escape_one(x),
|
||||
};
|
||||
|
||||
out.push_str(&first);
|
||||
|
||||
for x in iter {
|
||||
let next = match x {
|
||||
b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' => unsafe {
|
||||
String::from_utf8_unchecked(vec![*x])
|
||||
},
|
||||
_ => escape_one(*x),
|
||||
};
|
||||
|
||||
out.push_str(&next);
|
||||
}
|
||||
|
||||
out
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_escape() {
|
||||
assert_eq!(escape("".to_string()), "_");
|
||||
assert_eq!(escape("foo".to_string()), "foo");
|
||||
assert_eq!(escape("foo@bar".to_string()), "foo_40bar");
|
||||
assert_eq!(escape("foo_bar".to_string()), "foo_5fbar");
|
||||
assert_eq!(escape("foo__@__bar".to_string()), "foo_5f_5f_40_5f_5fbar");
|
||||
assert_eq!(escape("1foo".to_string()), "_31foo");
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user