initial commit
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: Ife1391ed23a1e7f388b1b5eca90b9ea76a6a6964
This commit is contained in:
commit
ef28bdaeb4
63 changed files with 17292 additions and 0 deletions
150
src/resolver.rs
Normal file
150
src/resolver.rs
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use crate::{
|
||||
error::{PakkerError, Result},
|
||||
model::{LockFile, Project},
|
||||
platform::PlatformClient,
|
||||
};
|
||||
|
||||
pub struct DependencyResolver {
|
||||
visited: HashSet<String>,
|
||||
path: Vec<String>,
|
||||
}
|
||||
|
||||
impl DependencyResolver {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
visited: HashSet::new(),
|
||||
path: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve<'a>(
|
||||
&'a mut self,
|
||||
project: &'a mut Project,
|
||||
lockfile: &'a mut LockFile,
|
||||
platforms: &'a HashMap<String, Box<dyn PlatformClient>>,
|
||||
) -> std::pin::Pin<
|
||||
Box<dyn std::future::Future<Output = Result<Vec<Project>>> + 'a>,
|
||||
> {
|
||||
Box::pin(async move {
|
||||
let mut resolved = Vec::new();
|
||||
|
||||
if let Some(ref pakku_id) = project.pakku_id {
|
||||
if lockfile.get_project(pakku_id).is_some() {
|
||||
log::debug!("Project already in lockfile: {}", project.get_name());
|
||||
return Ok(resolved);
|
||||
}
|
||||
if self.path.contains(pakku_id) {
|
||||
let cycle_path = self.path.join(" -> ");
|
||||
return Err(PakkerError::CircularDependency(format!(
|
||||
"{cycle_path} -> {pakku_id}"
|
||||
)));
|
||||
}
|
||||
self.path.push(pakku_id.clone());
|
||||
} else {
|
||||
return Ok(resolved);
|
||||
}
|
||||
|
||||
let mut dependencies_set: HashSet<String> = HashSet::new();
|
||||
for file in &project.files {
|
||||
for dep_id in &file.required_dependencies {
|
||||
dependencies_set.insert(dep_id.clone());
|
||||
}
|
||||
}
|
||||
let dependencies: Vec<String> = dependencies_set.into_iter().collect();
|
||||
|
||||
for dep_id in dependencies {
|
||||
let existing_pakku_id = lockfile
|
||||
.find_project_by_platform_id("modrinth", &dep_id)
|
||||
.or_else(|| {
|
||||
lockfile.find_project_by_platform_id("curseforge", &dep_id)
|
||||
})
|
||||
.or_else(|| lockfile.find_project_by_platform_id("github", &dep_id))
|
||||
.map(|p| p.pakku_id.clone());
|
||||
|
||||
if let Some(Some(existing_id)) = existing_pakku_id {
|
||||
if let Some(ref my_id) = project.pakku_id {
|
||||
project.pakku_links.insert(existing_id.clone());
|
||||
if let Some(existing_mut) = lockfile.find_project_mut(&existing_id)
|
||||
{
|
||||
existing_mut.pakku_links.insert(my_id.clone());
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut dep_project =
|
||||
self.fetch_dependency(&dep_id, lockfile, platforms).await?;
|
||||
|
||||
if let (Some(dep_id), Some(my_id)) =
|
||||
(&dep_project.pakku_id, &project.pakku_id)
|
||||
{
|
||||
project.pakku_links.insert(dep_id.clone());
|
||||
dep_project.pakku_links.insert(my_id.clone());
|
||||
}
|
||||
|
||||
let mut sub_deps =
|
||||
self.resolve(&mut dep_project, lockfile, platforms).await?;
|
||||
|
||||
resolved.push(dep_project);
|
||||
resolved.append(&mut sub_deps);
|
||||
}
|
||||
|
||||
if let Some(ref pakku_id) = project.pakku_id {
|
||||
self.visited.insert(pakku_id.clone());
|
||||
}
|
||||
self.path.pop();
|
||||
|
||||
Ok(resolved)
|
||||
})
|
||||
}
|
||||
|
||||
async fn fetch_dependency(
|
||||
&self,
|
||||
dep_id: &str,
|
||||
lockfile: &LockFile,
|
||||
platforms: &HashMap<String, Box<dyn PlatformClient>>,
|
||||
) -> Result<Project> {
|
||||
let mut projects = Vec::new();
|
||||
|
||||
for (platform_name, client) in platforms {
|
||||
match client
|
||||
.request_project_with_files(
|
||||
dep_id,
|
||||
&lockfile.mc_versions,
|
||||
&lockfile.get_loader_names(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(project) => {
|
||||
log::info!("Found dependency {dep_id} on {platform_name}");
|
||||
projects.push(project);
|
||||
},
|
||||
Err(e) => {
|
||||
log::debug!("Could not find {dep_id} on {platform_name}: {e}");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if projects.is_empty() {
|
||||
return Err(PakkerError::ProjectNotFound(dep_id.to_string()));
|
||||
}
|
||||
|
||||
if projects.len() == 1 {
|
||||
Ok(projects.into_iter().next().unwrap())
|
||||
} else {
|
||||
let mut merged = projects.remove(0);
|
||||
for project in projects {
|
||||
merged.merge(project);
|
||||
}
|
||||
Ok(merged)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DependencyResolver {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue