1use super::{
2 nom::{self, Finish},
3 EKind, InputError, InputType, NomError, Parser, Weight,
4};
5
6use std::fmt;
7
8pub type TextParser = for<'a, 'b> fn(&'a str, &'b str) -> nom::IResult<&'a str, String>;
9
10#[derive(Clone, Default)]
14pub struct Text {
15 marker: Option<String>,
16 parser: Option<TextParser>,
17 weight: Option<u8>,
18}
19
20impl Text {
21 pub const DEFAULT_WEIGHT: u8 = 255;
24 pub const DEFAULT_MARKER: &'static str = "";
26 pub const DEFAULT_PARSER: TextParser = default_text_parser;
28
29 pub fn new() -> Self {
31 Self::default()
32 }
33
34 pub fn with<F>(self, f: F) -> Self
46 where
47 F: FnMut(&mut Self) -> &mut Self,
48 {
49 let mut this = self;
50 let mut actions = f;
51
52 actions(&mut this);
53
54 this
55 }
56
57 pub fn marker(&mut self, marker: impl AsRef<str>) -> &mut Self {
61 self.marker = Some(marker.as_ref().to_string());
62
63 self
64 }
65
66 pub fn parser(&mut self, parser: TextParser) -> &mut Self {
74 self.parser = Some(parser);
75
76 self
77 }
78
79 pub fn weight(&mut self, weight: u8) -> &mut Self {
81 self.weight = Some(weight);
82
83 self
84 }
85
86 fn get_marker(&self) -> &str {
87 self.marker.as_deref().unwrap_or(Self::DEFAULT_MARKER)
88 }
89
90 fn get_weight(&self) -> u8 {
91 self.weight.unwrap_or(Self::DEFAULT_WEIGHT)
92 }
93
94 fn parse<'a>(&self, input: &'a str) -> Result<String, NomError<&'a str>> {
95 let marker = self.get_marker();
96
97 let (_, text) = self
98 .parser
99 .map(|p| p(input, marker))
100 .unwrap_or_else(|| Self::DEFAULT_PARSER(input, marker))
101 .finish()?;
102
103 Ok(text)
104 }
105
106 fn new_error(&self, _p_error: NomError<&str>) -> InputError {
107 InputError::new(EKind::TEXT)
108 }
109}
110
111impl Parser for Text {
112 fn parse_str(&self, s: &str) -> Result<InputType, InputError> {
113 self.parse(s)
114 .map(InputType::UTF8)
115 .map_err(|e| self.new_error(e))
116 }
117}
118
119impl Weight for Text {
120 fn weight(&self) -> u8 {
121 self.get_weight()
122 }
123}
124
125impl fmt::Debug for Text {
126 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127 f.debug_struct("Text")
128 .field("marker", &self.get_marker())
129 .field(
130 "parser",
131 &self
132 .parser
133 .map_or("Default TextParser", |_| "Custom TextParser"),
134 )
135 .finish()
136 }
137}
138
139pub fn default_text_parser<'a, 'b>(
143 input: &'a str,
144 marker: &'b str,
145) -> nom::IResult<&'a str, String> {
146 if marker.is_empty() {
148 Ok(("", input.to_string()))
149 } else {
150 nom::context("TEXT", nom::tag(marker))(input).map(|(path, _)| ("", String::from(path)))
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157
158 const INPUT: &'static str = "some arbitrary text";
159
160 #[test]
161 fn defaults_success() {
162 let input = INPUT;
163 let output = String::from(input);
164
165 let parser = Text::new();
166
167 let result = parser.parse_str(input);
168
169 assert_eq!(result, Ok(InputType::UTF8(output)))
170 }
171
172 #[test]
173 fn c_marker_success() {
174 let mkr = "!!";
175
176 let input = "!!valid text";
177 let output = String::from("valid text");
178
179 let parser = Text::new().with(|this| this.marker(mkr));
180
181 let result = parser.parse_str(input);
182
183 assert_eq!(result, Ok(InputType::UTF8(output)))
184 }
185
186 #[test]
187 fn c_marker_failure() {
188 let mkr = "!!";
189
190 let input = "no marker";
191
192 let parser = Text::new().with(|this| this.marker(mkr));
193
194 let result = parser.parse_str(input);
195
196 assert_eq!(result, Err(EKind::TEXT.into()))
197 }
198
199 #[test]
200 fn c_parser_success() {
201 let input = INPUT;
202 let output = String::from(input);
203
204 let parser = Text::new().with(|this| this.parser(test_custom_parser));
205
206 let result = parser.parse_str(input);
207
208 assert_eq!(result, Ok(InputType::UTF8(output)))
209 }
210
211 #[test]
212 fn c_parser_failure() {
213 let input = "";
214
215 let parser = Text::new().with(|this| this.parser(test_custom_parser));
216
217 let result = parser.parse_str(input);
218
219 assert_eq!(result, Err(EKind::TEXT.into()))
220 }
221
222 fn test_custom_parser<'a, 'b>(
223 input: &'a str,
224 marker: &'b str,
225 ) -> nom::IResult<&'a str, String> {
226 use ::nom::error::{make_error, ErrorKind};
227 if input.is_empty() {
228 Err(::nom::Err::Error(make_error(input, ErrorKind::NonEmpty)))
229 } else {
230 nom::context("TEXT", nom::tag(marker))(input).map(|(path, _)| ("", String::from(path)))
231 }
232 }
233}