// SPDX-FileCopyrightText: 2024 Ɓukasz Niemier <#@hauleth.dev> // // SPDX-License-Identifier: EUPL-1.2 use std::fmt; use std::path::Path; use serde::{de, Deserialize}; use wildmatch::WildMatch; /// Patterns that can be matched against files #[derive(Debug, Deserialize)] #[serde(rename_all = "snake_case")] pub enum Pattern { #[serde(deserialize_with = "deserialize_wildcard")] Wildcard(WildMatch), #[serde(deserialize_with = "deserialize_regex")] Regex(regex::Regex), Exact(Box), } impl Pattern { pub fn matches(&self, path: &Path) -> bool { match *self { Self::Wildcard(ref pattern) => pattern.matches(path.to_str().unwrap()), Self::Regex(ref pattern) => pattern.is_match(path.to_str().unwrap()), Self::Exact(ref pattern) => **pattern == *path, } } } struct RegexVisitor; impl<'de> de::Visitor<'de> for RegexVisitor { type Value = regex::Regex; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { write!(formatter, "expected regex string") } fn visit_str(self, s: &str) -> Result where E: de::Error, { match regex::Regex::new(s) { Ok(regex) => Ok(regex), Err(regex::Error::Syntax(ref desc)) => { Err(E::invalid_value(de::Unexpected::Str(s), &desc.as_str())) } Err(_) => unreachable!(), } } } fn deserialize_regex<'de, D>(des: D) -> Result where D: serde::Deserializer<'de>, { des.deserialize_str(RegexVisitor) } struct WildcardVisitor; impl<'de> de::Visitor<'de> for WildcardVisitor { type Value = WildMatch; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { write!(formatter, "expected regex string") } fn visit_str(self, s: &str) -> Result where E: de::Error, { Ok(WildMatch::new(s)) } } fn deserialize_wildcard<'de, D>(des: D) -> Result where D: serde::Deserializer<'de>, { des.deserialize_str(WildcardVisitor) }