1use std::path::{Path, PathBuf};
2
3use schema_config_toml::ConfigToml;
4use schema_core::common::IndexName;
5use schema_core::{IndexSchema, ParseFrom};
6use schema_index_yaml::SchemaYaml;
7
8use crate::{Config, Index};
9
10#[derive(thiserror::Error, Debug)]
11pub enum LoadError {
12 #[error("failed to read config `{path}`: {source}")]
13 ReadConfig {
14 path: PathBuf,
15 #[source]
16 source: std::io::Error,
17 },
18 #[error("failed to parse config `{path}`: {source}")]
19 ParseConfig {
20 path: PathBuf,
21 #[source]
22 source: schema_config_toml::ParseError,
23 },
24 #[error("failed to read schema `{name}` from `{path}`: {source}")]
25 ReadSchema {
26 name: IndexName,
27 path: PathBuf,
28 #[source]
29 source: std::io::Error,
30 },
31 #[error("failed to parse schema `{name}` from `{path}`: {source}")]
32 ParseSchema {
33 name: IndexName,
34 path: PathBuf,
35 #[source]
36 source: schema_index_yaml::ParseError,
37 },
38 #[error("failed to convert schema `{name}` from `{path}`: {source}")]
39 ConvertSchema {
40 name: IndexName,
41 path: PathBuf,
42 #[source]
43 source: schema_index_yaml::ConversionError,
44 },
45 #[error("duplicate index name `{0}`")]
46 DuplicateIndex(IndexName),
47}
48
49pub fn load(config_path: impl AsRef<Path>) -> Result<Config, LoadError> {
55 let config_path = config_path.as_ref();
56
57 let raw = std::fs::read_to_string(config_path).map_err(|source| LoadError::ReadConfig {
58 path: config_path.to_path_buf(),
59 source,
60 })?;
61
62 let config_toml = ConfigToml::try_parse(&raw).map_err(|source| LoadError::ParseConfig {
63 path: config_path.to_path_buf(),
64 source,
65 })?;
66
67 let indexes = config_toml.index.clone();
68
69 let mut config = Config::from(config_toml);
71
72 let base_dir = config_path.parent().unwrap_or(Path::new("."));
73
74 for entry in indexes {
75 let schema_path = resolve(base_dir, entry.schema.as_path());
76
77 let raw =
78 std::fs::read_to_string(&schema_path).map_err(|source| LoadError::ReadSchema {
79 name: entry.name.clone(),
80 path: schema_path.clone(),
81 source,
82 })?;
83
84 let schema_yaml = SchemaYaml::try_parse(&raw).map_err(|source| LoadError::ParseSchema {
85 name: entry.name.clone(),
86 path: schema_path.clone(),
87 source,
88 })?;
89
90 let schema =
91 IndexSchema::try_from(schema_yaml).map_err(|source| LoadError::ConvertSchema {
92 name: entry.name.clone(),
93 path: schema_path.clone(),
94 source,
95 })?;
96
97 let index = Index {
98 enabled: entry.enabled,
99 schema,
100 on_error: entry.on_error,
101 };
102
103 if config.indexes.insert(entry.name.clone(), index).is_some() {
104 return Err(LoadError::DuplicateIndex(entry.name));
105 }
106 }
107
108 Ok(config)
109}
110
111fn resolve(base_dir: &Path, path: &Path) -> PathBuf {
114 if path.is_absolute() {
115 path.to_path_buf()
116 } else {
117 base_dir.join(path)
118 }
119}