use crate::dependency::Dependency;
use lazy_static::lazy_static;
use std::sync::RwLock;
pub use upstream_ontologist::UpstreamMetadata;
pub type UpstreamProvider = Box<dyn Fn(&dyn Dependency) -> Option<UpstreamMetadata> + Send + Sync>;
lazy_static! {
static ref CUSTOM_PROVIDERS: RwLock<Vec<UpstreamProvider>> = RwLock::new(Vec::new());
}
pub fn register_upstream_provider<F>(provider: F)
where
F: Fn(&dyn Dependency) -> Option<UpstreamMetadata> + Send + Sync + 'static,
{
CUSTOM_PROVIDERS.write().unwrap().push(Box::new(provider));
}
pub fn clear_custom_providers() {
CUSTOM_PROVIDERS.write().unwrap().clear();
}
pub trait FindUpstream: Dependency {
fn find_upstream(&self) -> Option<UpstreamMetadata>;
}
pub fn find_upstream(dependency: &dyn Dependency) -> Option<UpstreamMetadata> {
for provider in CUSTOM_PROVIDERS.read().unwrap().iter() {
if let Some(metadata) = provider(dependency) {
return Some(metadata);
}
}
#[cfg(feature = "debian")]
if let Some(dep) = dependency
.as_any()
.downcast_ref::<crate::dependencies::debian::DebianDependency>()
{
return dep.find_upstream();
}
if let Some(dep) = dependency
.as_any()
.downcast_ref::<crate::dependencies::python::PythonPackageDependency>()
{
return dep.find_upstream();
}
if let Some(dep) = dependency
.as_any()
.downcast_ref::<crate::dependencies::RubyGemDependency>()
{
return dep.find_upstream();
}
if let Some(dep) = dependency
.as_any()
.downcast_ref::<crate::dependencies::node::NodePackageDependency>()
{
return dep.find_upstream();
}
if let Some(dep) = dependency
.as_any()
.downcast_ref::<crate::dependencies::CargoCrateDependency>()
{
return dep.find_upstream();
}
if let Some(dep) = dependency
.as_any()
.downcast_ref::<crate::dependencies::go::GoPackageDependency>()
{
return dep.find_upstream();
}
if let Some(dep) = dependency
.as_any()
.downcast_ref::<crate::dependencies::perl::PerlModuleDependency>()
{
return dep.find_upstream();
}
if let Some(dep) = dependency
.as_any()
.downcast_ref::<crate::dependencies::haskell::HaskellPackageDependency>()
{
return dep.find_upstream();
}
None
}
#[cfg(test)]
mod tests {
use super::*;
use std::any::Any;
use std::sync::Mutex;
lazy_static! {
static ref TEST_LOCK: Mutex<()> = Mutex::new(());
}
#[derive(Debug)]
struct TestDependency {
#[allow(dead_code)]
name: String,
}
impl Dependency for TestDependency {
fn family(&self) -> &'static str {
"test"
}
fn present(&self, _session: &dyn crate::session::Session) -> bool {
false
}
fn project_present(&self, _session: &dyn crate::session::Session) -> bool {
false
}
fn as_any(&self) -> &dyn Any {
self
}
}
#[test]
fn test_register_custom_provider() {
let _lock = TEST_LOCK.lock().unwrap();
clear_custom_providers();
let test_dep = TestDependency {
name: "test-package".to_string(),
};
let initial_result = find_upstream(&test_dep);
assert!(
initial_result.is_none(),
"Expected no metadata initially, but found: {:?}",
initial_result
);
register_upstream_provider(|dep| {
if dep.family() == "test" {
let mut metadata = UpstreamMetadata::default();
metadata.insert(upstream_ontologist::UpstreamDatumWithMetadata {
datum: upstream_ontologist::UpstreamDatum::Repository(
"https://github.com/test/repo".to_string(),
),
certainty: Some(upstream_ontologist::Certainty::Certain),
origin: None,
});
Some(metadata)
} else {
None
}
});
let metadata = find_upstream(&test_dep).unwrap();
assert_eq!(metadata.repository(), Some("https://github.com/test/repo"));
clear_custom_providers();
}
#[test]
fn test_multiple_custom_providers() {
let _lock = TEST_LOCK.lock().unwrap();
clear_custom_providers();
let test_dep = TestDependency {
name: "special-package".to_string(),
};
register_upstream_provider(|dep| {
if dep.family() == "other" {
let mut metadata = UpstreamMetadata::default();
metadata.insert(upstream_ontologist::UpstreamDatumWithMetadata {
datum: upstream_ontologist::UpstreamDatum::Repository(
"https://example.com/wrong".to_string(),
),
certainty: Some(upstream_ontologist::Certainty::Certain),
origin: None,
});
Some(metadata)
} else {
None
}
});
register_upstream_provider(|dep| {
if dep.family() == "test" {
let mut metadata = UpstreamMetadata::default();
metadata.insert(upstream_ontologist::UpstreamDatumWithMetadata {
datum: upstream_ontologist::UpstreamDatum::Repository(
"https://example.com/correct".to_string(),
),
certainty: Some(upstream_ontologist::Certainty::Certain),
origin: None,
});
Some(metadata)
} else {
None
}
});
let metadata = find_upstream(&test_dep).unwrap();
assert_eq!(metadata.repository(), Some("https://example.com/correct"));
clear_custom_providers();
}
#[test]
fn test_clear_custom_providers() {
let _lock = TEST_LOCK.lock().unwrap();
clear_custom_providers();
let test_dep = TestDependency {
name: "test-package".to_string(),
};
register_upstream_provider(|dep| {
if dep.family() == "test" {
let mut metadata = UpstreamMetadata::default();
metadata.insert(upstream_ontologist::UpstreamDatumWithMetadata {
datum: upstream_ontologist::UpstreamDatum::Homepage(
"https://example.com".to_string(),
),
certainty: Some(upstream_ontologist::Certainty::Certain),
origin: None,
});
Some(metadata)
} else {
None
}
});
assert!(find_upstream(&test_dep).is_some());
clear_custom_providers();
assert!(find_upstream(&test_dep).is_none());
}
}