chiark / gitweb /
document and parse limit section headings
[hippotat.git] / src / config.rs
index 64e2451bcbaca7abd12cc1f8c7a30eb2105aae2d..63665f96f15071be3f81fef558da536984388d18 100644 (file)
@@ -57,7 +57,21 @@ pub struct InstanceConfig {
   pub vroutes:                      Vec<CidrString>,
 }
 
-type SectionMap = HashMap<String, Option<String>>;
+#[derive(Debug,Clone,Hash,Eq,PartialEq)]
+pub enum SectionName {
+  Link { server: ServerName, client: ClientName },
+  Client(ClientName),
+  Server(ServerName), // includes SERVER, which is slightly special
+  ServerLimit(ServerName),
+  GlobalLimit,
+  Common,
+  Default,
+}
+pub use SectionName as SN;
+
+#[derive(Debug,Clone)]
+struct RawVal { val: Option<String>, loc: Arc<PathBuf> }
+type SectionMap = HashMap<String, RawVal>;
 
 pub struct Config {
   opts: Opts,
@@ -67,7 +81,7 @@ static OUTSIDE_SECTION: &str = "[";
 
 #[derive(Default,Debug)]
 struct Aggregate {
-  sections: HashMap<String, SectionMap>,
+  sections: HashMap<SectionName, SectionMap>,
 }
 
 type OkAnyway<'f,A> = &'f dyn Fn(ErrorKind) -> Option<A>;
@@ -81,6 +95,31 @@ impl<'f,A> OkAnyway<'f,A> {
   }
 }
 
+impl FromStr for SectionName {
+  type Err = AE;
+  #[throws(AE)]
+  fn from_str(s: &str) -> Self {
+    match s {
+      "COMMON" => return SN::Common,
+      "DEFAULT" => return SN::Default,
+      "LIMIT" => return SN::GlobalLimit,
+      _ => { }
+    };
+    if let Ok(n@ ServerName(_)) = s.parse() { return SN::Server(n) }
+    if let Ok(n@ ClientName(_)) = s.parse() { return SN::Client(n) }
+    let (server, client) = s.split_ascii_whitespace().collect_tuple()
+      .ok_or_else(|| anyhow!(
+        "bad section name {:?} \
+         (must be COMMON, DEFAULT, <server>, <client>, or <server> <client>",
+        s
+      ))?;
+    let server = server.parse().context("server name in link section name")?;
+    if client == "LIMIT" { return SN::ServerLimit(server) }
+    let client = client.parse().context("client name in link section name")?;
+    SN::Link { server, client }
+  }
+}
+
 impl Aggregate {
   #[throws(AE)] // AE does not include path
   fn read_file<A>(&mut self, path: &Path, anyway: OkAnyway<A>) -> Option<A>
@@ -97,15 +136,25 @@ impl Aggregate {
     let mut ini = Ini::new_cs();
     ini.set_default_section(OUTSIDE_SECTION);
     ini.read(s).map_err(|e| anyhow!("{}", e)).context("parse as INI")?;
-    let mut map = mem::take(ini.get_mut_map());
+    let map = mem::take(ini.get_mut_map());
     if map.get(OUTSIDE_SECTION).is_some() {
       throw!(anyhow!("INI file contains settings outside a section"));
     }
 
-    // xxx parse section names here
-    // xxx save Arc<PathBuf> where we found each item
-
-    self.sections.extend(map.drain());
+    let loc = Arc::new(path.to_owned());
+
+    for (sn, vars) in map {
+      let sn = sn.parse().dcontext(&sn)?;
+        self.sections.entry(sn)
+        .or_default()
+        .extend(
+          vars.into_iter()
+            .map(|(k,val)| {
+              (k.replace('-',"_"),
+               RawVal { val, loc: loc.clone() })
+            })
+        );
+    }
     None
   }
 
@@ -216,7 +265,7 @@ pub fn read() {
       agg.read_extra(extra).context("extra config")?;
     }
 
-    eprintln!("GOT {:?}", agg);
+    eprintln!("GOT {:#?}", agg);
 
     Ok::<_,AE>(())
   })().context("read configuration")?;