use std::string::String;
use facet::Facet;
use crate::config_value::ConfigValue;
#[derive(Facet, Debug)]
pub struct ConfigFormatError {
pub message: String,
pub offset: Option<usize>,
}
impl ConfigFormatError {
pub fn new(message: impl Into<String>) -> Self {
Self {
message: message.into(),
offset: None,
}
}
pub fn with_offset(message: impl Into<String>, offset: usize) -> Self {
Self {
message: message.into(),
offset: Some(offset),
}
}
}
impl core::fmt::Display for ConfigFormatError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
if let Some(offset) = self.offset {
write!(f, "at byte {}: {}", offset, self.message)
} else {
write!(f, "{}", self.message)
}
}
}
impl core::error::Error for ConfigFormatError {}
pub trait ConfigFormat: Send + Sync {
fn extensions(&self) -> &[&str];
fn parse(&self, contents: &str) -> Result<ConfigValue, ConfigFormatError>;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct JsonFormat;
impl ConfigFormat for JsonFormat {
fn extensions(&self) -> &[&str] {
&["json"]
}
fn parse(&self, contents: &str) -> Result<ConfigValue, ConfigFormatError> {
facet_json::from_str(contents).map_err(|e| ConfigFormatError::new(e.to_string()))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_json_format_extensions() {
let format = JsonFormat;
assert_eq!(format.extensions(), &["json"]);
}
#[test]
fn test_json_format_parse_object() {
let format = JsonFormat;
let result = format.parse(r#"{"port": 8080, "host": "localhost"}"#);
assert!(result.is_ok(), "parse failed: {:?}", result.err());
let value = result.unwrap();
assert!(matches!(value, ConfigValue::Object(_)));
}
#[test]
fn test_json_format_parse_nested() {
let format = JsonFormat;
let result = format.parse(r#"{"smtp": {"host": "mail.example.com", "port": 587}}"#);
assert!(result.is_ok(), "parse failed: {:?}", result.err());
}
#[test]
fn test_json_format_parse_array() {
let format = JsonFormat;
let result = format.parse(r#"["one", "two", "three"]"#);
assert!(result.is_ok(), "parse failed: {:?}", result.err());
let value = result.unwrap();
assert!(matches!(value, ConfigValue::Array(_)));
}
#[test]
fn test_json_format_parse_error() {
let format = JsonFormat;
let result = format.parse(r#"{"port": invalid}"#);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(!err.message.is_empty());
}
#[test]
fn test_config_format_error_display() {
let err = ConfigFormatError::new("something went wrong");
assert_eq!(err.to_string(), "something went wrong");
let err = ConfigFormatError::with_offset("unexpected token", 42);
assert_eq!(err.to_string(), "at byte 42: unexpected token");
}
}