intuicio_parser/
map.rs

1use crate::{ParseResult, Parser, ParserExt, ParserHandle, ParserOutput, ParserRegistry};
2use std::{
3    error::Error,
4    sync::{Arc, RwLock},
5};
6
7pub mod shorthand {
8    use super::*;
9
10    pub fn map<I: 'static, O: 'static>(
11        parser: ParserHandle,
12        f: impl FnMut(I) -> O + Send + Sync + 'static,
13    ) -> ParserHandle {
14        MapParser::new(parser, f).into_handle()
15    }
16
17    pub fn omap(
18        parser: ParserHandle,
19        f: impl FnMut(ParserOutput) -> ParserOutput + Send + Sync + 'static,
20    ) -> ParserHandle {
21        OutputMapParser::new(parser, f).into_handle()
22    }
23
24    pub fn map_err(
25        parser: ParserHandle,
26        f: impl FnMut(Box<dyn Error>) -> Box<dyn Error> + Send + Sync + 'static,
27    ) -> ParserHandle {
28        MapErrorParser::new(parser, f).into_handle()
29    }
30}
31
32pub struct MapParser<I, O> {
33    parser: ParserHandle,
34    closure: Arc<RwLock<dyn FnMut(I) -> O + Send + Sync>>,
35}
36
37impl<I, O> MapParser<I, O> {
38    pub fn new(parser: ParserHandle, f: impl FnMut(I) -> O + Send + Sync + 'static) -> Self {
39        Self {
40            parser,
41            closure: Arc::new(RwLock::new(f)),
42        }
43    }
44}
45
46impl<I: 'static, O: 'static> Parser for MapParser<I, O> {
47    fn parse<'a>(&self, registry: &ParserRegistry, input: &'a str) -> ParseResult<'a> {
48        let (input, result) = self.parser.parse(registry, input)?;
49        let result = match self.closure.write() {
50            Ok(mut closure) => match result.consume::<I>() {
51                Ok(result) => (closure)(result),
52                Err(_) => {
53                    return Err(format!(
54                        "MapParser cannot downcast input from `{}` type",
55                        std::any::type_name::<I>()
56                    )
57                    .into())
58                }
59            },
60            Err(_) => return Err("MapParser cannot access closure mutably".into()),
61        };
62        Ok((input, ParserOutput::new(result).ok().unwrap()))
63    }
64}
65
66pub struct OutputMapParser {
67    parser: ParserHandle,
68    closure: Arc<RwLock<dyn FnMut(ParserOutput) -> ParserOutput + Send + Sync>>,
69}
70
71impl OutputMapParser {
72    pub fn new(
73        parser: ParserHandle,
74        f: impl FnMut(ParserOutput) -> ParserOutput + Send + Sync + 'static,
75    ) -> Self {
76        Self {
77            parser,
78            closure: Arc::new(RwLock::new(f)),
79        }
80    }
81}
82
83impl Parser for OutputMapParser {
84    fn parse<'a>(&self, registry: &ParserRegistry, input: &'a str) -> ParseResult<'a> {
85        let (input, result) = self.parser.parse(registry, input)?;
86        let result = match self.closure.write() {
87            Ok(mut closure) => (closure)(result),
88            Err(_) => return Err("OutputMapParser cannot access closure mutably".into()),
89        };
90        Ok((input, result))
91    }
92}
93
94pub struct MapErrorParser {
95    parser: ParserHandle,
96    #[allow(clippy::type_complexity)]
97    closure: Arc<RwLock<dyn FnMut(Box<dyn Error>) -> Box<dyn Error> + Send + Sync>>,
98}
99
100impl MapErrorParser {
101    pub fn new(
102        parser: ParserHandle,
103        f: impl FnMut(Box<dyn Error>) -> Box<dyn Error> + Send + Sync + 'static,
104    ) -> Self {
105        Self {
106            parser,
107            closure: Arc::new(RwLock::new(f)),
108        }
109    }
110}
111
112impl Parser for MapErrorParser {
113    fn parse<'a>(&self, registry: &ParserRegistry, input: &'a str) -> ParseResult<'a> {
114        match self.parser.parse(registry, input) {
115            Ok(result) => Ok(result),
116            Err(error) => match self.closure.write() {
117                Ok(mut closure) => Err((closure)(error)),
118                Err(_) => Err("MapErrorParser cannot access closure mutably".into()),
119            },
120        }
121    }
122
123    fn extend(&self, parser: ParserHandle) {
124        self.parser.extend(parser);
125    }
126}
127
128#[cfg(test)]
129mod tests {
130    use crate::{
131        map::{MapErrorParser, MapParser, OutputMapParser},
132        shorthand::{map, map_err, number_float, omap},
133        ParserOutput, ParserRegistry,
134    };
135
136    fn is_async<T: Send + Sync>() {}
137
138    #[test]
139    fn test_map() {
140        is_async::<MapParser<(), ()>>();
141
142        let registry = ParserRegistry::default();
143        let number = map(number_float(), |value: String| {
144            value.parse::<f32>().unwrap()
145        });
146        assert_eq!(
147            number
148                .parse(&registry, "-4.2e1")
149                .unwrap()
150                .1
151                .consume::<f32>()
152                .ok()
153                .unwrap(),
154            -42.0
155        );
156    }
157
158    #[test]
159    fn test_omap() {
160        is_async::<OutputMapParser>();
161
162        let registry = ParserRegistry::default();
163        let number = omap(number_float(), |value| {
164            ParserOutput::new(
165                value
166                    .consume::<String>()
167                    .ok()
168                    .unwrap()
169                    .parse::<f32>()
170                    .unwrap(),
171            )
172            .ok()
173            .unwrap()
174        });
175        assert_eq!(
176            number
177                .parse(&registry, "-4.2e1")
178                .unwrap()
179                .1
180                .consume::<f32>()
181                .ok()
182                .unwrap(),
183            -42.0
184        );
185    }
186
187    #[test]
188    fn test_map_error() {
189        is_async::<MapErrorParser>();
190
191        let registry = ParserRegistry::default();
192        let number = map_err(number_float(), |_| "Expected float number".into());
193        assert_eq!(
194            format!("{}", number.parse(&registry, "foo").err().unwrap()),
195            "Expected float number"
196        );
197    }
198}