use std::sync::Arc;
use crate::resolution::rust::identity::{PackageId, TargetId};
use crate::resolution::rust::project::RustProject;
use crate::resolution::rust::target::{CargoTargetKind, RustTarget};
use super::authority;
use super::closure::{self, ClosureEntry, UnitClosure};
use super::error::RustSnapshotError;
use super::source::{self, RustSource};
use super::store::{SourceStore, refuse};
#[derive(Debug)]
pub struct RustPrimaryTargetSnapshot {
target: TargetId,
kind: CargoTargetKind,
crate_root: Arc<str>,
sources: Box<[Arc<str>]>,
}
impl RustPrimaryTargetSnapshot {
pub fn target(&self) -> TargetId {
self.target
}
pub fn kind(&self) -> CargoTargetKind {
self.kind
}
pub fn crate_root(&self) -> &str {
&self.crate_root
}
pub fn sources(&self) -> impl ExactSizeIterator<Item = &str> {
self.sources.iter().map(AsRef::as_ref)
}
}
#[derive(Debug)]
pub struct RustPackageSnapshot {
package: PackageId,
targets: Box<[RustPrimaryTargetSnapshot]>,
sources: Box<[RustSource]>,
}
impl RustPackageSnapshot {
pub fn package(&self) -> PackageId {
self.package
}
pub fn targets(&self) -> &[RustPrimaryTargetSnapshot] {
&self.targets
}
pub fn sources(&self) -> &[RustSource] {
&self.sources
}
pub fn source(&self, path: &str) -> Option<&RustSource> {
source::find(&self.sources, path)
}
}
#[derive(Debug, thiserror::Error)]
#[error("{source}")]
pub struct RustPackageSnapshotError {
target: Option<TargetId>,
#[source]
source: RustSnapshotError,
}
impl RustPackageSnapshotError {
pub fn target(&self) -> Option<TargetId> {
self.target
}
pub fn into_source(self) -> RustSnapshotError {
self.source
}
fn package(source: RustSnapshotError) -> Self {
Self {
target: None,
source,
}
}
fn for_target(target: TargetId, source: RustSnapshotError) -> Self {
Self {
target: Some(target),
source,
}
}
}
pub(in crate::resolution::rust) fn build(
project: &RustProject,
id: PackageId,
) -> Result<RustPackageSnapshot, RustPackageSnapshotError> {
let package =
authority::validated_package(project, id).map_err(RustPackageSnapshotError::package)?;
let primary_targets = project
.package_targets(package.id())
.filter(|target| is_primary(target.kind()));
let mut store = SourceStore::new(project.root(), project.limits());
let mut targets = Vec::new();
for target in primary_targets {
targets.push(build_target(&mut store, target)?);
}
Ok(RustPackageSnapshot {
package: id,
targets: targets.into_boxed_slice(),
sources: store.finish(),
})
}
fn build_target(
store: &mut SourceStore,
target: &RustTarget,
) -> Result<RustPrimaryTargetSnapshot, RustPackageSnapshotError> {
let entry = ClosureEntry {
target_name: target.name(),
entry_path: target.entry_path(),
edition: target.edition(),
};
let mut failures = Vec::new();
let closure = closure::walk_unit(store, &entry, &mut failures);
match (closure, failures.is_empty()) {
(Some(closure), true) => Ok(target_snapshot(target, closure)),
(None, _) | (Some(_), false) => Err(RustPackageSnapshotError::for_target(
target.id(),
refuse(store, failures),
)),
}
}
fn target_snapshot(target: &RustTarget, closure: UnitClosure) -> RustPrimaryTargetSnapshot {
RustPrimaryTargetSnapshot {
target: target.id(),
kind: target.kind(),
crate_root: Arc::clone(&target.entry_path),
sources: closure.sources,
}
}
fn is_primary(kind: CargoTargetKind) -> bool {
matches!(
kind,
CargoTargetKind::Library | CargoTargetKind::Binary | CargoTargetKind::BuildScript
)
}