kellnr_settings/
filter.rs1#![allow(clippy::struct_field_names)]
3
4use std::fmt::Display;
5
6use clap::ValueEnum;
7use provcfg::{ClapArgs, Configurable};
8use serde::{Deserialize, Deserializer, Serialize};
9use utoipa::ToSchema;
10
11#[derive(
16 Debug, Default, Deserialize, Serialize, Eq, PartialEq, Clone, Configurable, ClapArgs, ToSchema,
17)]
18#[serde(default)]
19#[configurable(clap_prefix = "cratesio-filter")]
20pub struct CratesioFilter {
21 pub license_enabled: bool,
23
24 #[configurable(env_list)]
26 #[arg(value_delimiter = ',')]
27 pub license_allowed: Vec<String>,
28
29 #[arg(value_enum)]
32 pub license_unknown: LicenseUnknown,
33
34 #[configurable(env_list)]
36 #[arg(value_delimiter = ',')]
37 pub license_exempt_crates: Vec<String>,
38}
39
40#[derive(
44 Debug, Default, Deserialize, Serialize, Eq, PartialEq, Clone, Configurable, ClapArgs, ToSchema,
45)]
46#[serde(default)]
47#[configurable(clap_prefix = "registry-filter")]
48pub struct RegistryFilter {
49 pub license_enabled: bool,
51
52 #[configurable(env_list)]
54 #[arg(value_delimiter = ',')]
55 pub license_allowed: Vec<String>,
56
57 #[arg(value_enum)]
60 pub license_unknown: LicenseUnknown,
61
62 #[configurable(env_list)]
64 #[arg(value_delimiter = ',')]
65 pub license_exempt_crates: Vec<String>,
66}
67
68#[derive(Debug, PartialEq, Eq, Clone, Copy, ValueEnum, Default, ToSchema)]
71#[schema(rename_all = "lowercase")]
72pub enum LicenseUnknown {
73 #[default]
74 Deny,
75 Allow,
76}
77
78impl<'de> Deserialize<'de> for LicenseUnknown {
79 fn deserialize<D>(de: D) -> Result<Self, D::Error>
80 where
81 D: Deserializer<'de>,
82 {
83 match String::deserialize(de)?.to_lowercase().as_str() {
84 "deny" => Ok(Self::Deny),
85 "allow" => Ok(Self::Allow),
86 other => Err(serde::de::Error::custom(format!(
87 "unknown license_unknown value {other:?}; expected \"deny\" or \"allow\""
88 ))),
89 }
90 }
91}
92
93impl Serialize for LicenseUnknown {
94 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
95 where
96 S: serde::Serializer,
97 {
98 serializer.serialize_str(&self.to_string())
99 }
100}
101
102impl Display for LicenseUnknown {
103 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104 match self {
105 LicenseUnknown::Deny => write!(f, "deny"),
106 LicenseUnknown::Allow => write!(f, "allow"),
107 }
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use provcfg::Config;
114
115 use crate::settings::SettingsProv;
116 use crate::{CratesioFilter, LicenseUnknown, RegistryFilter, Settings};
117
118 fn from_toml(toml: &str) -> Result<Settings, provcfg::Error> {
119 Config::new()
120 .add_toml_str("test.toml", toml)
121 .build::<SettingsProv>()
122 .map(|prov| Settings::from(&prov))
123 }
124
125 #[test]
126 fn filters_are_disabled_by_default() {
127 let settings = Settings::default();
128
129 assert_eq!(CratesioFilter::default(), settings.cratesio_filter);
130 assert_eq!(RegistryFilter::default(), settings.registry_filter);
131 assert!(!settings.cratesio_filter.license_enabled);
132 assert!(settings.cratesio_filter.license_allowed.is_empty());
133 assert_eq!(
134 LicenseUnknown::Deny,
135 settings.cratesio_filter.license_unknown
136 );
137 assert!(settings.cratesio_filter.license_exempt_crates.is_empty());
138 }
139
140 #[test]
141 fn both_sections_read_from_toml() {
142 let settings = from_toml(
143 r#"
144 [cratesio_filter]
145 license_enabled = true
146 license_allowed = ["MIT", "Apache-2.0"]
147 license_unknown = "allow"
148 license_exempt_crates = ["ring"]
149
150 [registry_filter]
151 license_enabled = true
152 license_allowed = ["MIT"]
153 "#,
154 )
155 .unwrap();
156
157 assert_eq!(
158 CratesioFilter {
159 license_enabled: true,
160 license_allowed: vec!["MIT".to_string(), "Apache-2.0".to_string()],
161 license_unknown: LicenseUnknown::Allow,
162 license_exempt_crates: vec!["ring".to_string()],
163 },
164 settings.cratesio_filter
165 );
166 assert_eq!(
167 RegistryFilter {
168 license_enabled: true,
169 license_allowed: vec!["MIT".to_string()],
170 license_unknown: LicenseUnknown::Deny,
171 license_exempt_crates: vec![],
172 },
173 settings.registry_filter
174 );
175 }
176
177 #[test]
178 fn invalid_license_unknown_fails() {
179 let result = from_toml("[cratesio_filter]\nlicense_unknown = \"deneay\"\n");
180
181 assert!(result.is_err());
182 }
183
184 #[test]
185 fn list_settings_are_env_list_keys() {
186 let keys = crate::settings::env_list_keys();
187
188 for key in [
189 "cratesio_filter.license_allowed",
190 "cratesio_filter.license_exempt_crates",
191 "registry_filter.license_allowed",
192 "registry_filter.license_exempt_crates",
193 ] {
194 assert!(keys.contains(&key), "{key} must be an env list key");
195 }
196 }
197}