use crate::{env, Path};
const CONFIG_FILE_NAME: &str = "config.toml";
#[derive(Debug, thiserror::Error)]
pub enum ConfigPathError {
#[error("config file does not exist: {0}")]
PathNotFound(Box<Path>),
#[error(
"cannot determine the user config directory: neither XDG_CONFIG_HOME nor HOME is available"
)]
HomeUnavailable,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConfigSource {
Explicit,
Home,
Default,
}
#[derive(Debug, Clone)]
pub struct ConfigLocation {
path: Box<Path>,
source: ConfigSource,
}
impl ConfigLocation {
pub fn find(
explicit: Option<&(impl AsRef<Path> + ?Sized)>,
) -> Result<Self, ConfigPathError> {
if let Some(path) = explicit {
let path = path.as_ref();
if !path.is_file() {
return Err(ConfigPathError::PathNotFound(Box::from(path)));
}
return Ok(Self {
path: path.into(),
source: ConfigSource::Explicit,
});
}
if let Some(path) = env::var_os("XDG_CONFIG_HOME")
.filter(|val| !val.is_empty())
.as_deref()
.map(Path::new)
.filter(|path| path.is_absolute())
.map(|path| path.join(crate::NAMESPACE).join(CONFIG_FILE_NAME))
&& path.is_file()
{
return Ok(Self {
path: path.into(),
source: ConfigSource::Home,
});
}
match env::var_os("HOME")
.filter(|val| !val.is_empty())
.as_deref()
.map(Path::new)
{
Some(home) => {
let path = home
.join(".config")
.join(crate::NAMESPACE)
.join(CONFIG_FILE_NAME);
if path.is_file() {
return Ok(Self {
path: path.into(),
source: ConfigSource::Home,
});
}
Ok(Self {
path: path.into(),
source: ConfigSource::Default,
})
},
None => Err(ConfigPathError::HomeUnavailable),
}
}
pub fn path(&self) -> &Path {
self.path.as_ref()
}
pub fn source(&self) -> ConfigSource {
self.source
}
}