use crate::{
metadata::{cilassemblyview::CilAssemblyView, identity::AssemblyIdentity},
project::CilProject,
};
use std::{
collections::{HashMap, HashSet, VecDeque},
path::PathBuf,
};
#[derive(Debug, Clone)]
pub struct VersionMismatch {
pub required: AssemblyIdentity,
pub actual: AssemblyIdentity,
}
pub struct ProjectResult {
pub project: CilProject,
pub loaded_assemblies: Vec<AssemblyIdentity>,
pub missing_dependencies: Vec<String>,
pub failed_loads: Vec<(String, String)>,
pub version_mismatches: Vec<VersionMismatch>,
pub loaded_count: usize,
pub failed_count: usize,
pub(crate) pending_views: HashMap<AssemblyIdentity, CilAssemblyView>,
pub(crate) primary_identity: Option<AssemblyIdentity>,
pub(crate) processed_paths: HashSet<PathBuf>,
pub(crate) discovery_queue: VecDeque<PathBuf>,
}
impl std::fmt::Debug for ProjectResult {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ProjectResult")
.field("project", &self.project)
.field("loaded_assemblies", &self.loaded_assemblies)
.field("missing_dependencies", &self.missing_dependencies)
.field("failed_loads", &self.failed_loads)
.field("version_mismatches", &self.version_mismatches)
.field("loaded_count", &self.loaded_count)
.field("failed_count", &self.failed_count)
.field("pending_views_count", &self.pending_views.len())
.finish_non_exhaustive()
}
}
impl ProjectResult {
#[must_use]
pub fn new() -> Self {
Self {
project: CilProject::new(),
loaded_assemblies: Vec::new(),
missing_dependencies: Vec::new(),
failed_loads: Vec::new(),
version_mismatches: Vec::new(),
loaded_count: 0,
failed_count: 0,
pending_views: HashMap::new(),
primary_identity: None,
processed_paths: HashSet::new(),
discovery_queue: VecDeque::new(),
}
}
pub(crate) fn has_compatible_version(&self, required: &AssemblyIdentity) -> bool {
self.pending_views.keys().any(|id| id.satisfies(required))
}
pub(crate) fn enqueue(&mut self, path: PathBuf) {
if !self.processed_paths.contains(&path) {
self.discovery_queue.push_back(path);
}
}
pub(crate) fn next_path(&mut self) -> Option<PathBuf> {
while let Some(path) = self.discovery_queue.pop_front() {
if !self.processed_paths.contains(&path) {
self.processed_paths.insert(path.clone());
return Some(path);
}
}
None
}
pub(crate) fn take_pending_views(&mut self) -> HashMap<AssemblyIdentity, CilAssemblyView> {
self.processed_paths.clear();
self.discovery_queue.clear();
std::mem::take(&mut self.pending_views)
}
pub fn is_complete_success(&self) -> bool {
self.failed_count == 0
}
pub fn has_failures(&self) -> bool {
self.failed_count > 0
}
pub fn success_count(&self) -> usize {
self.loaded_count
}
pub fn failure_count(&self) -> usize {
self.failed_count
}
pub(crate) fn record_success(&mut self, identity: Option<AssemblyIdentity>) {
if let Some(identity) = identity {
self.loaded_assemblies.push(identity);
}
self.loaded_count += 1;
}
pub(crate) fn record_failure(&mut self, file_path: String, error_message: String) {
self.failed_loads.push((file_path.clone(), error_message));
self.missing_dependencies.push(file_path);
self.failed_count += 1;
}
pub(crate) fn record_version_mismatch(
&mut self,
required: AssemblyIdentity,
actual: AssemblyIdentity,
) {
self.version_mismatches
.push(VersionMismatch { required, actual });
}
pub fn has_version_mismatches(&self) -> bool {
!self.version_mismatches.is_empty()
}
pub fn version_mismatch_count(&self) -> usize {
self.version_mismatches.len()
}
pub fn get_version_mismatches(&self) -> &[VersionMismatch] {
&self.version_mismatches
}
}
impl Default for ProjectResult {
fn default() -> Self {
Self::new()
}
}