1#![doc = include_str!("../README.md")]
2
3mod impls;
4mod parse;
5
6pub use parse::{ParseError, parse};
7
8#[cfg(feature = "serde")]
9mod serde;
10
11use std::fmt::{Debug, Display, Formatter};
12
13#[derive(Debug)]
15pub enum Error {
16 MissingSource,
18 Parse(ParseError),
20}
21
22impl Display for Error {
23 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
24 match self {
25 Self::MissingSource => write!(f, "configuration source is required"),
26 Self::Parse(error) => Display::fmt(error, f),
28 }
29 }
30}
31
32impl std::error::Error for Error {
33 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
34 match self {
35 Self::Parse(error) => Some(error),
36 Self::MissingSource => None,
37 }
38 }
39}
40
41impl From<ParseError> for Error {
42 fn from(error: ParseError) -> Self {
43 Self::Parse(error)
44 }
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
52pub enum Stage {
53 Load,
55 Parse,
57 Validate,
59}
60
61impl Stage {
62 pub fn as_str(self) -> &'static str {
64 match self {
65 Self::Load => "load",
66 Self::Parse => "parse",
67 Self::Validate => "validate",
68 }
69 }
70}
71
72impl Display for Stage {
73 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
74 f.write_str(self.as_str())
75 }
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
83pub enum OnError {
84 #[default]
86 Fail,
87 Skip,
89}
90
91#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
93pub enum OptionValueType {
94 Bool,
95 Integer,
96 Float,
97 String,
98 Map,
99 List,
100}
101
102impl Display for OptionValueType {
103 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
104 f.write_str(match self {
105 Self::Bool => "boolean",
106 Self::Integer => "integer",
107 Self::Float => "float",
108 Self::String => "string",
109 Self::Map => "map",
110 Self::List => "list",
111 })
112 }
113}
114
115#[derive(Debug, Clone, PartialEq)]
117pub enum OptionValue {
118 Bool(bool),
119 Integer(i64),
120 Float(f64),
121 String(String),
122 List(Vec<OptionValue>),
123 Map(Options),
124}
125
126impl OptionValue {
127 pub fn new_map() -> Self {
128 Self::Map(Options::default())
129 }
130
131 pub fn new_list() -> Self {
132 Self::List(Vec::new())
133 }
134
135 pub fn new_string() -> Self {
136 Self::String(String::new())
137 }
138
139 pub fn is_bool(&self) -> bool {
140 matches!(self, Self::Bool(_))
141 }
142
143 pub fn as_bool(&self) -> Option<bool> {
144 match self {
145 Self::Bool(value) => Some(*value),
146 _ => None,
147 }
148 }
149
150 pub fn into_bool(self) -> Option<bool> {
151 match self {
152 Self::Bool(value) => Some(value),
153 _ => None,
154 }
155 }
156
157 pub fn bool_mut(&mut self) -> Option<&mut bool> {
158 match self {
159 Self::Bool(value) => Some(value),
160 _ => None,
161 }
162 }
163
164 pub fn is_integer(&self) -> bool {
165 matches!(self, Self::Integer(_))
166 }
167
168 pub fn as_integer(&self) -> Option<i64> {
169 match self {
170 Self::Integer(value) => Some(*value),
171 _ => None,
172 }
173 }
174
175 pub fn into_integer(self) -> Option<i64> {
176 match self {
177 Self::Integer(value) => Some(value),
178 _ => None,
179 }
180 }
181
182 pub fn integer_mut(&mut self) -> Option<&mut i64> {
183 match self {
184 Self::Integer(value) => Some(value),
185 _ => None,
186 }
187 }
188
189 pub fn is_float(&self) -> bool {
190 matches!(self, Self::Float(_))
191 }
192
193 pub fn as_float(&self) -> Option<f64> {
194 match self {
195 Self::Float(value) => Some(*value),
196 _ => None,
197 }
198 }
199
200 pub fn into_float(self) -> Option<f64> {
201 match self {
202 Self::Float(value) => Some(value),
203 _ => None,
204 }
205 }
206
207 pub fn float_mut(&mut self) -> Option<&mut f64> {
208 match self {
209 Self::Float(value) => Some(value),
210 _ => None,
211 }
212 }
213
214 pub fn is_string(&self) -> bool {
215 matches!(self, Self::String(_))
216 }
217
218 pub fn as_string(&self) -> Option<&String> {
219 match self {
220 Self::String(value) => Some(value),
221 _ => None,
222 }
223 }
224
225 pub fn into_string(self) -> Option<String> {
226 match self {
227 Self::String(value) => Some(value),
228 _ => None,
229 }
230 }
231
232 pub fn string_mut(&mut self) -> Option<&mut String> {
233 match self {
234 Self::String(value) => Some(value),
235 _ => None,
236 }
237 }
238
239 pub fn is_list(&self) -> bool {
240 matches!(self, Self::List(_))
241 }
242
243 pub fn as_list(&self) -> Option<&Vec<OptionValue>> {
244 match self {
245 Self::List(value) => Some(value),
246 _ => None,
247 }
248 }
249
250 pub fn into_list(self) -> Option<Vec<OptionValue>> {
251 match self {
252 Self::List(value) => Some(value),
253 _ => None,
254 }
255 }
256
257 pub fn list_mut(&mut self) -> Option<&mut Vec<OptionValue>> {
258 match self {
259 Self::List(value) => Some(value),
260 _ => None,
261 }
262 }
263
264 pub fn is_map(&self) -> bool {
265 matches!(self, Self::Map(_))
266 }
267
268 pub fn as_map(&self) -> Option<&Options> {
269 match self {
270 Self::Map(value) => Some(value),
271 _ => None,
272 }
273 }
274
275 pub fn into_map(self) -> Option<Options> {
276 match self {
277 Self::Map(value) => Some(value),
278 _ => None,
279 }
280 }
281
282 pub fn map_mut(&mut self) -> Option<&mut Options> {
283 match self {
284 Self::Map(value) => Some(value),
285 _ => None,
286 }
287 }
288
289 pub fn type_name(&self) -> OptionValueType {
290 match self {
291 Self::Bool(_) => OptionValueType::Bool,
292 Self::Integer(_) => OptionValueType::Integer,
293 Self::Float(_) => OptionValueType::Float,
294 Self::String(_) => OptionValueType::String,
295 Self::List(_) => OptionValueType::List,
296 Self::Map(_) => OptionValueType::Map,
297 }
298 }
299}
300
301impl Display for OptionValue {
302 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
303 match self {
304 Self::Bool(value) => write!(f, "{value}"),
305 Self::Integer(value) => write!(f, "{value}"),
306 Self::Float(value) => write!(f, "{value}"),
307 Self::String(value) => write!(f, "{value:?}"),
308 Self::List(value) => {
309 write!(f, "[")?;
310 for (index, inner_value) in value.iter().enumerate() {
311 if index > 0 {
312 write!(f, ", ")?;
313 }
314 write!(f, "{inner_value}")?;
315 }
316 write!(f, "]")
317 }
318 Self::Map(value) => write!(f, "{value}"),
319 }
320 }
321}
322
323#[derive(Debug, Clone, PartialEq, Default)]
325pub struct Options {
326 entries: Vec<(String, OptionValue)>,
327}
328
329impl Options {
330 pub fn new() -> Self {
331 Self::default()
332 }
333
334 pub fn len(&self) -> usize {
335 self.entries.len()
336 }
337
338 pub fn is_empty(&self) -> bool {
339 self.entries.is_empty()
340 }
341
342 pub fn contains_key(&self, key: &str) -> bool {
343 self.entries.iter().any(|(entry_key, _)| entry_key == key)
344 }
345
346 pub fn get(&self, key: &str) -> Option<&OptionValue> {
347 self.entries
348 .iter()
349 .rfind(|(entry_key, _)| entry_key == key)
350 .map(|(_, value)| value)
351 }
352
353 pub fn get_mut(&mut self, key: &str) -> Option<&mut OptionValue> {
354 let index = self
355 .entries
356 .iter()
357 .rposition(|(entry_key, _)| entry_key == key)?;
358 Some(&mut self.entries[index].1)
359 }
360
361 pub fn insert<K: Into<String>, V: Into<OptionValue>>(
362 &mut self,
363 key: K,
364 value: V,
365 ) -> Option<OptionValue> {
366 let key = key.into();
367 let value = value.into();
368 let old = self.remove(&key);
369 self.entries.push((key, value));
370 old
371 }
372
373 pub fn remove(&mut self, key: &str) -> Option<OptionValue> {
374 let index = self
375 .entries
376 .iter()
377 .rposition(|(entry_key, _)| entry_key == key)?;
378 Some(self.entries.remove(index).1)
379 }
380
381 pub fn iter(&self) -> impl Iterator<Item = (&str, &OptionValue)> {
382 self.entries
383 .iter()
384 .map(|(key, value)| (key.as_str(), value))
385 }
386
387 pub fn keys(&self) -> impl Iterator<Item = &str> {
388 self.entries.iter().map(|(key, _)| key.as_str())
389 }
390
391 pub fn values(&self) -> impl Iterator<Item = &OptionValue> {
392 self.entries.iter().map(|(_, value)| value)
393 }
394
395 pub(crate) fn entries(&self) -> &[(String, OptionValue)] {
396 &self.entries
397 }
398}
399
400impl Display for Options {
401 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
402 write!(f, "{{")?;
403 for (index, (key, value)) in self.entries.iter().enumerate() {
404 if index > 0 {
405 write!(f, ", ")?;
406 }
407 write!(f, "{key:?}: {value}")?;
408 }
409 write!(f, "}}")
410 }
411}
412
413#[derive(Debug, Clone, PartialEq)]
418pub struct Source {
419 pub(crate) source: String,
420 pub(crate) options: Options,
421 pub(crate) resource: String,
422 pub(crate) resource_colon: bool,
423}
424
425impl Source {
426 pub fn parse(input: &str) -> Result<Self, ParseError> {
427 parse::parse(input)
428 }
429
430 pub fn named(name: impl Into<String>) -> Self {
436 Self {
437 source: name.into(),
438 options: Options::default(),
439 resource: String::new(),
440 resource_colon: false,
441 }
442 }
443
444 pub fn on_error(&self, stage: Stage) -> OnError {
449 let Some(OptionValue::Map(map)) = self.options.get("on_error") else {
450 return OnError::Fail;
451 };
452 match map.get(stage.as_str()) {
453 Some(OptionValue::String(value)) if value.eq_ignore_ascii_case("skip") => OnError::Skip,
454 _ => OnError::Fail,
455 }
456 }
457
458 pub fn source(&self) -> &str {
459 self.source.as_str()
460 }
461
462 pub fn source_mut(&mut self) -> &mut String {
463 &mut self.source
464 }
465
466 pub fn set_source(&mut self, source: impl Into<String>) {
467 self.source = source.into();
468 }
469
470 pub fn with_source(mut self, source: impl Into<String>) -> Self {
471 self.source = source.into();
472 self
473 }
474
475 pub fn options(&self) -> &Options {
476 &self.options
477 }
478
479 pub fn options_mut(&mut self) -> &mut Options {
480 &mut self.options
481 }
482
483 pub fn set_options(&mut self, options: Options) {
484 self.options = options;
485 }
486
487 pub fn with_options(mut self, options: Options) -> Self {
488 self.options = options;
489 self
490 }
491
492 pub fn set_option<K: Into<String>, V: Into<OptionValue>>(&mut self, key: K, value: V) {
493 self.options.insert(key, value);
494 }
495
496 pub fn with_option<K: Into<String>, V: Into<OptionValue>>(mut self, key: K, value: V) -> Self {
497 self.options.insert(key, value);
498 self
499 }
500
501 pub fn resource(&self) -> &str {
502 self.resource.as_str()
503 }
504
505 pub fn resource_mut(&mut self) -> &mut String {
506 &mut self.resource
507 }
508
509 pub fn set_resource(&mut self, resource: impl Into<String>) {
510 self.resource = resource.into();
511 if !self.resource.is_empty() {
512 self.resource_colon = true;
513 }
514 }
515
516 pub fn with_resource(mut self, resource: impl Into<String>) -> Self {
517 self.resource = resource.into();
518 if !self.resource.is_empty() {
519 self.resource_colon = true;
520 }
521 self
522 }
523
524 pub fn resource_colon(&self) -> bool {
525 self.resource_colon
526 }
527
528 pub fn set_resource_colon(&mut self, resource_colon: bool) {
529 self.resource_colon = resource_colon;
530 }
531
532 pub fn with_resource_colon(mut self, resource_colon: bool) -> Self {
533 self.resource_colon = resource_colon;
534 self
535 }
536}
537
538#[derive(Debug, Clone, PartialEq, Default)]
540pub struct SourceBuilder {
541 source: Option<String>,
542 options: Options,
543 resource: String,
544 resource_colon: bool,
545}
546
547impl SourceBuilder {
548 pub fn new() -> Self {
549 Self::default()
550 }
551
552 pub fn with_source(mut self, source: impl Into<String>) -> Self {
553 self.source = Some(source.into());
554 self
555 }
556
557 pub fn with_resource(mut self, resource: impl Into<String>) -> Self {
558 self.resource = resource.into();
559 self
560 }
561
562 pub fn with_options(mut self, options: Options) -> Self {
563 self.options = options;
564 self
565 }
566
567 pub fn with_option<K: Into<String>, V: Into<OptionValue>>(mut self, key: K, value: V) -> Self {
568 self.options.insert(key, value);
569 self
570 }
571
572 pub fn with_resource_colon(mut self, resource_colon: bool) -> Self {
573 self.resource_colon = resource_colon;
574 self
575 }
576
577 pub fn build(self) -> Result<Source, Error> {
578 let source = self.source.ok_or(Error::MissingSource)?;
579 if source.is_empty() {
580 return Err(Error::MissingSource);
581 }
582 let resource_colon = self.resource_colon || !self.resource.is_empty();
583 Ok(Source {
584 source,
585 options: self.options,
586 resource: self.resource,
587 resource_colon,
588 })
589 }
590}