1#![forbid(unsafe_code)]
2
3pub mod diagnostics;
4pub mod lines;
5pub mod parse;
6pub mod spanned;
7pub mod value;
8
9pub use parse::{Config as ParseConfig, Error};
10pub use spanned::{DerefInner, Span, Spanned};
11pub use value::{Options, Section, SectionProxy, SectionProxyMut, Value, from_reader, from_str};
12
13#[cfg(test)]
14pub mod tests {
15 use crate::{
16 SectionProxy, Spanned,
17 value::{Options, Value},
18 };
19 use codespan_reporting::{diagnostic::Diagnostic, files, term};
20 use std::sync::{Mutex, RwLock};
21
22 static INIT: std::sync::Once = std::sync::Once::new();
23
24 pub fn init() {
28 INIT.call_once(|| {
29 color_eyre::install().ok();
30 });
31 }
32
33 impl From<&str> for Spanned<String> {
35 fn from(value: &str) -> Self {
36 Spanned::dummy(value.to_string())
37 }
38 }
39
40 pub(crate) trait SectionProxyExt<'a> {
41 fn items_vec(self) -> Vec<(&'a str, &'a str)>;
42 fn keys_vec(self) -> Vec<&'a str>;
43 }
44
45 impl<'a> SectionProxyExt<'a> for SectionProxy<'a> {
46 fn items_vec(self) -> Vec<(&'a str, &'a str)> {
47 self.iter()
48 .map(|(k, v)| (k.as_ref().as_str(), v.as_ref().as_str()))
49 .collect::<Vec<_>>()
50 }
51
52 fn keys_vec(self) -> Vec<&'a str> {
53 self.keys()
54 .map(|k| k.as_ref().as_str())
55 .collect::<Vec<&'a str>>()
56 }
57 }
58
59 impl From<String> for Spanned<String> {
61 fn from(value: String) -> Self {
62 Spanned::dummy(value)
63 }
64 }
65
66 #[derive(Debug)]
67 pub(crate) struct Printer {
68 writer: Mutex<term::termcolor::Buffer>,
69 diagnostic_config: term::Config,
70 files: RwLock<files::SimpleFiles<String, String>>,
71 }
72
73 impl Default for Printer {
74 fn default() -> Self {
75 Self::new()
76 }
77 }
78
79 impl Printer {
80 #[must_use]
81 pub(crate) fn new() -> Self {
82 let writer = term::termcolor::Buffer::ansi();
83 let diagnostic_config = term::Config {
84 styles: term::Styles::with_blue(term::termcolor::Color::Blue),
85 ..term::Config::default()
86 };
87 Self {
88 writer: Mutex::new(writer),
89 diagnostic_config,
90 files: RwLock::new(files::SimpleFiles::new()),
91 }
92 }
93
94 pub(crate) fn add_source_file(&self, name: String, source: String) -> usize {
95 let mut files = self.files.write().unwrap();
96 files.add(name, source)
97 }
98
99 pub(crate) fn emit(&self, diagnostic: &Diagnostic<usize>) -> Result<(), files::Error> {
100 term::emit(
101 &mut *self.writer.lock().unwrap(),
102 &self.diagnostic_config,
103 &*self.files.read().unwrap(),
104 diagnostic,
105 )
106 }
107
108 pub(crate) fn print(&self) {
112 use std::io::Write;
113 let mut writer = self.writer.lock().unwrap();
114 let _ = writer.flush();
115 eprintln!("{}", String::from_utf8_lossy(writer.as_slice()));
116 }
117 }
118
119 pub(crate) fn parse(
121 config: &str,
122 options: Options,
123 printer: &Printer,
124 ) -> (Result<Value, super::Error>, usize, Vec<Diagnostic<usize>>) {
125 let file_id = printer.add_source_file("config.ini".to_string(), config.to_string());
126 let mut diagnostics = vec![];
127 let config = crate::from_str(config, options, file_id, &mut diagnostics);
128 if let Err(ref err) = config {
129 diagnostics.extend(err.to_diagnostics(file_id));
130 }
131 for diagnostic in &diagnostics {
132 printer.emit(diagnostic).expect("emit diagnostic");
133 }
134 printer.print();
135 (config, file_id, diagnostics)
136 }
137}