use serde::de::{SeqAccess, Visitor};
use serde::{Deserialize, Deserializer};
use std::fmt;
use wildmatch::WildMatch;
#[derive(Debug, Deserialize)]
pub struct Config {
#[serde(default = "default_allow_std")]
pub allow_alloc: bool,
#[serde(default = "default_allow_std")]
pub allow_core: bool,
#[serde(default = "default_allow_std")]
pub allow_std: bool,
#[serde(deserialize_with = "deserialize_vec_wild_match")]
pub allowed_external_types: Vec<WildMatch>,
}
impl Config {
pub fn allows_type(&self, root_crate_name: &str, type_name: &str) -> bool {
let type_crate_name = &type_name[0..type_name.find("::").unwrap_or(type_name.len())];
match type_crate_name {
_ if type_crate_name == root_crate_name => true,
"alloc" => self.allow_alloc,
"core" => self.allow_core,
"std" => self.allow_std,
_ => self
.allowed_external_types
.iter()
.any(|glob| glob.matches(type_name)),
}
}
}
impl Default for Config {
fn default() -> Self {
Self {
allow_alloc: default_allow_std(),
allow_core: default_allow_std(),
allow_std: default_allow_std(),
allowed_external_types: Default::default(),
}
}
}
const fn default_allow_std() -> bool {
true
}
struct VecWildMatchDeserializer;
impl<'de> Visitor<'de> for VecWildMatchDeserializer {
type Value = Vec<WildMatch>;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("list of glob strings")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut result = Vec::new();
while let Some(value) = seq.next_element::<&str>()? {
result.push(WildMatch::new(value));
}
Ok(result)
}
}
fn deserialize_vec_wild_match<'de, D>(de: D) -> Result<Vec<WildMatch>, D::Error>
where
D: Deserializer<'de>,
{
de.deserialize_any(VecWildMatchDeserializer)
}
#[cfg(test)]
mod tests {
use super::Config;
use wildmatch::WildMatch;
#[test]
fn deserialize_config() {
let config = r#"
allow_std = false
allowed_external_types = [
"test::*",
"another_test::something::*::something",
]
"#;
let config: Config = toml::from_str(config).unwrap();
assert!(config.allow_alloc);
assert!(config.allow_core);
assert!(!config.allow_std);
assert!(config.allowed_external_types[0].matches("test::something"));
assert!(!config.allowed_external_types[0].matches("other::something"));
assert!(config.allowed_external_types[1].matches("another_test::something::foo::something"));
assert!(!config.allowed_external_types[1].matches("another_test::other::foo::something"));
}
#[test]
fn test_allows_type() {
let config = Config {
allowed_external_types: vec![WildMatch::new("one::*"), WildMatch::new("two::*")],
..Default::default()
};
assert!(config.allows_type("root", "alloc::System"));
assert!(config.allows_type("root", "std::vec::Vec"));
assert!(config.allows_type("root", "std::path::Path"));
assert!(config.allows_type("root", "root::thing"));
assert!(config.allows_type("root", "one::thing"));
assert!(config.allows_type("root", "two::thing"));
assert!(!config.allows_type("root", "three::thing"));
}
}