Skip to main content

codex_patcher/config/
loader.rs

1use crate::config::schema::{PatchConfig, ValidationError};
2use std::fmt;
3use std::fs;
4use std::path::{Path, PathBuf};
5
6#[derive(Debug)]
7pub enum ConfigError {
8    Io {
9        path: PathBuf,
10        source: std::io::Error,
11    },
12    Toml {
13        path: Option<PathBuf>,
14        source: toml_edit::de::Error,
15    },
16    Validation {
17        path: Option<PathBuf>,
18        source: ValidationError,
19    },
20}
21
22impl ConfigError {
23    fn with_path(self, path: &Path) -> Self {
24        let path = path.to_path_buf();
25        match self {
26            ConfigError::Io { .. } => self,
27            ConfigError::Toml { path: None, source } => ConfigError::Toml {
28                path: Some(path),
29                source,
30            },
31            ConfigError::Validation { path: None, source } => ConfigError::Validation {
32                path: Some(path),
33                source,
34            },
35            other => other,
36        }
37    }
38}
39
40impl fmt::Display for ConfigError {
41    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42        match self {
43            ConfigError::Io { path, source } => {
44                write!(
45                    f,
46                    "failed to read patch config from {}: {}",
47                    path.display(),
48                    source
49                )
50            }
51            ConfigError::Toml { path, source } => match path {
52                Some(path) => write!(
53                    f,
54                    "failed to parse patch config TOML ({}): {}",
55                    path.display(),
56                    source
57                ),
58                None => write!(f, "failed to parse patch config TOML: {}", source),
59            },
60            ConfigError::Validation { path, source } => match path {
61                Some(path) => write!(f, "invalid patch config ({}): {}", path.display(), source),
62                None => write!(f, "invalid patch config: {}", source),
63            },
64        }
65    }
66}
67
68impl std::error::Error for ConfigError {
69    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
70        match self {
71            ConfigError::Io { source, .. } => Some(source),
72            ConfigError::Toml { source, .. } => Some(source),
73            ConfigError::Validation { source, .. } => Some(source),
74        }
75    }
76}
77
78pub fn load_from_str(input: &str) -> Result<PatchConfig, ConfigError> {
79    let config: PatchConfig = toml_edit::de::from_str(input)
80        .map_err(|source| ConfigError::Toml { path: None, source })?;
81    config
82        .validate()
83        .map_err(|source| ConfigError::Validation { path: None, source })?;
84    Ok(config)
85}
86
87pub fn load_from_path(path: impl AsRef<Path>) -> Result<PatchConfig, ConfigError> {
88    let path = path.as_ref();
89    let contents = fs::read_to_string(path).map_err(|source| ConfigError::Io {
90        path: path.to_path_buf(),
91        source,
92    })?;
93    load_from_str(&contents).map_err(|error| error.with_path(path))
94}