parent
ce1c198552
commit
f78121f9d3
@ -1,156 +0,0 @@ |
||||
use crate::utils::StaticSliceMap; |
||||
use std::borrow::Cow; |
||||
use std::char; |
||||
use std::str::Chars; |
||||
|
||||
pub fn unescape_unicode_codepoints(input: &str) -> Cow<'_, str> { |
||||
if needs_unescape_unicode_codepoints(input) { |
||||
UnescapeUnicodeCharIterator::new(input).collect() |
||||
} else { |
||||
input.into() |
||||
} |
||||
} |
||||
|
||||
fn needs_unescape_unicode_codepoints(input: &str) -> bool { |
||||
let bytes = input.as_bytes(); |
||||
for i in 1..bytes.len() { |
||||
if (bytes[i] == b'u' || bytes[i] == b'U') && bytes[i - 1] == b'\\' { |
||||
return true; |
||||
} |
||||
} |
||||
false |
||||
} |
||||
|
||||
struct UnescapeUnicodeCharIterator<'a> { |
||||
iter: Chars<'a>, |
||||
buffer: String, |
||||
} |
||||
|
||||
impl<'a> UnescapeUnicodeCharIterator<'a> { |
||||
fn new(string: &'a str) -> Self { |
||||
Self { |
||||
iter: string.chars(), |
||||
buffer: String::with_capacity(9), |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl<'a> Iterator for UnescapeUnicodeCharIterator<'a> { |
||||
type Item = char; |
||||
|
||||
fn next(&mut self) -> Option<char> { |
||||
if !self.buffer.is_empty() { |
||||
return Some(self.buffer.remove(0)); |
||||
} |
||||
match self.iter.next()? { |
||||
'\\' => match self.iter.next() { |
||||
Some('u') => { |
||||
self.buffer.push('u'); |
||||
for _ in 0..4 { |
||||
if let Some(c) = self.iter.next() { |
||||
self.buffer.push(c); |
||||
} else { |
||||
return Some('\\'); |
||||
} |
||||
} |
||||
if let Some(c) = u32::from_str_radix(&self.buffer[1..5], 16) |
||||
.ok() |
||||
.and_then(char::from_u32) |
||||
{ |
||||
self.buffer.clear(); |
||||
Some(c) |
||||
} else { |
||||
Some('\\') |
||||
} |
||||
} |
||||
Some('U') => { |
||||
self.buffer.push('U'); |
||||
for _ in 0..8 { |
||||
if let Some(c) = self.iter.next() { |
||||
self.buffer.push(c); |
||||
} else { |
||||
return Some('\\'); |
||||
} |
||||
} |
||||
if let Some(c) = u32::from_str_radix(&self.buffer[1..9], 16) |
||||
.ok() |
||||
.and_then(char::from_u32) |
||||
{ |
||||
self.buffer.clear(); |
||||
Some(c) |
||||
} else { |
||||
Some('\\') |
||||
} |
||||
} |
||||
Some(c) => { |
||||
self.buffer.push(c); |
||||
Some('\\') |
||||
} |
||||
None => Some('\\'), |
||||
}, |
||||
c => Some(c), |
||||
} |
||||
} |
||||
} |
||||
|
||||
pub fn unescape_characters<'a>( |
||||
input: &'a str, |
||||
characters: &'static [u8], |
||||
replacement: &'static StaticSliceMap<char, char>, |
||||
) -> Cow<'a, str> { |
||||
if needs_unescape_characters(input, characters) { |
||||
UnescapeCharsIterator::new(input, replacement).collect() |
||||
} else { |
||||
input.into() |
||||
} |
||||
} |
||||
|
||||
fn needs_unescape_characters(input: &str, characters: &[u8]) -> bool { |
||||
let bytes = input.as_bytes(); |
||||
for i in 1..bytes.len() { |
||||
if bytes[i - 1] == b'\\' && characters.contains(&bytes[i]) { |
||||
return true; |
||||
} |
||||
} |
||||
false |
||||
} |
||||
|
||||
struct UnescapeCharsIterator<'a> { |
||||
iter: Chars<'a>, |
||||
buffer: Option<char>, |
||||
replacement: &'static StaticSliceMap<char, char>, |
||||
} |
||||
|
||||
impl<'a> UnescapeCharsIterator<'a> { |
||||
fn new(string: &'a str, replacement: &'static StaticSliceMap<char, char>) -> Self { |
||||
Self { |
||||
iter: string.chars(), |
||||
buffer: None, |
||||
replacement, |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl<'a> Iterator for UnescapeCharsIterator<'a> { |
||||
type Item = char; |
||||
|
||||
fn next(&mut self) -> Option<char> { |
||||
if let Some(ch) = self.buffer { |
||||
self.buffer = None; |
||||
return Some(ch); |
||||
} |
||||
match self.iter.next()? { |
||||
'\\' => match self.iter.next() { |
||||
Some(ch) => match self.replacement.get(ch) { |
||||
Some(replace) => Some(replace), |
||||
None => { |
||||
self.buffer = Some(ch); |
||||
Some('\\') |
||||
} |
||||
}, |
||||
None => Some('\\'), |
||||
}, |
||||
c => Some(c), |
||||
} |
||||
} |
||||
} |
@ -1,129 +0,0 @@ |
||||
use failure::Backtrace; |
||||
use failure::Fail; |
||||
use std::sync::PoisonError; |
||||
|
||||
pub trait Escaper { |
||||
fn escape(&self) -> String; |
||||
} |
||||
|
||||
impl Escaper for str { |
||||
fn escape(&self) -> String { |
||||
self.chars().flat_map(EscapeRDF::new).collect() |
||||
} |
||||
} |
||||
|
||||
/// Customized version of EscapeDefault of the Rust standard library
|
||||
struct EscapeRDF { |
||||
state: EscapeRdfState, |
||||
} |
||||
|
||||
enum EscapeRdfState { |
||||
Done, |
||||
Char(char), |
||||
Backslash(char), |
||||
} |
||||
|
||||
impl EscapeRDF { |
||||
fn new(c: char) -> Self { |
||||
Self { |
||||
state: match c { |
||||
'\t' => EscapeRdfState::Backslash('t'), |
||||
'\u{08}' => EscapeRdfState::Backslash('b'), |
||||
'\n' => EscapeRdfState::Backslash('n'), |
||||
'\r' => EscapeRdfState::Backslash('r'), |
||||
'\u{0C}' => EscapeRdfState::Backslash('f'), |
||||
'\\' | '\'' | '"' => EscapeRdfState::Backslash(c), |
||||
c => EscapeRdfState::Char(c), |
||||
}, |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl Iterator for EscapeRDF { |
||||
type Item = char; |
||||
|
||||
fn next(&mut self) -> Option<char> { |
||||
match self.state { |
||||
EscapeRdfState::Backslash(c) => { |
||||
self.state = EscapeRdfState::Char(c); |
||||
Some('\\') |
||||
} |
||||
EscapeRdfState::Char(c) => { |
||||
self.state = EscapeRdfState::Done; |
||||
Some(c) |
||||
} |
||||
EscapeRdfState::Done => None, |
||||
} |
||||
} |
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) { |
||||
let n = self.len(); |
||||
(n, Some(n)) |
||||
} |
||||
|
||||
fn count(self) -> usize { |
||||
self.len() |
||||
} |
||||
} |
||||
|
||||
impl ExactSizeIterator for EscapeRDF { |
||||
fn len(&self) -> usize { |
||||
match self.state { |
||||
EscapeRdfState::Done => 0, |
||||
EscapeRdfState::Char(_) => 1, |
||||
EscapeRdfState::Backslash(_) => 2, |
||||
} |
||||
} |
||||
} |
||||
|
||||
#[test] |
||||
fn test_escaper() { |
||||
assert_eq!("foo", "foo".escape()); |
||||
assert_eq!( |
||||
"John said: \\\"Hello World!\\\"", |
||||
"John said: \"Hello World!\"".escape() |
||||
); |
||||
assert_eq!( |
||||
"John said: \\\"Hello World!\\\\\\\"", |
||||
"John said: \"Hello World!\\\"".escape() |
||||
); |
||||
} |
||||
|
||||
pub struct StaticSliceMap<K: 'static + Copy + Eq, V: 'static + Copy> { |
||||
keys: &'static [K], |
||||
values: &'static [V], |
||||
} |
||||
|
||||
impl<K: 'static + Copy + Eq, V: 'static + Copy> StaticSliceMap<K, V> { |
||||
pub fn new(keys: &'static [K], values: &'static [V]) -> Self { |
||||
assert_eq!( |
||||
keys.len(), |
||||
values.len(), |
||||
"keys and values slices of StaticSliceMap should have the same size" |
||||
); |
||||
Self { keys, values } |
||||
} |
||||
|
||||
pub fn get(&self, key: K) -> Option<V> { |
||||
for i in 0..self.keys.len() { |
||||
if self.keys[i] == key { |
||||
return Some(self.values[i]); |
||||
} |
||||
} |
||||
None |
||||
} |
||||
} |
||||
|
||||
#[derive(Debug, Fail)] |
||||
#[fail(display = "Mutex Mutex was poisoned")] |
||||
pub struct MutexPoisonError { |
||||
backtrace: Backtrace, |
||||
} |
||||
|
||||
impl<T> From<PoisonError<T>> for MutexPoisonError { |
||||
fn from(_: PoisonError<T>) -> Self { |
||||
Self { |
||||
backtrace: Backtrace::new(), |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue