use std::path::Path;
use std::sync::Arc;
use crate::resolution::line_index;
use crate::resolution::supply::SourceSupply;
use super::discovery::{EntryBudget, package_directories};
use super::fault::GoSourceFault;
use super::fingerprint::{self, GoSnapshotFingerprint};
use super::identity::{index_of, position};
use super::inventory::GoFileInventory;
use super::limits::GoResolutionLimits;
use super::packages::{PackageSite, UnitDraft};
use super::project::GoProject;
use super::requirement::GoRequirementResolution;
use super::snapshot_error::GoSnapshotError;
use super::snapshot_module::{GoSnapshotEdge, GoSnapshotModule, GoSnapshotModuleId};
use super::source::GoSource;
use super::store::GoSourceStore;
use super::unit::{GoResolutionUnit, GoSnapshotUnitId};
use super::unit_table::UnitTable;
#[derive(Debug)]
pub struct GoResolutionSnapshot {
root: Box<Path>,
limits: GoResolutionLimits,
modules: Box<[GoSnapshotModule]>,
units: Box<[GoResolutionUnit]>,
edges: Box<[GoSnapshotEdge]>,
sources: Box<[GoSource]>,
fingerprint: GoSnapshotFingerprint,
}
impl GoResolutionSnapshot {
pub fn root(&self) -> &Path {
&self.root
}
pub fn limits(&self) -> GoResolutionLimits {
self.limits
}
pub fn root_module(&self) -> GoSnapshotModuleId {
GoSnapshotModuleId::new(position(0))
}
pub fn modules(&self) -> &[GoSnapshotModule] {
&self.modules
}
pub fn module(&self, id: GoSnapshotModuleId) -> Option<&GoSnapshotModule> {
self.modules.get(index_of(id.index()))
}
pub fn units(&self) -> &[GoResolutionUnit] {
&self.units
}
pub fn unit(&self, id: GoSnapshotUnitId) -> Option<&GoResolutionUnit> {
self.units.get(index_of(id.index()))
}
pub fn edges(&self) -> &[GoSnapshotEdge] {
&self.edges
}
pub fn sources(&self) -> &[GoSource] {
&self.sources
}
pub fn source(&self, path: &str) -> Option<&GoSource> {
line_index::find(&self.sources, path)
}
pub fn fingerprint(&self) -> GoSnapshotFingerprint {
self.fingerprint
}
}
pub(super) fn build<P: SourceSupply<GoFileInventory, GoSourceFault>>(
project: &GoProject,
provider: &mut P,
) -> Result<GoResolutionSnapshot, GoSnapshotError> {
let limits = project.limits();
let root = project.root();
let mut store = GoSourceStore::new(root, limits);
let mut budget = EntryBudget::new(limits);
let mut table = UnitTable::new(limits);
for (index, module) in project.modules().enumerate() {
let site = PackageSite {
root,
module: position(index),
module_path: &module.path,
};
let directories = package_directories(root, &root.join(module.directory()), &mut budget)?;
table.retain_module(&mut store, provider, &site, &directories)?;
}
Ok(complete(
project,
sealed_units(table.finish()),
store.finish(),
))
}
fn complete(
project: &GoProject,
units: Box<[GoResolutionUnit]>,
sources: Box<[GoSource]>,
) -> GoResolutionSnapshot {
let root = Box::from(project.root());
let modules = snapshot_modules(project);
let edges = snapshot_edges(project);
let fingerprint = fingerprint::of_completed(&root, &modules, (&units, &edges), &sources);
GoResolutionSnapshot {
root,
limits: project.limits(),
modules,
units,
edges,
sources,
fingerprint,
}
}
fn sealed_units(drafts: Vec<UnitDraft>) -> Box<[GoResolutionUnit]> {
let mut ordered = drafts;
ordered.sort_by(|left, right| {
(left.module, &left.import_path, left.context).cmp(&(
right.module,
&right.import_path,
right.context,
))
});
ordered
.into_iter()
.enumerate()
.map(|(index, draft)| GoResolutionUnit {
id: GoSnapshotUnitId::new(position(index)),
module: GoSnapshotModuleId::new(draft.module),
context: draft.context,
import_path: draft.import_path,
package_name: draft.package_name,
directory: draft.directory,
sources: draft.sources,
})
.collect()
}
fn snapshot_modules(project: &GoProject) -> Box<[GoSnapshotModule]> {
project
.modules()
.enumerate()
.map(|(index, module)| GoSnapshotModule {
id: GoSnapshotModuleId::new(position(index)),
path: Arc::clone(&module.path),
directory: Arc::clone(&module.directory),
manifest: Arc::clone(&module.manifest),
depth: module.depth,
})
.collect()
}
fn snapshot_edges(project: &GoProject) -> Box<[GoSnapshotEdge]> {
project
.requirements()
.iter()
.filter_map(|requirement| {
let GoRequirementResolution::LocalModule(target) = requirement.resolution() else {
return None;
};
Some(GoSnapshotEdge {
source: GoSnapshotModuleId::new(requirement.module().index()),
target: GoSnapshotModuleId::new(target.index()),
module_path: Box::from(requirement.path()),
version: Box::from(requirement.version()),
})
})
.collect()
}