use std::{fs::read_to_string, sync::OnceLock};
use thiserror::Error as ThisError;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, ThisError)]
pub struct Error {
inner: Inner,
}
impl Error {
#[must_use]
pub const fn other(value: String) -> Self {
Self {
inner: Inner::Other(value),
}
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.inner)
}
}
impl<T> From<T> for Error
where
Inner: From<T>,
{
fn from(value: T) -> Self {
Self {
inner: Inner::from(value),
}
}
}
#[derive(Debug, ThisError)]
enum Inner {
#[error("cargo metadata error: {0}")]
CargoMetadata(#[from] cargo_metadata::Error),
#[error("io error: {0}: {1}")]
Io(String, std::io::Error),
#[error("toml error: {0}")]
Toml(#[from] toml::de::Error),
#[error("{0}")]
Other(String),
}
static CONFIG_TABLE: OnceLock<toml::value::Table> = OnceLock::new();
pub fn get() -> Option<&'static toml::value::Table> {
CONFIG_TABLE.get()
}
pub fn try_init_with_metadata(metadata: &cargo_metadata::Metadata) -> Result<Option<String>> {
if CONFIG_TABLE.get().is_some() {
return Ok(None);
}
let cargo_metadata::Metadata { workspace_root, .. } = metadata;
let dylint_toml = workspace_root.join("dylint.toml");
let value = if dylint_toml
.try_exists()
.map_err(|error| Inner::Io(format!("`try_exists` failed for {dylint_toml:?}"), error))?
{
let value = read_to_string(&dylint_toml).map_err(|error| {
Inner::Io(
format!("`read_to_string` failed for {dylint_toml:?}"),
error,
)
})?;
Some(value)
} else {
None
};
if let Some(s) = &value {
init_from_string(s)?;
}
Ok(value)
}
pub fn init_from_string(s: &str) -> Result<()> {
assert!(CONFIG_TABLE.get().is_none());
let toml: toml::Value = toml::from_str(s)?;
let table = toml
.as_table()
.cloned()
.ok_or_else(|| Inner::Other("Value is not a table".into()))?;
CONFIG_TABLE
.set(table)
.unwrap_or_else(|error| panic!("`CONFIG_TABLE` was determined to be unset above: {error}"));
Ok(())
}