--- /dev/null
+// Copyright 2021 Ian Jackson and contributors to Hippotat
+// SPDX-License-Identifier: GPL-3.0-or-later
+// There is NO WARRANTY.
+
+pub struct Loc {
+ pub file: Arc<PathBuf>,
+ pub line: usize,
+ pub section: Option<Arc<String>>,
+}
+
+#[derive(Debug,Clone)]
+pub struct RawVal {
+ pub raw: String,
+ pub loc: Loc,
+}
+
+pub type Parsed = HashMap<Arc<String>, Section>;
+
+pub struct Section {
+ /// Location of first encounter
+ pub loc: Loc,
+ pub values: HashMap<String, RawVal>,
+}
+
+impl Display for Loc {
+ #[throws(fmt::Error)]
+ fn fmt(&self, f: &mut fmt::Formatter) {
+ write!(f, "{}:{}", &self.file, self.line)?;
+ if let Some(s) = &self.section {
+ let dbg = format!("{:?}", &m);
+ if let Some(mid) = (||{
+ let mid = dbg.strip_prefix(r#"""#)?;
+ let mid = mid.strip_suffix(r#"""#)?;
+ Some(mid)
+ })() {
+ write!(f, "[{}]", mid)?;
+ } else {
+ write!(f, "{}", dbg)?;
+ }
+ }
+ }
+}
+
+#[throws(AE)]
+pub fn read(parsed: &mut Parsed, file: &dyn BufRead, path_for_loc: &Path) {
+ let path = path_for_loc.to_owned().into();
+ let section: Option<&mut Section> = None;
+ for (lno, line) in file.lines().enumerate() {
+ let line = line.context("read")?;
+ (||{
+ let line = line.trim();
+
+ if line.is_empty() { continue }
+ if regex_is_match!(r#"^ [;#] "#x, line) { continue }
+
+ let mut loc = Loc {
+ line,
+ file: path.clone(),
+ section: section.as_ref().map(|s| s.section.clone()),
+ };
+
+ if let Some((_,section,)) =
+ regex_captures!(r#"^ \[ \s* (.+?) \s* \] $"#x, line)
+ {
+ let sname = Some(section.to_owned().into());
+ section = Some(parsed.entry(sname.clone())
+ .get_or_insert_with(|| {
+ Section {
+ loc: Loc { section: sname, ..loc },
+ values: default(),
+ }
+ }));
+ continue;
+ }
+
+ if let Some((_,k,v)) =
+ regex_captures!(r#"^ ( [^\[] .*? ) \s* = \s* (.*) $"#, line)
+ {
+ section.values.insert(k.into(), RawVal {
+ loc,
+ raw: v.into(),
+ });
+ continue;
+ }
+
+ throw!(if line.starts_with("[") {
+ anyhow!(r#"syntax error (section missing final "]"?)"#)
+ } else {
+ anyhow!(r#"syntax error (setting missing "="?)"#)
+ })
+
+ })().with_context(|| loc().to_string())?
+ }
+}