use std::collections::HashSet;
use crate::artifact::{Artifact, LocalRepository};
use crate::model::Dependency;
use crate::resolver::repository::RemoteRepository;
use crate::resolver::downloader::ArtifactDownloader;
pub struct DependencyResolver {
local_repository: Box<dyn LocalRepository>,
remote_repositories: Vec<RemoteRepository>,
downloader: ArtifactDownloader,
}
impl DependencyResolver {
pub fn new(local_repository: Box<dyn LocalRepository>) -> Self {
Self {
local_repository,
remote_repositories: vec![RemoteRepository::new(
"central",
url::Url::parse("https://repo1.maven.org/maven2/").unwrap(),
)],
downloader: ArtifactDownloader::new(),
}
}
pub fn with_remote_repositories(
mut self,
repositories: Vec<RemoteRepository>,
) -> Self {
self.remote_repositories = repositories;
self
}
pub fn remote_repositories(&self) -> &[RemoteRepository] {
&self.remote_repositories
}
pub fn resolve_dependency(
&self,
dependency: &Dependency,
) -> anyhow::Result<Option<Artifact>> {
let version = dependency.version.as_ref().ok_or_else(|| {
anyhow::anyhow!("Dependency {}:{} has no version", dependency.group_id, dependency.artifact_id)
})?;
let artifact = Artifact::new(
&dependency.group_id,
&dependency.artifact_id,
version,
);
self.resolve_artifact(&artifact)
}
pub fn resolve_artifact(
&self,
artifact: &Artifact,
) -> anyhow::Result<Option<Artifact>> {
if self.local_repository.artifact_exists(artifact) {
let mut resolved = artifact.clone();
resolved.file = Some(self.local_repository.artifact_path(artifact));
return Ok(Some(resolved));
}
let local_path = self.local_repository.artifact_path(artifact);
match self.downloader.download_from_repositories(
artifact,
&self.remote_repositories,
&local_path,
) {
Ok(_) => {
let mut resolved = artifact.clone();
resolved.file = Some(local_path);
Ok(Some(resolved))
}
Err(e) => {
tracing::warn!("Failed to resolve artifact {}: {}", artifact, e);
Ok(None)
}
}
}
pub fn resolve_pom(
&self,
artifact: &Artifact,
) -> anyhow::Result<Option<crate::model::Model>> {
let mut pom_artifact = artifact.clone();
pom_artifact.coordinates.packaging = Some("pom".to_string());
if let Some(resolved) = self.resolve_artifact(&pom_artifact)? {
if let Some(path) = resolved.file {
let content = std::fs::read_to_string(path)?;
let model = crate::model::parser::parse_pom(&content)?;
return Ok(Some(model));
}
}
Ok(None)
}
pub fn resolve_dependencies(
&self,
dependencies: &[Dependency],
) -> anyhow::Result<Vec<Artifact>> {
let mut resolved = Vec::new();
let mut seen = HashSet::new();
for dependency in dependencies {
let key = format!("{}:{}", dependency.group_id, dependency.artifact_id);
if seen.contains(&key) {
continue;
}
seen.insert(key);
if let Some(artifact) = self.resolve_dependency(dependency)? {
resolved.push(artifact);
}
}
Ok(resolved)
}
}