1mod array;
2mod comment;
3mod include;
4pub(crate) mod loader;
5mod object;
6pub mod read;
7mod string;
8mod substitution;
9
10use std::rc::Rc;
11
12use derive_more::Constructor;
13
14use crate::Result;
15use crate::config_options::ConfigOptions;
16use crate::error::Error;
17use crate::parser::read::Read;
18use crate::raw::raw_object::RawObject;
19
20#[derive(Constructor, Default, Debug, Clone)]
21pub(crate) struct Context {
22 pub(crate) include_chain: Vec<Rc<String>>,
23 pub(crate) depth: usize,
24}
25
26impl Context {
27 pub(crate) fn increase_depth(&mut self) -> usize {
28 self.depth += 1;
29 self.depth
30 }
31
32 pub(crate) fn decrease_depth(&mut self) -> usize {
33 self.depth -= 1;
34 self.depth
35 }
36}
37
38#[derive(Debug)]
39pub struct HoconParser<R> {
40 pub(crate) reader: R,
41 pub(crate) scratch: Vec<u8>,
42 pub(crate) options: ConfigOptions,
43 pub(crate) ctx: Context,
44}
45
46impl<'de, R: Read<'de>> HoconParser<R> {
47 pub fn new(reader: R) -> Self {
48 HoconParser {
49 reader,
50 scratch: vec![],
51 options: Default::default(),
52 ctx: Default::default(),
53 }
54 }
55
56 pub fn with_options(reader: R, options: ConfigOptions) -> Self {
57 HoconParser {
58 reader,
59 scratch: vec![],
60 options,
61 ctx: Default::default(),
62 }
63 }
64
65 pub(crate) fn with_options_and_ctx(reader: R, options: ConfigOptions, ctx: Context) -> Self {
66 HoconParser {
67 reader,
68 scratch: vec![],
69 options,
70 ctx,
71 }
72 }
73
74 pub(crate) fn parse_horizontal_whitespace(&mut self, scratch: &mut Vec<u8>) -> Result<()> {
75 loop {
76 match self.reader.peek_horizontal_whitespace() {
77 Ok(Some(n)) => {
78 for _ in 0..n {
79 let byte = self.reader.next()?;
80 scratch.push(byte);
81 }
82 }
83 Ok(None) | Err(Error::Eof) => break,
84 Err(err) => return Err(err),
85 }
86 }
87 Ok(())
88 }
89
90 pub(crate) fn drop_horizontal_whitespace(&mut self) -> Result<()> {
91 loop {
92 match self.reader.peek_horizontal_whitespace() {
93 Ok(Some(n)) => {
94 self.reader.discard(n)?;
95 }
96 Ok(None) | Err(Error::Eof) => break,
97 Err(err) => return Err(err),
98 }
99 }
100 Ok(())
101 }
102
103 pub(crate) fn drop_whitespace(&mut self) -> Result<()> {
104 loop {
105 match self.reader.peek_whitespace() {
106 Ok(Some(n)) => {
107 self.reader.discard(n)?;
108 }
109 Ok(None) | Err(Error::Eof) => break,
110 Err(err) => return Err(err),
111 }
112 }
113 Ok(())
114 }
115
116 pub(crate) fn drop_comma_separator(&mut self) -> Result<bool> {
117 match self.reader.peek() {
118 Ok(ch) => {
119 if ch == b',' {
120 self.reader.discard(1)?;
121 }
122 }
123 Err(Error::Eof) => return Ok(true),
124 Err(err) => {
125 return Err(err);
126 }
127 }
128 Ok(false)
129 }
130
131 pub fn parse(&mut self) -> Result<RawObject> {
132 self.drop_whitespace_and_comments()?;
133 let raw_obj = match self.reader.peek() {
134 Ok(ch) => {
135 if ch == b'{' {
136 self.parse_object(false)?
137 } else {
138 self.parse_braces_omitted_object()?
139 }
140 }
141 Err(Error::Eof) => {
142 return Ok(RawObject::default());
143 }
144 Err(err) => {
145 return Err(err);
146 }
147 };
148 self.drop_whitespace_and_comments()?;
149 match self.reader.peek() {
150 Ok(ch) => {
151 return Err(Error::UnexpectedToken {
152 expected: "end of file",
153 found_beginning: ch,
154 });
155 }
156 Err(Error::Eof) => {}
157 Err(err) => {
158 return Err(err);
159 }
160 }
161 Ok(raw_obj)
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use std::io::BufReader;
168
169 use crate::Result;
170 use crate::config_options::ConfigOptions;
171 use crate::parser::HoconParser;
172 use crate::parser::read::StreamRead;
173 use rstest::rstest;
174
175 #[rstest]
176 #[case("resources/base.conf")]
177 #[case("resources/concat.conf")]
178 #[case("resources/concat2.conf")]
179 #[case("resources/concat3.conf")]
180 #[case("resources/demo.conf")]
181 #[case("resources/deserialize.conf")]
182 #[case("resources/empty.conf")]
183 #[cfg_attr(feature = "urls_includes", case("resources/included.conf"))]
184 #[cfg_attr(feature = "urls_includes", case("resources/main.conf"))]
185 fn test_parse(#[case] path: impl AsRef<std::path::Path>) -> Result<()> {
186 let file = std::fs::File::open(&path)?;
187 let read = StreamRead::new(BufReader::new(file));
188 let options = ConfigOptions::new(false, vec!["resources".to_string()]);
189 let mut parser = HoconParser::with_options(read, options);
190 parser.parse()?;
191 Ok(())
192 }
193}