use std::borrow::Cow;
use std::fmt;
use std::fmt::{Debug, Display, Formatter};
#[derive(PartialEq)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub enum CodePairValue {
Boolean(i16),
Integer(i32),
Long(i64),
Short(i16),
Double(f64),
Str(String),
Binary(Vec<u8>),
}
impl CodePairValue {
pub(crate) fn un_escape_string(val: &'_ str) -> Cow<'_, str> {
fn needs_un_escaping(c: char) -> bool {
c == '^'
}
if let Some(first) = val.find(needs_un_escaping) {
let mut result = String::from(&val[0..first]);
result.reserve(val.len() - first);
let rest = val[first..].chars();
let mut do_escape = false;
for c in rest {
match c {
'^' if !do_escape => do_escape = true,
_ => {
if do_escape {
do_escape = false;
let c = match c {
'@' => 0x00,
'A' => 0x01,
'B' => 0x02,
'C' => 0x03,
'D' => 0x04,
'E' => 0x05,
'F' => 0x06,
'G' => 0x07,
'H' => 0x08,
'I' => 0x09,
'J' => 0x0A,
'K' => 0x0B,
'L' => 0x0C,
'M' => 0x0D,
'N' => 0x0E,
'O' => 0x0F,
'P' => 0x10,
'Q' => 0x11,
'R' => 0x12,
'S' => 0x13,
'T' => 0x14,
'U' => 0x15,
'V' => 0x16,
'W' => 0x17,
'X' => 0x18,
'Y' => 0x19,
'Z' => 0x1A,
'[' => 0x1B,
'\\' => 0x1C,
']' => 0x1D,
'^' => 0x1E,
'_' => 0x1F,
' ' => b'^',
_ => c as u8, };
result.push(c as char);
} else {
result.push(c);
}
}
}
}
result.into()
} else {
val.into()
}
}
}
impl Clone for CodePairValue {
fn clone(&self) -> Self {
match self {
CodePairValue::Boolean(b) => CodePairValue::Boolean(*b),
CodePairValue::Integer(i) => CodePairValue::Integer(*i),
CodePairValue::Long(l) => CodePairValue::Long(*l),
CodePairValue::Short(s) => CodePairValue::Short(*s),
CodePairValue::Double(d) => CodePairValue::Double(*d),
CodePairValue::Str(ref s) => CodePairValue::Str(String::from(s.as_str())),
CodePairValue::Binary(ref b) => CodePairValue::Binary(b.clone()),
}
}
}
impl Debug for CodePairValue {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
CodePairValue::Boolean(s) => write!(f, "{}", s),
CodePairValue::Integer(i) => write!(f, "{: >9}", i),
CodePairValue::Long(l) => write!(f, "{}", l),
CodePairValue::Short(s) => write!(f, "{: >6}", s),
CodePairValue::Double(d) => write!(f, "{}", format_f64(*d)),
CodePairValue::Str(ref s) => write!(f, "{}", s),
CodePairValue::Binary(ref b) => {
let mut line = String::new();
for s in b {
line.push_str(&format!("{:02X}", s));
}
write!(f, "{}", line)
}
}
}
}
impl Display for CodePairValue {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self) }
}
pub(crate) fn escape_control_characters(val: &str) -> String {
fn needs_escaping(c: char) -> bool {
let c = c as u32;
c <= 0x1F || c == 0x5E
}
let mut result = String::from("");
for c in val.chars() {
if needs_escaping(c) {
result.push('^');
match c as u8 {
0x00 => result.push('@'),
0x01 => result.push('A'),
0x02 => result.push('B'),
0x03 => result.push('C'),
0x04 => result.push('D'),
0x05 => result.push('E'),
0x06 => result.push('F'),
0x07 => result.push('G'),
0x08 => result.push('H'),
0x09 => result.push('I'),
0x0A => result.push('J'),
0x0B => result.push('K'),
0x0C => result.push('L'),
0x0D => result.push('M'),
0x0E => result.push('N'),
0x0F => result.push('O'),
0x10 => result.push('P'),
0x11 => result.push('Q'),
0x12 => result.push('R'),
0x13 => result.push('S'),
0x14 => result.push('T'),
0x15 => result.push('U'),
0x16 => result.push('V'),
0x17 => result.push('W'),
0x18 => result.push('X'),
0x19 => result.push('Y'),
0x1A => result.push('Z'),
0x1B => result.push('['),
0x1C => result.push('\\'),
0x1D => result.push(']'),
0x1E => result.push('^'),
0x1F => result.push('_'),
0x5E => result.push(' '),
_ => panic!("this should never happen"),
}
} else {
result.push(c);
}
}
result
}
#[test]
fn test_escape_control_characters() {
assert_eq!("a^G^ ^^ b", escape_control_characters("a\u{7}^\u{1E} b"));
}
pub(crate) fn escape_unicode_to_ascii(val: &str) -> String {
let mut result = String::from("");
for c in val.chars() {
let b = c as u32;
if b >= 128 {
result.push_str(&format!("\\U+{:04X}", b));
} else {
result.push(c);
}
}
result
}
#[test]
fn test_unicode_escape_1() {
assert_eq!(
"Rep\\U+00E8re pi\\U+00E8ce",
escape_unicode_to_ascii("Repère pièce")
);
assert_eq!("\\U+4F60\\U+597D", escape_unicode_to_ascii("你好"));
}
#[test]
fn test_unicode_escape_2() {
assert_eq!(
"\\U+0410\\U+0430\\U+042F\\U+044F",
escape_unicode_to_ascii("АаЯя")
);
}
pub(crate) fn un_escape_ascii_to_unicode(val: &str) -> String {
let mut result = String::from("");
let mut seq = String::from("");
let mut in_escape_sequence = false;
let mut sequence_start = 0;
for (i, c) in val.chars().enumerate() {
if !in_escape_sequence {
if c == '\\' {
in_escape_sequence = true;
sequence_start = i;
seq.clear();
seq.push(c);
} else {
result.push(c);
}
} else {
seq.push(c);
if i == sequence_start + 6 {
in_escape_sequence = false;
if seq.starts_with("\\U+") {
let code_str = &seq[3..];
let decoded = match u32::from_str_radix(code_str, 16) {
Ok(code) => match std::char::from_u32(code) {
Some(c) => c,
None => '?',
},
Err(_) => '?',
};
result.push(decoded);
} else {
result.push_str(&seq);
}
seq.clear();
}
}
}
result
}
#[test]
fn test_ascii_unescape() {
assert_eq!(
"Repère pièce",
un_escape_ascii_to_unicode("Rep\\U+00E8re pi\\U+00E8ce")
);
assert_eq!("你好", un_escape_ascii_to_unicode("\\U+4F60\\U+597D"));
}
fn format_f64(val: f64) -> String {
let mut val = format!("{:.12}", val);
while val.ends_with('0') {
val.pop();
}
if val.ends_with('.') {
val.push('0');
}
val
}