use std::fmt;
use std::path::Path;
use sha2::{Digest, Sha256};
use super::dependency::{CargoDependencyKind, DependencyActivation};
use super::snapshot::{RustResolutionUnit, RustSnapshotEdge, RustSource};
pub(in crate::resolution::rust) struct UnitClaim<'a> {
pub(in crate::resolution::rust) name: &'a str,
pub(in crate::resolution::rust) manifest: &'a str,
pub(in crate::resolution::rust) kind: &'a str,
pub(in crate::resolution::rust) crate_root: &'a str,
pub(in crate::resolution::rust) predicate: Option<&'a str>,
pub(in crate::resolution::rust) sources: &'a [&'a str],
}
pub(in crate::resolution::rust) struct EdgeClaim<'a> {
pub(in crate::resolution::rust) source: u32,
pub(in crate::resolution::rust) target: u32,
pub(in crate::resolution::rust) alias: &'a str,
pub(in crate::resolution::rust) kind: CargoDependencyKind,
pub(in crate::resolution::rust) predicate: Option<&'a str>,
}
pub(in crate::resolution::rust) struct SourceClaim<'a> {
pub(in crate::resolution::rust) path: &'a str,
pub(in crate::resolution::rust) digest: &'a [u8; 32],
}
pub(in crate::resolution::rust) struct SnapshotClaims<'a> {
pub(in crate::resolution::rust) root: &'a Path,
pub(in crate::resolution::rust) requested: u32,
pub(in crate::resolution::rust) units: &'a [UnitClaim<'a>],
pub(in crate::resolution::rust) edges: &'a [EdgeClaim<'a>],
pub(in crate::resolution::rust) sources: &'a [SourceClaim<'a>],
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RustSnapshotFingerprint {
digest: [u8; 32],
}
impl RustSnapshotFingerprint {
pub(in crate::resolution::rust) fn from_claims(claims: &SnapshotClaims<'_>) -> Self {
let mut hasher = Sha256::new();
field(&mut hasher, claims.root.as_os_str().as_encoded_bytes());
field(&mut hasher, &claims.requested.to_le_bytes());
count(&mut hasher, claims.units.len());
claims
.units
.iter()
.for_each(|unit| hash_unit(&mut hasher, unit));
count(&mut hasher, claims.edges.len());
claims
.edges
.iter()
.for_each(|edge| hash_edge(&mut hasher, edge));
count(&mut hasher, claims.sources.len());
claims
.sources
.iter()
.for_each(|source| hash_source(&mut hasher, source));
Self {
digest: hasher.finalize().into(),
}
}
#[cfg(feature = "semantic")]
pub(in crate::resolution::rust) fn bytes(&self) -> &[u8; 32] {
&self.digest
}
}
impl fmt::Debug for RustSnapshotFingerprint {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("RustSnapshotFingerprint(redacted)")
}
}
pub(in crate::resolution::rust) fn of_completed(
root: &Path,
requested: u32,
units: &[RustResolutionUnit],
edges: &[RustSnapshotEdge],
sources: &[RustSource],
) -> RustSnapshotFingerprint {
let memberships: Box<[Box<[&str]>]> = units.iter().map(unit_sources).collect();
let stated: Box<[UnitClaim<'_>]> = units
.iter()
.zip(&memberships)
.map(|(unit, sources)| unit_claim(unit, sources))
.collect();
let selected: Box<[EdgeClaim<'_>]> = edges.iter().map(edge_claim).collect();
let read: Box<[SourceClaim<'_>]> = sources.iter().map(source_claim).collect();
RustSnapshotFingerprint::from_claims(&SnapshotClaims {
root,
requested,
units: &stated,
edges: &selected,
sources: &read,
})
}
fn unit_sources(unit: &RustResolutionUnit) -> Box<[&str]> {
unit.sources().iter().map(|path| &**path).collect()
}
fn unit_claim<'a>(unit: &'a RustResolutionUnit, sources: &'a [&'a str]) -> UnitClaim<'a> {
UnitClaim {
name: unit.name(),
manifest: unit.manifest_path(),
kind: unit.kind().token(),
crate_root: unit.crate_root(),
predicate: predicate(unit.activation()),
sources,
}
}
fn edge_claim(edge: &RustSnapshotEdge) -> EdgeClaim<'_> {
EdgeClaim {
source: edge.source().index(),
target: edge.target().index(),
alias: edge.name(),
kind: edge.kind(),
predicate: predicate(edge.activation()),
}
}
fn source_claim(source: &RustSource) -> SourceClaim<'_> {
SourceClaim {
path: source.path(),
digest: source.digest(),
}
}
fn predicate(activation: &DependencyActivation) -> Option<&str> {
match activation {
DependencyActivation::Always => None,
DependencyActivation::Conditional(predicate) => Some(predicate),
}
}
fn hash_unit(hasher: &mut Sha256, unit: &UnitClaim<'_>) {
field(hasher, unit.name.as_bytes());
field(hasher, unit.manifest.as_bytes());
field(hasher, unit.kind.as_bytes());
field(hasher, unit.crate_root.as_bytes());
hash_predicate(hasher, unit.predicate);
count(hasher, unit.sources.len());
unit.sources
.iter()
.for_each(|path| field(hasher, path.as_bytes()));
}
fn hash_edge(hasher: &mut Sha256, edge: &EdgeClaim<'_>) {
field(hasher, &edge.source.to_le_bytes());
field(hasher, &edge.target.to_le_bytes());
field(hasher, edge.alias.as_bytes());
field(hasher, &[kind_tag(edge.kind)]);
hash_predicate(hasher, edge.predicate);
}
fn hash_source(hasher: &mut Sha256, source: &SourceClaim<'_>) {
field(hasher, source.path.as_bytes());
field(hasher, source.digest);
}
fn hash_predicate(hasher: &mut Sha256, predicate: Option<&str>) {
match predicate {
None => field(hasher, &[0]),
Some(text) => {
field(hasher, &[1]);
field(hasher, text.as_bytes());
}
}
}
fn kind_tag(kind: CargoDependencyKind) -> u8 {
match kind {
CargoDependencyKind::Normal => 0,
CargoDependencyKind::Development => 1,
CargoDependencyKind::Build => 2,
}
}
fn field(hasher: &mut Sha256, bytes: &[u8]) {
hasher.update((bytes.len() as u64).to_le_bytes());
hasher.update(bytes);
}
fn count(hasher: &mut Sha256, elements: usize) {
field(hasher, &(elements as u64).to_le_bytes());
}