plugx_config/parser/
closure.rs

1//! Custom configuration parser with [Fn].
2//!
3//! ### Example
4//! In the following example we implement a complete [HJSON](https://hjson.github.io/) parser.
5//!
6//! ```rust
7//! use plugx_config::{
8//!     ext::{plugx_input::Input, anyhow::anyhow},
9//!     error::Error,
10//!     parser::{Parser, closure::Closure}
11//! };
12//!
13//! let parser_fn = |bytes: &[u8]| -> anyhow::Result<Input> {
14//!     deser_hjson::from_slice(bytes).map_err(|error| anyhow!(error))
15//! };
16//!
17//! let parser_name = "HJSON";
18//! let parser_format = "hjson";
19//! let parser = Closure::new("HJSNO", "hjson", Box::new(parser_fn));
20//! let bytes = br#"
21//! {
22//!     hello: ["w", "o", "l", "d"]
23//!     foo: {
24//!         bar: {
25//!             baz: Qux
26//!             abc: 3.14
27//!         }
28//!         xyz: false
29//!     }
30//! }
31//! "#;
32//! let parsed = parser.parse(bytes).unwrap();
33//! assert!(
34//!     parsed.as_map().len() == 2 &&
35//!     parsed.as_map().contains_key("foo") &&
36//!     parsed.as_map().contains_key("hello")
37//! );
38//! let foo = parsed.as_map().get("foo").unwrap();
39//! assert!(
40//!     foo.as_map().len() == 2 &&
41//!     foo.as_map().contains_key("bar") &&
42//!     foo.as_map().contains_key("xyz")
43//! );
44//! let bar = foo.as_map().get("bar").unwrap();
45//! assert_eq!(bar.as_map().get("baz").unwrap(), &"Qux".into());
46//! assert_eq!(bar.as_map().get("abc").unwrap(), &3.14.into());
47//! let xyz = foo.as_map().get("xyz").unwrap();
48//! assert_eq!(xyz, &false.into());
49//! let list = ["w", "o", "l", "d"].into();
50//! assert_eq!(parsed.as_map().get("hello").unwrap(), &list);
51//! ```
52//!
53
54use crate::parser::Parser;
55use plugx_input::Input;
56use std::fmt::{Debug, Display, Formatter};
57
58/// A `|&[u8]| -> Result<Input, ConfigurationParserError>` [Fn] to parse contents.
59pub type BoxedParserFn = Box<dyn Fn(&[u8]) -> anyhow::Result<Input> + Send + Sync>;
60/// A `|&[u8]| -> Option<bool>` [Fn] to validate contents.
61pub type BoxedValidatorFn = Box<dyn Fn(&[u8]) -> Option<bool> + Send + Sync>;
62
63/// Builder struct.
64pub struct Closure {
65    name: String,
66    parser: BoxedParserFn,
67    validator: BoxedValidatorFn,
68    supported_format_list: Vec<String>,
69}
70
71impl Display for Closure {
72    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
73        f.write_str(self.name.as_str())
74    }
75}
76
77impl Debug for Closure {
78    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
79        f.debug_struct("ConfigurationParserFn")
80            .field("name", &self.name)
81            .field("supported_format_list", &self.supported_format_list)
82            .finish()
83    }
84}
85
86impl Closure {
87    pub fn new<N: AsRef<str>, F: AsRef<str>>(
88        name: N,
89        supported_format: F,
90        parser: BoxedParserFn,
91    ) -> Self {
92        Self {
93            name: name.as_ref().to_string(),
94            parser,
95            validator: Box::new(|_| None),
96            supported_format_list: [supported_format.as_ref().to_string()].to_vec(),
97        }
98    }
99
100    pub fn set_parser(&mut self, parser: BoxedParserFn) {
101        self.parser = parser;
102    }
103
104    pub fn with_parser(mut self, parser: BoxedParserFn) -> Self {
105        self.set_parser(parser);
106        self
107    }
108
109    pub fn set_validator(&mut self, validator: BoxedValidatorFn) {
110        self.validator = validator;
111    }
112
113    pub fn with_validator(mut self, validator: BoxedValidatorFn) -> Self {
114        self.set_validator(validator);
115        self
116    }
117
118    pub fn set_format_list<N: AsRef<str>>(&mut self, format_list: &[N]) {
119        self.supported_format_list = format_list
120            .iter()
121            .map(|format| format.as_ref().to_string())
122            .collect();
123    }
124
125    pub fn with_format_list<N: AsRef<str>>(mut self, format_list: &[N]) -> Self {
126        self.set_format_list(format_list);
127        self
128    }
129}
130
131impl Parser for Closure {
132    fn supported_format_list(&self) -> Vec<String> {
133        self.supported_format_list.clone()
134    }
135
136    fn try_parse(&self, bytes: &[u8]) -> anyhow::Result<Input> {
137        (self.parser)(bytes)
138    }
139
140    fn is_format_supported(&self, bytes: &[u8]) -> Option<bool> {
141        (self.validator)(bytes)
142    }
143}