use std::{fs, path::Path};
use tide_disco::api::{Api, ApiError};
use toml::{map::Entry, Value};
use vbs::version::StaticVersionType;
pub(crate) fn load_api<State: 'static, Error: 'static, Ver: StaticVersionType + 'static>(
path: Option<impl AsRef<Path>>,
default: &str,
extensions: impl IntoIterator<Item = Value>,
) -> Result<Api<State, Error, Ver>, ApiError> {
let mut toml = match path {
Some(path) => load_toml(path.as_ref())?,
None => toml::from_str(default).map_err(|err| ApiError::CannotReadToml {
reason: err.to_string(),
})?,
};
for extension in extensions {
merge_toml(&mut toml, extension);
}
Api::new(toml)
}
fn merge_toml(into: &mut Value, from: Value) {
if let (Value::Table(into), Value::Table(from)) = (into, from) {
for (key, value) in from {
match into.entry(key) {
Entry::Occupied(mut entry) => merge_toml(entry.get_mut(), value),
Entry::Vacant(entry) => {
entry.insert(value);
}
}
}
}
}
fn load_toml(path: &Path) -> Result<Value, ApiError> {
let bytes = fs::read(path).map_err(|err| ApiError::CannotReadToml {
reason: err.to_string(),
})?;
let string = std::str::from_utf8(&bytes).map_err(|err| ApiError::CannotReadToml {
reason: err.to_string(),
})?;
toml::from_str(string).map_err(|err| ApiError::CannotReadToml {
reason: err.to_string(),
})
}