From 277d8aa15141dc30b31445fb69187c92ea1dd9c4 Mon Sep 17 00:00:00 2001 From: Nils Pukropp Date: Wed, 18 Mar 2026 17:19:09 +0100 Subject: [PATCH] improved error handling --- src/error.rs | 6 +---- src/format/env.rs | 6 ++--- src/format/hierarchical.rs | 45 ++++++++++++++------------------------ src/format/ini.rs | 12 +++++----- src/format/mod.rs | 7 +++--- src/format/properties.rs | 14 ++++++------ src/main.rs | 5 +---- 7 files changed, 37 insertions(+), 58 deletions(-) diff --git a/src/error.rs b/src/error.rs index 1ee2036..d4b70b0 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,14 +1,10 @@ -use std::io; use thiserror::Error; /// Custom error types for the mould application. #[derive(Error, Debug)] pub enum MouldError { #[error("IO error: {0}")] - Io(#[from] io::Error), - - #[error("Format error: {0}")] - Format(String), + Io(#[from] std::io::Error), #[error("File not found: {0}")] FileNotFound(String), diff --git a/src/format/env.rs b/src/format/env.rs index 924c3e7..ad2b534 100644 --- a/src/format/env.rs +++ b/src/format/env.rs @@ -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> { + fn parse(&self, path: &Path) -> anyhow::Result> { 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 { diff --git a/src/format/hierarchical.rs b/src/format/hierarchical.rs index b642bfd..c6b7464 100644 --- a/src/format/hierarchical.rs +++ b/src/format/hierarchical.rs @@ -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 { + fn read_value(&self, path: &Path) -> anyhow::Result { 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 { +fn xml_to_json(content: &str) -> anyhow::Result { use quick_xml::reader::Reader; use quick_xml::events::Event; @@ -64,7 +53,7 @@ fn xml_to_json(content: &str) -> io::Result { reader.config_mut().trim_text(true); let mut buf = Vec::new(); - fn parse_recursive(reader: &mut Reader<&[u8]>) -> io::Result { + fn parse_recursive(reader: &mut Reader<&[u8]>) -> anyhow::Result { 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> { + fn parse(&self, path: &Path) -> anyhow::Result> { 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 { diff --git a/src/format/ini.rs b/src/format/ini.rs index 2582739..29583d1 100644 --- a/src/format/ini.rs +++ b/src/format/ini.rs @@ -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> { - let conf = Ini::load_from_file(path) - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + fn parse(&self, path: &Path) -> anyhow::Result> { + 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(()) } } diff --git a/src/format/mod.rs b/src/format/mod.rs index ed5f0e2..a76c706 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -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>; - fn merge(&self, path: &Path, vars: &mut Vec) -> io::Result<()> { + fn parse(&self, path: &Path) -> anyhow::Result>; + fn merge(&self, path: &Path, vars: &mut Vec) -> 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) -> FormatType { diff --git a/src/format/properties.rs b/src/format/properties.rs index 642d657..a53567d 100644 --- a/src/format/properties.rs +++ b/src/format/properties.rs @@ -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> { + fn parse(&self, path: &Path) -> anyhow::Result> { 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(()) } } diff --git a/src/main.rs b/src/main.rs index f175ac4..d01abd6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -96,10 +96,7 @@ fn main() -> anyhow::Result<()> { } } else if vars.is_empty() { // Fallback if no template and active is empty - vars = handler.parse(&input_path).map_err(|e| { - error!("Failed to parse input file: {}", e); - MouldError::Format(format!("Failed to parse {}: {}", input_path.display(), e)) - })?; + vars = handler.parse(&input_path)?; } if vars.is_empty() {