hemtt_config/model/
str.rs1use hemtt_tokens::{Symbol, Token};
2use peekmore::PeekMoreIterator;
3
4use crate::{
5 error::Error,
6 rapify::{Rapify, WriteExt},
7 Options,
8};
9
10use super::Parse;
11
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct Str(pub String);
15
16impl Parse for Str {
17 fn parse(
18 _options: &Options,
19 tokens: &mut PeekMoreIterator<impl Iterator<Item = Token>>,
20 from: &Token,
21 ) -> Result<Self, Error>
22 where
23 Self: Sized,
24 {
25 if let Some(token) = tokens.next() {
26 if token.symbol() != &Symbol::DoubleQuote {
27 return Err(Error::UnexpectedToken {
28 token: Box::new(token),
29 expected: vec![Symbol::DoubleQuote],
30 });
31 }
32 } else {
33 return Err(Error::UnexpectedEOF {
34 token: Box::new(from.clone()),
35 });
36 }
37 let mut string = String::new();
38 'outer: loop {
39 if let Some(token) = tokens.peek() {
40 match token.symbol() {
41 Symbol::DoubleQuote => 'inner: loop {
42 tokens.next();
43 if let Some(token) = tokens.peek() {
44 match token.symbol() {
45 Symbol::DoubleQuote => {
46 tokens.next();
47 string.push('"');
48 break 'inner;
49 }
50 Symbol::Whitespace(_) => continue,
51 Symbol::Escape => {
52 if tokens.peek_nth(1).unwrap().symbol()
53 == &Symbol::Word(String::from("n"))
54 {
55 tokens.next();
56 tokens.next();
57 string.push('\n');
58 loop {
59 if let Some(token) = tokens.peek() {
60 match token.symbol() {
61 Symbol::Whitespace(_) => {
62 tokens.next();
63 continue;
64 }
65 Symbol::DoubleQuote => {
66 tokens.next();
67 break 'inner;
68 }
69 _ => break 'outer,
70 }
71 }
72 return Err(Error::UnexpectedEOF {
73 token: Box::new(from.clone()),
74 });
75 }
76 }
77 break;
78 }
79 _ => break 'outer,
80 }
81 }
82 break 'outer;
83 },
84 _ => {
85 string.push_str(&tokens.next().unwrap().to_string());
86 }
87 }
88 } else {
89 return Err(Error::UnexpectedEOF {
90 token: Box::new(from.clone()),
91 });
92 }
93 }
94 Ok(Self(string))
95 }
96}
97
98impl Rapify for Str {
99 fn rapify<O: std::io::Write>(
100 &self,
101 output: &mut O,
102 _offset: usize,
103 ) -> Result<usize, std::io::Error> {
104 output.write_cstring(&self.0)?;
105 Ok(self.0.len() + 1)
106 }
107
108 fn rapified_length(&self) -> usize {
109 self.0.len() + 1
110 }
111
112 fn rapified_code(&self) -> Option<u8> {
113 Some(0)
114 }
115}
116
117#[cfg(test)]
118mod tests {
119 use hemtt_tokens::Token;
120 use peekmore::PeekMore;
121
122 use crate::model::Parse;
123
124 #[test]
125 fn string() {
126 let mut tokens = hemtt_preprocessor::preprocess_string(r#""test""#)
127 .unwrap()
128 .into_iter()
129 .peekmore();
130 let string = super::Str::parse(
131 &crate::Options::default(),
132 &mut tokens,
133 &Token::builtin(None),
134 )
135 .unwrap();
136 assert_eq!(string, super::Str("test".to_string()));
137 }
138
139 #[test]
140 fn string_escape() {
141 let mut tokens = hemtt_preprocessor::preprocess_string(r#""test is ""cool""""#)
142 .unwrap()
143 .into_iter()
144 .peekmore();
145 let string = super::Str::parse(
146 &crate::Options::default(),
147 &mut tokens,
148 &Token::builtin(None),
149 )
150 .unwrap();
151 assert_eq!(string, super::Str(r#"test is "cool""#.to_string()));
152 }
153
154 #[test]
155 fn multiline_string() {
157 let mut tokens = hemtt_preprocessor::preprocess_string(r#""test" \n "is" \n "cool""#)
158 .unwrap()
159 .into_iter()
160 .peekmore();
161 let string = super::Str::parse(
162 &crate::Options::default(),
163 &mut tokens,
164 &Token::builtin(None),
165 )
166 .unwrap();
167 assert_eq!(string, super::Str("test\nis\ncool".to_string()));
168 }
169}