use std::sync::{Mutex, OnceLock};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InstalledProvider {
pub name: String,
pub class_name: String,
pub constructor_param: Option<String>,
}
impl InstalledProvider {
pub fn new(
class_name: impl Into<String>,
constructor_param: Option<String>,
) -> InstalledProvider {
let class_name = class_name.into();
InstalledProvider {
name: provider_name_from_class_name(&class_name),
class_name,
constructor_param,
}
}
}
pub fn provider_name_from_class_name(class_name: &str) -> String {
match class_name.rsplit('.').next() {
Some(simple) if !simple.is_empty() => simple.to_string(),
_ => class_name.to_string(),
}
}
fn registry() -> &'static Mutex<Vec<InstalledProvider>> {
static REGISTRY: OnceLock<Mutex<Vec<InstalledProvider>>> = OnceLock::new();
REGISTRY.get_or_init(|| Mutex::new(Vec::new()))
}
fn lock() -> std::sync::MutexGuard<'static, Vec<InstalledProvider>> {
match registry().lock() {
Ok(guard) => guard,
Err(poisoned) => poisoned.into_inner(),
}
}
pub fn add_provider(provider: InstalledProvider) -> bool {
let mut providers = lock();
if providers.iter().any(|p| p.name == provider.name) {
return false;
}
providers.push(provider);
true
}
pub fn insert_provider_at(provider: InstalledProvider, position: i32) -> i32 {
let mut providers = lock();
if let Some(index) = providers.iter().position(|p| p.name == provider.name) {
providers.remove(index);
}
let position = position.max(1) as usize;
let index = (position - 1).min(providers.len());
providers.insert(index, provider);
(index + 1) as i32
}
#[cfg(test)]
pub fn installed_providers() -> Vec<InstalledProvider> {
lock().clone()
}
#[cfg(test)]
pub fn clear_providers() {
lock().clear();
}
#[cfg(test)]
mod tests {
use super::*;
fn provider(class_name: &str) -> InstalledProvider {
InstalledProvider::new(class_name, None)
}
fn test_lock() -> std::sync::MutexGuard<'static, ()> {
static LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
match LOCK.lock() {
Ok(guard) => guard,
Err(poisoned) => poisoned.into_inner(),
}
}
#[test]
fn the_name_is_the_class_simple_name() {
assert_eq!(
provider_name_from_class_name("org.conscrypt.OpenSSLProvider"),
"OpenSSLProvider"
);
assert_eq!(
provider_name_from_class_name("NoPackageProvider"),
"NoPackageProvider"
);
assert_eq!(provider_name_from_class_name(""), "");
}
#[test]
fn add_appends_and_refuses_a_duplicate_name() {
let _guard = test_lock();
clear_providers();
assert!(add_provider(provider("a.A")));
assert!(add_provider(provider("b.B")));
assert!(!add_provider(provider("c.A")), "same simple name");
let names: Vec<String> = installed_providers().into_iter().map(|p| p.name).collect();
assert_eq!(names, vec!["A", "B"]);
clear_providers();
}
#[test]
fn insert_places_at_a_one_based_position() {
let _guard = test_lock();
clear_providers();
add_provider(provider("a.A"));
add_provider(provider("b.B"));
add_provider(provider("c.C"));
assert_eq!(insert_provider_at(provider("d.D"), 1), 1);
let names: Vec<String> = installed_providers().into_iter().map(|p| p.name).collect();
assert_eq!(names, vec!["D", "A", "B", "C"]);
clear_providers();
}
#[test]
fn insert_clamps_an_out_of_range_position() {
let _guard = test_lock();
clear_providers();
add_provider(provider("a.A"));
assert_eq!(insert_provider_at(provider("b.B"), 0), 1);
assert_eq!(insert_provider_at(provider("c.C"), 99), 3);
let names: Vec<String> = installed_providers().into_iter().map(|p| p.name).collect();
assert_eq!(names, vec!["B", "A", "C"]);
clear_providers();
}
#[test]
fn insert_replaces_an_already_installed_provider() {
let _guard = test_lock();
clear_providers();
add_provider(provider("a.A"));
add_provider(provider("b.B"));
assert_eq!(insert_provider_at(provider("c.A"), 2), 2);
let names: Vec<String> = installed_providers().into_iter().map(|p| p.name).collect();
assert_eq!(names, vec!["B", "A"]);
clear_providers();
}
}