improved error handling
All checks were successful
Version Check / check-version (pull_request) Successful in 3s

This commit is contained in:
2026-03-18 17:19:09 +01:00
parent 5056f8dd0a
commit 277d8aa151
7 changed files with 37 additions and 58 deletions

View File

@@ -1,12 +1,12 @@
use super::{ConfigItem, FormatHandler, ItemStatus, ValueType};
use std::fs;
use std::io::{self, Write};
use std::io::Write;
use std::path::Path;
pub struct EnvHandler;
impl FormatHandler for EnvHandler {
fn parse(&self, path: &Path) -> io::Result<Vec<ConfigItem>> {
fn parse(&self, path: &Path) -> anyhow::Result<Vec<ConfigItem>> {
let content = fs::read_to_string(path)?;
let mut vars = Vec::new();
@@ -35,7 +35,7 @@ impl FormatHandler for EnvHandler {
Ok(vars)
}
fn write(&self, path: &Path, vars: &[ConfigItem]) -> io::Result<()> {
fn write(&self, path: &Path, vars: &[ConfigItem]) -> anyhow::Result<()> {
let mut file = fs::File::create(path)?;
for var in vars {
if !var.is_group {

View File

@@ -1,7 +1,6 @@
use super::{ConfigItem, FormatHandler, FormatType, ItemStatus, ValueType};
use serde_json::{Map, Value};
use std::fs;
use std::io;
use std::path::Path;
pub struct HierarchicalHandler {
@@ -13,50 +12,40 @@ impl HierarchicalHandler {
Self { format_type }
}
fn read_value(&self, path: &Path) -> io::Result<Value> {
fn read_value(&self, path: &Path) -> anyhow::Result<Value> {
let content = fs::read_to_string(path)?;
let value = match self.format_type {
FormatType::Json => serde_json::from_str(&content)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?,
FormatType::Yaml => serde_yaml::from_str(&content)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?,
FormatType::Toml => toml::from_str(&content)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?,
FormatType::Xml => xml_to_json(&content)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?,
FormatType::Json => serde_json::from_str(&content)?,
FormatType::Yaml => serde_yaml::from_str(&content)?,
FormatType::Toml => toml::from_str(&content)?,
FormatType::Xml => xml_to_json(&content)?,
_ => unreachable!(),
};
Ok(value)
}
fn write_value(&self, path: &Path, value: &Value) -> io::Result<()> {
fn write_value(&self, path: &Path, value: &Value) -> anyhow::Result<()> {
let content = match self.format_type {
FormatType::Json => serde_json::to_string_pretty(value)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?,
FormatType::Yaml => serde_yaml::to_string(value)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?,
FormatType::Json => serde_json::to_string_pretty(value)?,
FormatType::Yaml => serde_yaml::to_string(value)?,
FormatType::Toml => {
// toml requires the root to be a table
if value.is_object() {
let toml_value: toml::Value = serde_json::from_value(value.clone())
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
toml::to_string_pretty(&toml_value)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?
let toml_value: toml::Value = serde_json::from_value(value.clone())?;
toml::to_string_pretty(&toml_value)?
} else {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Root of TOML must be an object",
));
anyhow::bail!("Root of TOML must be an object");
}
}
FormatType::Xml => json_to_xml(value),
_ => unreachable!(),
};
fs::write(path, content)
fs::write(path, content)?;
Ok(())
}
}
fn xml_to_json(content: &str) -> io::Result<Value> {
fn xml_to_json(content: &str) -> anyhow::Result<Value> {
use quick_xml::reader::Reader;
use quick_xml::events::Event;
@@ -64,7 +53,7 @@ fn xml_to_json(content: &str) -> io::Result<Value> {
reader.config_mut().trim_text(true);
let mut buf = Vec::new();
fn parse_recursive(reader: &mut Reader<&[u8]>) -> io::Result<Value> {
fn parse_recursive(reader: &mut Reader<&[u8]>) -> anyhow::Result<Value> {
let mut map = Map::new();
let mut text = String::new();
let mut buf = Vec::new();
@@ -276,14 +265,14 @@ fn flatten(value: &Value, prefix: &str, depth: usize, key_name: &str, vars: &mut
}
impl FormatHandler for HierarchicalHandler {
fn parse(&self, path: &Path) -> io::Result<Vec<ConfigItem>> {
fn parse(&self, path: &Path) -> anyhow::Result<Vec<ConfigItem>> {
let value = self.read_value(path)?;
let mut vars = Vec::new();
flatten(&value, "", 0, "", &mut vars);
Ok(vars)
}
fn write(&self, path: &Path, vars: &[ConfigItem]) -> io::Result<()> {
fn write(&self, path: &Path, vars: &[ConfigItem]) -> anyhow::Result<()> {
let mut root = Value::Object(Map::new());
for var in vars {
if !var.is_group {

View File

@@ -1,14 +1,12 @@
use super::{ConfigItem, FormatHandler, ItemStatus, ValueType};
use ini::Ini;
use std::io;
use std::path::Path;
pub struct IniHandler;
impl FormatHandler for IniHandler {
fn parse(&self, path: &Path) -> io::Result<Vec<ConfigItem>> {
let conf = Ini::load_from_file(path)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
fn parse(&self, path: &Path) -> anyhow::Result<Vec<ConfigItem>> {
let conf = Ini::load_from_file(path)?;
let mut vars = Vec::new();
for (section, prop) in &conf {
@@ -52,7 +50,7 @@ impl FormatHandler for IniHandler {
Ok(vars)
}
fn write(&self, path: &Path, vars: &[ConfigItem]) -> io::Result<()> {
fn write(&self, path: &Path, vars: &[ConfigItem]) -> anyhow::Result<()> {
let mut conf = Ini::new();
for var in vars {
if !var.is_group {
@@ -67,8 +65,8 @@ impl FormatHandler for IniHandler {
}
}
}
conf.write_to_file(path)
.map_err(io::Error::other)
conf.write_to_file(path)?;
Ok(())
}
}

View File

@@ -1,4 +1,3 @@
use std::io;
use std::path::Path;
pub mod env;
@@ -46,8 +45,8 @@ pub enum FormatType {
}
pub trait FormatHandler {
fn parse(&self, path: &Path) -> io::Result<Vec<ConfigItem>>;
fn merge(&self, path: &Path, vars: &mut Vec<ConfigItem>) -> io::Result<()> {
fn parse(&self, path: &Path) -> anyhow::Result<Vec<ConfigItem>>;
fn merge(&self, path: &Path, vars: &mut Vec<ConfigItem>) -> anyhow::Result<()> {
if !path.exists() {
return Ok(());
}
@@ -82,7 +81,7 @@ pub trait FormatHandler {
Ok(())
}
fn write(&self, path: &Path, vars: &[ConfigItem]) -> io::Result<()>;
fn write(&self, path: &Path, vars: &[ConfigItem]) -> anyhow::Result<()>;
}
pub fn detect_format(path: &Path, override_format: Option<String>) -> FormatType {

View File

@@ -1,13 +1,13 @@
use super::{ConfigItem, FormatHandler, ItemStatus, ValueType};
use java_properties::{LineContent, PropertiesIter, PropertiesWriter};
use std::fs::File;
use std::io::{self, BufReader, BufWriter};
use std::io::{BufReader, BufWriter};
use std::path::Path;
pub struct PropertiesHandler;
impl FormatHandler for PropertiesHandler {
fn parse(&self, path: &Path) -> io::Result<Vec<ConfigItem>> {
fn parse(&self, path: &Path) -> anyhow::Result<Vec<ConfigItem>> {
let file = File::open(path)?;
let reader = BufReader::new(file);
let iter = PropertiesIter::new(reader);
@@ -16,7 +16,7 @@ impl FormatHandler for PropertiesHandler {
let mut groups = std::collections::HashSet::new();
for line_result in iter {
let line = line_result.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
let line = line_result?;
if let LineContent::KVPair(path, value) = line.consume_content() {
// Add groups based on dot notation
@@ -62,7 +62,7 @@ impl FormatHandler for PropertiesHandler {
Ok(vars)
}
fn write(&self, path: &Path, vars: &[ConfigItem]) -> io::Result<()> {
fn write(&self, path: &Path, vars: &[ConfigItem]) -> anyhow::Result<()> {
let file = File::create(path)?;
let writer = BufWriter::new(file);
let mut prop_writer = PropertiesWriter::new(writer);
@@ -72,12 +72,12 @@ impl FormatHandler for PropertiesHandler {
let val = var.value.as_deref()
.or(var.template_value.as_deref())
.unwrap_or("");
prop_writer.write(&var.path, val)
.map_err(io::Error::other)?;
prop_writer.write(&var.path, val)?;
}
}
prop_writer.finish().map_err(io::Error::other)
prop_writer.finish()?;
Ok(())
}
}