use std::path::PathBuf;
use config::{ConfigError, File};
use sozu_command_lib::config::{Config, ConfigBuilder, ConfigError as ConfigBuilderError};
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("failed to load configuration from path '{0}', {1}")]
Build(String, ConfigError),
#[error("failed to deserialize configuration keys-values into internal structure, {0}")]
Deserialize(ConfigError),
#[error("failed to convert configuration into internal representation structure, {0}")]
Convert(ConfigBuilderError),
#[error("failed to convert path to utf-8 string, there is incompatibility, {0}")]
PathIsInvalid(String),
#[error("failed to canonicalize the command socket path, {0}")]
Canonicalize(std::io::Error),
}
#[tracing::instrument]
pub fn try_from(path: &PathBuf) -> Result<Config, Error> {
let file_config = config::Config::builder()
.add_source(File::from(path.as_path()).required(true))
.build()
.map_err(|err| Error::Build(path.display().to_string(), err))?
.try_deserialize()
.map_err(Error::Deserialize)?;
let config_path = path
.to_str()
.ok_or_else(|| Error::PathIsInvalid(path.display().to_string()))?;
ConfigBuilder::new(file_config, config_path)
.into_config()
.map_err(Error::Convert)
}
#[tracing::instrument(skip_all)]
pub fn canonicalize_command_socket(path: &PathBuf, config: &Config) -> Result<PathBuf, Error> {
match &config.command_socket {
socket if socket.starts_with('/') => Ok(PathBuf::from(socket)),
socket => {
let mut socket_path = PathBuf::from(socket)
.parent()
.ok_or_else(|| Error::PathIsInvalid(path.display().to_string()))?
.to_owned();
socket_path.push(path);
socket_path.canonicalize().map_err(Error::Canonicalize)
}
}
}