* Removed the itertools dependency * Removed an unused feature flag * Stubbed out a toml_query replacement * Update dependencies. * Bump env_logger. * Use warp instead of iron for http server. Iron does not appear to be maintained anymore. warp/hyper seems to be reasonably maintained. Unfortunately this takes a few seconds more to compile, but shouldn't be too bad. One benefit is that there is no longer a need for a separate websocket port, which makes it easier to run multiple servers at once. * Update pulldown-cmark to 0.7 * Switch from error-chain to anyhow. * Bump MSRV to 1.39. * Update elasticlunr-rs. Co-authored-by: Michael Bryan <michaelfbryan@gmail.com>
130 lines
3.2 KiB
Rust
130 lines
3.2 KiB
Rust
use toml::value::{Table, Value};
|
|
|
|
pub(crate) trait TomlExt {
|
|
fn read(&self, key: &str) -> Option<&Value>;
|
|
fn read_mut(&mut self, key: &str) -> Option<&mut Value>;
|
|
fn insert(&mut self, key: &str, value: Value);
|
|
fn delete(&mut self, key: &str) -> Option<Value>;
|
|
}
|
|
|
|
impl TomlExt for Value {
|
|
fn read(&self, key: &str) -> Option<&Value> {
|
|
if let Some((head, tail)) = split(key) {
|
|
self.get(head)?.read(tail)
|
|
} else {
|
|
self.get(key)
|
|
}
|
|
}
|
|
|
|
fn read_mut(&mut self, key: &str) -> Option<&mut Value> {
|
|
if let Some((head, tail)) = split(key) {
|
|
self.get_mut(head)?.read_mut(tail)
|
|
} else {
|
|
self.get_mut(key)
|
|
}
|
|
}
|
|
|
|
fn insert(&mut self, key: &str, value: Value) {
|
|
if !self.is_table() {
|
|
*self = Value::Table(Table::new());
|
|
}
|
|
|
|
let table = self.as_table_mut().expect("unreachable");
|
|
|
|
if let Some((head, tail)) = split(key) {
|
|
table
|
|
.entry(head)
|
|
.or_insert_with(|| Value::Table(Table::new()))
|
|
.insert(tail, value);
|
|
} else {
|
|
table.insert(key.to_string(), value);
|
|
}
|
|
}
|
|
|
|
fn delete(&mut self, key: &str) -> Option<Value> {
|
|
if let Some((head, tail)) = split(key) {
|
|
self.get_mut(head)?.delete(tail)
|
|
} else if let Some(table) = self.as_table_mut() {
|
|
table.remove(key)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
fn split(key: &str) -> Option<(&str, &str)> {
|
|
let ix = key.find(".")?;
|
|
|
|
let (head, tail) = key.split_at(ix);
|
|
// splitting will leave the "."
|
|
let tail = &tail[1..];
|
|
|
|
Some((head, tail))
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use std::str::FromStr;
|
|
|
|
#[test]
|
|
fn read_simple_table() {
|
|
let src = "[table]";
|
|
let value = Value::from_str(src).unwrap();
|
|
|
|
let got = value.read("table").unwrap();
|
|
|
|
assert!(got.is_table());
|
|
}
|
|
|
|
#[test]
|
|
fn read_nested_item() {
|
|
let src = "[table]\nnested=true";
|
|
let value = Value::from_str(src).unwrap();
|
|
|
|
let got = value.read("table.nested").unwrap();
|
|
|
|
assert_eq!(got, &Value::Boolean(true));
|
|
}
|
|
|
|
#[test]
|
|
fn insert_item_at_top_level() {
|
|
let mut value = Value::Table(Table::default());
|
|
let item = Value::Boolean(true);
|
|
|
|
value.insert("first", item.clone());
|
|
|
|
assert_eq!(value.get("first").unwrap(), &item);
|
|
}
|
|
|
|
#[test]
|
|
fn insert_nested_item() {
|
|
let mut value = Value::Table(Table::default());
|
|
let item = Value::Boolean(true);
|
|
|
|
value.insert("first.second", item.clone());
|
|
|
|
let inserted = value.read("first.second").unwrap();
|
|
assert_eq!(inserted, &item);
|
|
}
|
|
|
|
#[test]
|
|
fn delete_a_top_level_item() {
|
|
let src = "top = true";
|
|
let mut value = Value::from_str(src).unwrap();
|
|
|
|
let got = value.delete("top").unwrap();
|
|
|
|
assert_eq!(got, Value::Boolean(true));
|
|
}
|
|
|
|
#[test]
|
|
fn delete_a_nested_item() {
|
|
let src = "[table]\n nested = true";
|
|
let mut value = Value::from_str(src).unwrap();
|
|
|
|
let got = value.delete("table.nested").unwrap();
|
|
|
|
assert_eq!(got, Value::Boolean(true));
|
|
}
|
|
}
|