Line data Source code
1 : use std::{fs::read_to_string, sync::OnceLock}; 2 : use thiserror::Error as ThisError; 3 : 4 : pub type Result<T> = std::result::Result<T, Error>; 5 : 6 : #[derive(Debug, ThisError)] 7 : pub struct Error { 8 : inner: Inner, 9 : } 10 : 11 : impl Error { 12 : #[must_use] 13 0 : pub const fn other(value: String) -> Self { 14 0 : Self { 15 0 : inner: Inner::Other(value), 16 0 : } 17 0 : } 18 : } 19 : 20 : impl std::fmt::Display for Error { 21 0 : fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 22 0 : write!(f, "{}", self.inner) 23 0 : } 24 : } 25 : 26 : impl<T> From<T> for Error 27 : where 28 : Inner: From<T>, 29 : { 30 0 : fn from(value: T) -> Self { 31 0 : Self { 32 0 : inner: Inner::from(value), 33 0 : } 34 0 : } 35 : } 36 : 37 : #[derive(Debug, ThisError)] 38 : enum Inner { 39 : #[error("cargo metadata error: {0}")] 40 : CargoMetadata(#[from] cargo_metadata::Error), 41 : #[error("io error: {0}: {1}")] 42 : Io(String, std::io::Error), 43 : #[error("toml error: {0}")] 44 : Toml(#[from] toml::de::Error), 45 : #[error("{0}")] 46 : Other(String), 47 : } 48 : 49 : static CONFIG_TABLE: OnceLock<toml::value::Table> = OnceLock::new(); 50 : 51 24 : pub fn get() -> Option<&'static toml::value::Table> { 52 24 : CONFIG_TABLE.get() 53 24 : } 54 : 55 : // smoelius: `try_init_with_metadata` returns a string so that `dylint_linting` can record it in 56 : // `file_depinfo`. 57 24 : pub fn try_init_with_metadata(metadata: &cargo_metadata::Metadata) -> Result<Option<String>> { 58 24 : if CONFIG_TABLE.get().is_some() { 59 0 : return Ok(None); 60 24 : } 61 24 : 62 24 : let cargo_metadata::Metadata { workspace_root, .. } = metadata; 63 24 : 64 24 : let dylint_toml = workspace_root.join("dylint.toml"); 65 : 66 24 : let value = if dylint_toml 67 24 : .try_exists() 68 24 : .map_err(|error| Inner::Io(format!("`try_exists` failed for {dylint_toml:?}"), error))? 69 : { 70 1 : let value = read_to_string(&dylint_toml).map_err(|error| { 71 0 : Inner::Io( 72 0 : format!("`read_to_string` failed for {dylint_toml:?}"), 73 0 : error, 74 0 : ) 75 1 : })?; 76 1 : Some(value) 77 : } else { 78 23 : None 79 : }; 80 : 81 24 : if let Some(s) = &value { 82 1 : init_from_string(s)?; 83 23 : } 84 : 85 24 : Ok(value) 86 24 : } 87 : 88 1 : pub fn init_from_string(s: &str) -> Result<()> { 89 1 : assert!(CONFIG_TABLE.get().is_none()); 90 : 91 1 : let toml: toml::Value = toml::from_str(s)?; 92 : 93 1 : let table = toml 94 1 : .as_table() 95 1 : .cloned() 96 1 : .ok_or_else(|| Inner::Other("Value is not a table".into()))?; 97 : 98 : // smoelius: Rewrite this function (`init_from_string`) and eliminate the next `expect` once 99 : // `get_or_try_init` stabilizes: https://github.com/rust-lang/rust/issues/109737 100 1 : CONFIG_TABLE 101 1 : .set(table) 102 1 : .expect("`CONFIG_TABLE` was determined to be unset above"); 103 1 : 104 1 : Ok(()) 105 1 : }