Merge pull request #326 from LibreQoE/format_preserving_toml

Format-preserving TOML editing
This commit is contained in:
Robert Chacón 2023-04-11 09:57:00 -06:00 committed by GitHub
commit f70c4ca639
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 56 additions and 40 deletions

16
src/rust/Cargo.lock generated
View File

@ -812,7 +812,7 @@ dependencies = [
"atomic",
"pear",
"serde",
"toml 0.5.11",
"toml",
"uncased",
"version_check",
]
@ -1436,7 +1436,7 @@ dependencies = [
"serde_json",
"sha2",
"thiserror",
"toml 0.7.2",
"toml_edit",
"uuid",
]
@ -2787,18 +2787,6 @@ dependencies = [
"serde",
]
[[package]]
name = "toml"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7afcae9e3f0fe2c370fd4657108972cbb2fa9db1b9f84849cefd80741b01cb6"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]]
name = "toml_datetime"
version = "0.6.1"

View File

@ -6,7 +6,7 @@ license = "GPL-2.0-only"
[dependencies]
thiserror = "1"
toml = "0"
toml_edit = { version = "0", features = [ "serde" ] }
serde = { version = "1.0", features = [ "derive" ] }
serde_json = "1"
csv = "1"

View File

@ -68,7 +68,7 @@ impl WebUsers {
fn save_to_disk(&self) -> Result<(), AuthenticationError> {
let path = Self::path()?;
let new_contents = toml::to_string(&self);
let new_contents = toml_edit::ser::to_string(&self);
if let Err(e) = new_contents {
return Err(AuthenticationError::SerializationError(e));
}
@ -109,7 +109,7 @@ impl WebUsers {
} else {
// Load from disk
if let Ok(raw) = read_to_string(path) {
let parse_result = toml::from_str(&raw);
let parse_result = toml_edit::de::from_str(&raw);
if let Ok(users) = parse_result {
Ok(users)
} else {
@ -255,7 +255,7 @@ pub enum AuthenticationError {
#[error("Unable to load /etc/lqos.conf")]
UnableToLoadEtcLqos,
#[error("Unable to serialize to TOML")]
SerializationError(toml::ser::Error),
SerializationError(toml_edit::ser::Error),
#[error("Unable to remove existing web users file")]
UnableToDelete,
#[error("Unable to open lqusers.toml for writing. Check permissions?")]

View File

@ -1,6 +1,7 @@
//! Manages the `/etc/lqos.conf` file.
use log::error;
use serde::{Deserialize, Serialize};
use toml_edit::{Document, value};
use std::{fs, path::Path};
use thiserror::Error;
@ -136,17 +137,27 @@ impl EtcLqos {
return Err(EtcLqosError::ConfigDoesNotExist);
}
if let Ok(raw) = std::fs::read_to_string("/etc/lqos.conf") {
let config_result: Result<Self, toml::de::Error> = toml::from_str(&raw);
match config_result {
Ok(mut config) => {
check_config(&mut config);
Ok(config)
}
let document = raw.parse::<Document>();
match document {
Err(e) => {
error!("Unable to parse TOML from /etc/lqos.conf");
error!("Full error: {:?}", e);
Err(EtcLqosError::CannotParseToml)
}
Ok(mut config_doc) => {
let cfg = toml_edit::de::from_document::<EtcLqos>(config_doc.clone());
match cfg {
Ok(mut cfg) => {
check_config(&mut config_doc, &mut cfg);
Ok(cfg)
}
Err(e) => {
error!("Unable to parse TOML from /etc/lqos.conf");
error!("Full error: {:?}", e);
Err(EtcLqosError::CannotParseToml)
}
}
}
}
} else {
error!("Unable to read contents of /etc/lqos.conf");
@ -156,7 +167,7 @@ impl EtcLqos {
/// Saves changes made to /etc/lqos.conf
/// Copies current configuration into /etc/lqos.conf.backup first
pub fn save(&self) -> Result<(), EtcLqosError> {
pub fn save(&self, document: &mut Document) -> Result<(), EtcLqosError> {
let cfg_path = Path::new("/etc/lqos.conf");
let backup_path = Path::new("/etc/lqos.conf.backup");
if let Err(e) = std::fs::copy(cfg_path, backup_path) {
@ -164,26 +175,17 @@ impl EtcLqos {
log::error!("{e:?}");
return Err(EtcLqosError::BackupFail);
}
let new_cfg = toml::to_string_pretty(&self);
match new_cfg {
Err(e) => {
log::error!("Unable to serialize new /etc/lqos.conf");
log::error!("{e:?}");
return Err(EtcLqosError::SerializeFail);
}
Ok(new_cfg) => {
if let Err(e) = fs::write(cfg_path, new_cfg) {
log::error!("Unable to write to /etc/lqos.conf");
log::error!("{e:?}");
return Err(EtcLqosError::WriteFail);
}
}
let new_cfg = document.to_string();
if let Err(e) = fs::write(cfg_path, new_cfg) {
log::error!("Unable to write to /etc/lqos.conf");
log::error!("{e:?}");
return Err(EtcLqosError::WriteFail);
}
Ok(())
}
}
fn check_config(cfg: &mut EtcLqos) {
fn check_config(cfg_doc: &mut Document, cfg: &mut EtcLqos) {
use sha2::digest::Update;
use sha2::Digest;
@ -191,6 +193,12 @@ fn check_config(cfg: &mut EtcLqos) {
if let Ok(machine_id) = std::fs::read_to_string("/etc/machine-id") {
let hash = sha2::Sha256::new().chain(machine_id).finalize();
cfg.node_id = Some(format!("{:x}", hash));
cfg_doc["node_id"] = value(format!("{:x}", hash));
println!("Updating");
if let Err(e) = cfg.save(cfg_doc) {
log::error!("Unable to save /etc/lqos.conf");
log::error!("{e:?}");
}
}
}
}
@ -212,3 +220,23 @@ pub enum EtcLqosError {
#[error("Unable to write to /etc/lqos.conf")]
WriteFail,
}
#[cfg(test)]
mod test {
const EXAMPLE_LQOS_CONF: &str = include_str!("../../../lqos.example");
#[test]
fn round_trip_toml() {
let doc = EXAMPLE_LQOS_CONF.parse::<toml_edit::Document>().unwrap();
let reserialized = doc.to_string();
assert_eq!(EXAMPLE_LQOS_CONF, reserialized);
}
#[test]
fn add_node_id() {
let mut doc = EXAMPLE_LQOS_CONF.parse::<toml_edit::Document>().unwrap();
doc["node_id"] = toml_edit::value("test");
let reserialized = doc.to_string();
assert!(reserialized.contains("node_id = \"test\""));
}
}