1#![doc = include_str!("../README.md")]
2#![deny(missing_docs)]
3
4mod impls;
5mod parse;
6
7pub use parse::{ParseError, parse};
8
9#[cfg(feature = "serde")]
10mod serde;
11
12use std::fmt::{Debug, Display, Formatter};
13
14#[derive(Debug)]
16pub enum Error {
17 MissingSource,
19 Parse(ParseError),
21}
22
23impl Display for Error {
24 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
25 match self {
26 Self::MissingSource => write!(f, "configuration source is required"),
27 Self::Parse(error) => Display::fmt(error, f),
29 }
30 }
31}
32
33impl std::error::Error for Error {
34 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
35 match self {
36 Self::Parse(error) => Some(error),
37 Self::MissingSource => None,
38 }
39 }
40}
41
42impl From<ParseError> for Error {
43 fn from(error: ParseError) -> Self {
44 Self::Parse(error)
45 }
46}
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
53pub enum Stage {
54 Load,
56 Parse,
58 Validate,
60}
61
62impl Stage {
63 pub fn as_str(self) -> &'static str {
65 match self {
66 Self::Load => "load",
67 Self::Parse => "parse",
68 Self::Validate => "validate",
69 }
70 }
71}
72
73impl Display for Stage {
74 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
75 f.write_str(self.as_str())
76 }
77}
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
84pub enum OnError {
85 #[default]
87 Fail,
88 Skip,
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
94pub enum OptionValueType {
95 Bool,
97 Integer,
99 Float,
101 String,
103 Map,
105 List,
107}
108
109impl Display for OptionValueType {
110 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
111 f.write_str(match self {
112 Self::Bool => "boolean",
113 Self::Integer => "integer",
114 Self::Float => "float",
115 Self::String => "string",
116 Self::Map => "map",
117 Self::List => "list",
118 })
119 }
120}
121
122#[derive(Debug, Clone, PartialEq)]
135pub enum OptionValue {
136 Bool(bool),
138 Integer(i64),
140 Float(f64),
142 String(String),
144 List(Vec<OptionValue>),
146 Map(Options),
148}
149
150impl OptionValue {
151 pub fn new_map() -> Self {
153 Self::Map(Options::default())
154 }
155
156 pub fn new_list() -> Self {
158 Self::List(Vec::new())
159 }
160
161 pub fn new_string() -> Self {
163 Self::String(String::new())
164 }
165
166 pub fn is_bool(&self) -> bool {
168 matches!(self, Self::Bool(_))
169 }
170
171 pub fn as_bool(&self) -> Option<bool> {
173 match self {
174 Self::Bool(value) => Some(*value),
175 _ => None,
176 }
177 }
178
179 pub fn into_bool(self) -> Option<bool> {
181 match self {
182 Self::Bool(value) => Some(value),
183 _ => None,
184 }
185 }
186
187 pub fn bool_mut(&mut self) -> Option<&mut bool> {
189 match self {
190 Self::Bool(value) => Some(value),
191 _ => None,
192 }
193 }
194
195 pub fn is_integer(&self) -> bool {
197 matches!(self, Self::Integer(_))
198 }
199
200 pub fn as_integer(&self) -> Option<i64> {
202 match self {
203 Self::Integer(value) => Some(*value),
204 _ => None,
205 }
206 }
207
208 pub fn into_integer(self) -> Option<i64> {
210 match self {
211 Self::Integer(value) => Some(value),
212 _ => None,
213 }
214 }
215
216 pub fn integer_mut(&mut self) -> Option<&mut i64> {
218 match self {
219 Self::Integer(value) => Some(value),
220 _ => None,
221 }
222 }
223
224 pub fn is_float(&self) -> bool {
226 matches!(self, Self::Float(_))
227 }
228
229 pub fn as_float(&self) -> Option<f64> {
231 match self {
232 Self::Float(value) => Some(*value),
233 _ => None,
234 }
235 }
236
237 pub fn into_float(self) -> Option<f64> {
239 match self {
240 Self::Float(value) => Some(value),
241 _ => None,
242 }
243 }
244
245 pub fn float_mut(&mut self) -> Option<&mut f64> {
247 match self {
248 Self::Float(value) => Some(value),
249 _ => None,
250 }
251 }
252
253 pub fn is_string(&self) -> bool {
255 matches!(self, Self::String(_))
256 }
257
258 pub fn as_string(&self) -> Option<&String> {
260 match self {
261 Self::String(value) => Some(value),
262 _ => None,
263 }
264 }
265
266 pub fn into_string(self) -> Option<String> {
268 match self {
269 Self::String(value) => Some(value),
270 _ => None,
271 }
272 }
273
274 pub fn string_mut(&mut self) -> Option<&mut String> {
276 match self {
277 Self::String(value) => Some(value),
278 _ => None,
279 }
280 }
281
282 pub fn is_list(&self) -> bool {
284 matches!(self, Self::List(_))
285 }
286
287 pub fn as_list(&self) -> Option<&Vec<OptionValue>> {
289 match self {
290 Self::List(value) => Some(value),
291 _ => None,
292 }
293 }
294
295 pub fn into_list(self) -> Option<Vec<OptionValue>> {
297 match self {
298 Self::List(value) => Some(value),
299 _ => None,
300 }
301 }
302
303 pub fn list_mut(&mut self) -> Option<&mut Vec<OptionValue>> {
305 match self {
306 Self::List(value) => Some(value),
307 _ => None,
308 }
309 }
310
311 pub fn is_map(&self) -> bool {
313 matches!(self, Self::Map(_))
314 }
315
316 pub fn as_map(&self) -> Option<&Options> {
318 match self {
319 Self::Map(value) => Some(value),
320 _ => None,
321 }
322 }
323
324 pub fn into_map(self) -> Option<Options> {
326 match self {
327 Self::Map(value) => Some(value),
328 _ => None,
329 }
330 }
331
332 pub fn map_mut(&mut self) -> Option<&mut Options> {
334 match self {
335 Self::Map(value) => Some(value),
336 _ => None,
337 }
338 }
339
340 pub fn type_name(&self) -> OptionValueType {
342 match self {
343 Self::Bool(_) => OptionValueType::Bool,
344 Self::Integer(_) => OptionValueType::Integer,
345 Self::Float(_) => OptionValueType::Float,
346 Self::String(_) => OptionValueType::String,
347 Self::List(_) => OptionValueType::List,
348 Self::Map(_) => OptionValueType::Map,
349 }
350 }
351}
352
353impl Display for OptionValue {
354 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
355 match self {
356 Self::Bool(value) => write!(f, "{value}"),
357 Self::Integer(value) => write!(f, "{value}"),
358 Self::Float(value) => write!(f, "{value}"),
359 Self::String(value) => write!(f, "{value:?}"),
360 Self::List(value) => {
361 write!(f, "[")?;
362 for (index, inner_value) in value.iter().enumerate() {
363 if index > 0 {
364 write!(f, ", ")?;
365 }
366 write!(f, "{inner_value}")?;
367 }
368 write!(f, "]")
369 }
370 Self::Map(value) => write!(f, "{value}"),
371 }
372 }
373}
374
375#[derive(Debug, Clone, PartialEq, Default)]
377pub struct Options {
378 entries: Vec<(String, OptionValue)>,
379}
380
381impl Options {
382 pub fn new() -> Self {
384 Self::default()
385 }
386
387 pub fn len(&self) -> usize {
389 self.entries.len()
390 }
391
392 pub fn is_empty(&self) -> bool {
394 self.entries.is_empty()
395 }
396
397 pub fn contains_key(&self, key: &str) -> bool {
399 self.entries.iter().any(|(entry_key, _)| entry_key == key)
400 }
401
402 pub fn get(&self, key: &str) -> Option<&OptionValue> {
404 self.entries
405 .iter()
406 .rfind(|(entry_key, _)| entry_key == key)
407 .map(|(_, value)| value)
408 }
409
410 pub fn get_mut(&mut self, key: &str) -> Option<&mut OptionValue> {
412 let index = self
413 .entries
414 .iter()
415 .rposition(|(entry_key, _)| entry_key == key)?;
416 Some(&mut self.entries[index].1)
417 }
418
419 pub fn insert<K: Into<String>, V: Into<OptionValue>>(
424 &mut self,
425 key: K,
426 value: V,
427 ) -> Option<OptionValue> {
428 let key = key.into();
429 let value = value.into();
430 let old = self.remove(&key);
431 self.entries.push((key, value));
432 old
433 }
434
435 pub fn remove(&mut self, key: &str) -> Option<OptionValue> {
437 let index = self
438 .entries
439 .iter()
440 .rposition(|(entry_key, _)| entry_key == key)?;
441 Some(self.entries.remove(index).1)
442 }
443
444 pub fn iter(&self) -> impl Iterator<Item = (&str, &OptionValue)> {
446 self.entries
447 .iter()
448 .map(|(key, value)| (key.as_str(), value))
449 }
450
451 pub fn keys(&self) -> impl Iterator<Item = &str> {
453 self.entries.iter().map(|(key, _)| key.as_str())
454 }
455
456 pub fn values(&self) -> impl Iterator<Item = &OptionValue> {
458 self.entries.iter().map(|(_, value)| value)
459 }
460
461 pub(crate) fn entries(&self) -> &[(String, OptionValue)] {
462 &self.entries
463 }
464}
465
466impl Display for Options {
467 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
468 write!(f, "{{")?;
469 for (index, (key, value)) in self.entries.iter().enumerate() {
470 if index > 0 {
471 write!(f, ", ")?;
472 }
473 write!(f, "{key:?}: {value}")?;
474 }
475 write!(f, "}}")
476 }
477}
478
479#[derive(Debug, Clone, PartialEq)]
505pub struct Source {
506 pub(crate) source: String,
507 pub(crate) options: Options,
508 pub(crate) resource: String,
509 pub(crate) resource_colon: bool,
510}
511
512impl Source {
513 pub fn parse(input: &str) -> Result<Self, ParseError> {
518 parse::parse(input)
519 }
520
521 pub fn named(name: impl Into<String>) -> Self {
527 Self {
528 source: name.into(),
529 options: Options::default(),
530 resource: String::new(),
531 resource_colon: false,
532 }
533 }
534
535 pub fn on_error(&self, stage: Stage) -> OnError {
540 let Some(OptionValue::Map(map)) = self.options.get("on_error") else {
541 return OnError::Fail;
542 };
543 match map.get(stage.as_str()) {
544 Some(OptionValue::String(value)) if value.eq_ignore_ascii_case("skip") => OnError::Skip,
545 _ => OnError::Fail,
546 }
547 }
548
549 pub fn source(&self) -> &str {
551 self.source.as_str()
552 }
553
554 pub fn source_mut(&mut self) -> &mut String {
556 &mut self.source
557 }
558
559 pub fn set_source(&mut self, source: impl Into<String>) {
561 self.source = source.into();
562 }
563
564 pub fn with_source(mut self, source: impl Into<String>) -> Self {
566 self.source = source.into();
567 self
568 }
569
570 pub fn options(&self) -> &Options {
572 &self.options
573 }
574
575 pub fn options_mut(&mut self) -> &mut Options {
577 &mut self.options
578 }
579
580 pub fn set_options(&mut self, options: Options) {
582 self.options = options;
583 }
584
585 pub fn with_options(mut self, options: Options) -> Self {
587 self.options = options;
588 self
589 }
590
591 pub fn set_option<K: Into<String>, V: Into<OptionValue>>(&mut self, key: K, value: V) {
593 self.options.insert(key, value);
594 }
595
596 pub fn with_option<K: Into<String>, V: Into<OptionValue>>(mut self, key: K, value: V) -> Self {
598 self.options.insert(key, value);
599 self
600 }
601
602 pub fn resource(&self) -> &str {
604 self.resource.as_str()
605 }
606
607 pub fn resource_mut(&mut self) -> &mut String {
609 &mut self.resource
610 }
611
612 pub fn set_resource(&mut self, resource: impl Into<String>) {
614 self.resource = resource.into();
615 if !self.resource.is_empty() {
616 self.resource_colon = true;
617 }
618 }
619
620 pub fn with_resource(mut self, resource: impl Into<String>) -> Self {
623 self.resource = resource.into();
624 if !self.resource.is_empty() {
625 self.resource_colon = true;
626 }
627 self
628 }
629
630 pub fn resource_colon(&self) -> bool {
633 self.resource_colon
634 }
635
636 pub fn set_resource_colon(&mut self, resource_colon: bool) {
638 self.resource_colon = resource_colon;
639 }
640
641 pub fn with_resource_colon(mut self, resource_colon: bool) -> Self {
643 self.resource_colon = resource_colon;
644 self
645 }
646}
647
648#[derive(Debug, Clone, PartialEq, Default)]
664pub struct SourceBuilder {
665 source: Option<String>,
666 options: Options,
667 resource: String,
668 resource_colon: bool,
669}
670
671impl SourceBuilder {
672 pub fn new() -> Self {
674 Self::default()
675 }
676
677 pub fn with_source(mut self, source: impl Into<String>) -> Self {
679 self.source = Some(source.into());
680 self
681 }
682
683 pub fn with_resource(mut self, resource: impl Into<String>) -> Self {
685 self.resource = resource.into();
686 self
687 }
688
689 pub fn with_options(mut self, options: Options) -> Self {
691 self.options = options;
692 self
693 }
694
695 pub fn with_option<K: Into<String>, V: Into<OptionValue>>(mut self, key: K, value: V) -> Self {
697 self.options.insert(key, value);
698 self
699 }
700
701 pub fn with_resource_colon(mut self, resource_colon: bool) -> Self {
703 self.resource_colon = resource_colon;
704 self
705 }
706
707 pub fn build(self) -> Result<Source, Error> {
709 let source = self.source.ok_or(Error::MissingSource)?;
710 if source.is_empty() {
711 return Err(Error::MissingSource);
712 }
713 let resource_colon = self.resource_colon || !self.resource.is_empty();
714 Ok(Source {
715 source,
716 options: self.options,
717 resource: self.resource,
718 resource_colon,
719 })
720 }
721}