gambit_parser/
unescaped.rs1use std::fmt::{self, Display, Formatter};
3use std::iter::FusedIterator;
4use std::str::Chars;
5
6#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
12pub struct EscapedStr {
13 escaped: str,
14}
15
16impl EscapedStr {
17 pub(crate) fn new<S: AsRef<str> + ?Sized>(escaped: &S) -> &Self {
19 unsafe { &*(escaped.as_ref() as *const str as *const EscapedStr) }
20 }
21
22 pub fn as_raw_str(&self) -> &str {
24 &self.escaped
25 }
26
27 pub fn unescape(&self) -> Unescaped<'_> {
29 Unescaped {
30 chars: self.escaped.chars(),
31 }
32 }
33}
34
35impl Display for EscapedStr {
36 fn fmt(&self, out: &mut Formatter<'_>) -> Result<(), fmt::Error> {
37 write!(out, "{}", self.unescape())
38 }
39}
40
41#[derive(Debug, Clone)]
43pub struct Unescaped<'a> {
44 chars: Chars<'a>,
45}
46
47impl<'a> Display for Unescaped<'a> {
48 fn fmt(&self, out: &mut Formatter<'_>) -> Result<(), fmt::Error> {
49 for chr in self.clone() {
50 write!(out, "{}", chr)?;
51 }
52 Ok(())
53 }
54}
55
56impl<'a> Iterator for Unescaped<'a> {
57 type Item = char;
58
59 fn next(&mut self) -> Option<Self::Item> {
60 match self.chars.next() {
61 Some('\\') => Some(self.chars.next().unwrap()),
62 chr => chr,
63 }
64 }
65
66 fn size_hint(&self) -> (usize, Option<usize>) {
67 let (min, max) = self.chars.size_hint();
68 ((min + 1) / 2, max)
69 }
70}
71
72impl<'a> FusedIterator for Unescaped<'a> {}
73
74#[cfg(test)]
75mod tests {
76 use super::EscapedStr;
77
78 #[test]
79 fn test_formatting() {
80 let escaped = EscapedStr::new("air \\\" quote");
81 let res = escaped.to_string();
82 assert_eq!(res, "air \" quote");
83 }
84}