Shouldn't change anything for now, but in the future it should be much easier to add other package-specific annotations, such as lines of code, downloads per month, time since last release, size in final binary, etc
BRXHJFU7ANVWOFXBNR5EYUGDURCFCZIF66VDV6L3JBYC45CAFNJAC PJPTNU2SXAGBBBVDFZMHHRMC6X5UGOPBJWT7DP5YXOZKQKGLKHWQC XVQXXAGZ4WHBQIU3WEUKLZX6FKV5V52LSTTGMFXR42WPWJZJNENQC YA5ITLOV2UWAQZWFJND2WM45DLWG7PTECNJQOLPZAHH2GETPI3HQC C43IWI7GMF6UEKGTXTK4GSQLAFD2SH6BKQRGR4ZQNITYSUK6RMXAC ZEN3WUPDVQWI7LPTEG3WQA5QSEHU74CUXVFJJFIJNSIFC7N2CMEAC UXJFRBBL7IZ2PR7ZYNFGOJ6A7EH5ZVLYVFLA3HNFCNRVL6KWJUDAC OPTMCUTBEZQT3HETRWVBU4HYSB7NNBA4TY7QOOKCN3YLSMDKNM2QC 475UXTLYE5LQTMOGOFYC5Z5Z45YJ32M4GD2WOBTXZRBXKUSWFW2AC LOR3KOXGQ2VYGDHXQ6MG22ZME5TMPFTUW7A5OG36IAVQANOCXBRAC 7CVIL7UJBYEZ4KHKPJ7ZYSVQ7BLQWWUSJLJR5FOXBICQTD5ETK4QC UQJO24KBYI77E4J6LXWX2IUN7VABQKG6PGKBKWEPDCH5CKYBTC4AC T34OV3YQGRFMXYWEFLBCFMX3U2TVXF552B5B3S6HMBJZU66PDMYAC ZPFD3275NTWST7F5YCWYOOUS3QB5HE23LUOZXZIND4HBFSX62NCQC use guppy::graph::{DependencyDirection, PackageGraph};pub fn nodes(graph: &PackageGraph) -> Vec<&str> {graph.packages().map(|pkg| pkg.name()).collect()
pub fn nodes<'graph>(annotations: &'graph Annotations) -> Vec<&'graph str> {annotations.packages().map(|id| annotations.metadata(id).unwrap().name()).collect()
pub fn links<'graph>(graph: &'graph PackageGraph,timings: &timings::Output,) -> Vec<(&'graph str, &'graph str, f64)> {let package_set = graph.resolve_all();let mut links = Vec::with_capacity(package_set.len());
pub fn links<'graph>(annotations: &'graph Annotations) -> Vec<(&'graph str, &'graph str, f64)> {let links = annotations.links().collect::<Vec<_>>();let mut sankey_links = Vec::with_capacity(links.len());
for link in package_set.links(DependencyDirection::Forward) {let (from, to) = link.endpoints();let time_taken = timings.pkg_time(to.id()).unwrap_or(0_f64);// Can't just set the edge weight to duration - if the package has N dependents// it would appear to take N times longer, just need to divide duration by// direct dependents to fixlet direct_dependents = to.direct_links_directed(DependencyDirection::Reverse).count();// Make sure to not divide by 0let edge_weight = if direct_dependents == 0 {time_taken} else {time_taken / (direct_dependents as f64)};
for (from, to) in links {let edge_weight = annotations.variable(to, Variable::UnitDuration, Measurement::Relative).unwrap_or(0_f64);
pub fn axes(timings: &timings::Output) -> (Axis, Axis) {let pkg_durations: Vec<f64> = timings.pkg_times().collect();
pub fn axes(annotations: &Annotations) -> (Axis, Axis) {let packages = annotations.packages();let pkg_durations = packages.filter_map(|id| annotations.variable(id, Variable::UnitDuration, Measurement::Exact)).collect::<Vec<_>>();
pub fn data(timings: &timings::Output) -> Vec<DataPoint> {let pkg_durations: Vec<f64> = timings.pkg_times().collect();
pub fn data(annotations: &Annotations) -> Vec<DataPoint> {let packages = annotations.packages();let pkg_durations = packages.filter_map(|id| annotations.variable(id, Variable::UnitDuration, Measurement::Exact)).collect::<Vec<_>>();
let edges = package_set.links(DependencyDirection::Forward).map(|link| (link.from(), link.to())).map(|(from, to)| (link_index.get(from.id()), link_index.get(to.id())))
let edges = annotations.links().map(|(from, to)| (link_index.get(from), link_index.get(to)))
let sizes = package_set.package_ids(DependencyDirection::Forward).map(|id| timings.pkg_time(id).unwrap_or(0_f64));
let sizes = link_index.keys().map(|id| {annotations.variable(id, Variable::UnitDuration, Measurement::Exact).unwrap_or(0_f64)});
let unit_time = if let Some(duration) = timings.pkg_time(id) {duration} else {// TODO: once using the resolved crate graph, `None` should never appearprintln!("Queried node outside cargo's unit graph: {id}");0_f64};
let unit_time = annotations.variable(id, Variable::UnitDuration, Measurement::Exact).unwrap_or(0_f64);
}pub fn pkg_time(&self, pkg: &PackageId) -> Option<f64> {self.repr.get(pkg).map(|timings| timings.iter().map(|msg| msg.duration).sum())}pub fn pkg_messages(&self, pkg: &PackageId) -> Option<&Vec<Message>> {self.repr.get(pkg)}// TODO: this returns each total package time, but it would be interesting to filter by// crate type (lib, binary, proc_macro), target, build script runs etcpub fn pkg_times<'s>(&'s self) -> impl Iterator<Item = f64> + 's {self.repr.values().map(|messages| messages.iter().map(|msg| msg.duration).sum())}pub fn len(&self) -> usize {self.repr.len()
use std::collections::HashMap;use guppy::graph::{DependencyDirection, PackageGraph, PackageMetadata};use guppy::PackageId;use indexmap::IndexMap;use petgraph::data::{Element, FromElements};use petgraph::graph::NodeIndex;use petgraph::matrix_graph::Zero;use petgraph::{Direction, Graph};pub mod timings;#[derive(Debug, Clone, Copy)]pub enum Measurement {Relative,Exact,}#[derive(Debug, Clone, Copy)]pub enum Variable {UnitDuration,TotalDuration,}#[derive(Debug, Clone)]pub struct Node<'graph> {id: &'graph PackageId,timings: Option<Vec<timings::Message>>,}impl<'graph> Node<'graph> {pub fn timings(&self) -> Option<&Vec<timings::Message>> {self.timings.as_ref()}}#[derive(Debug, Clone)]pub struct Annotations<'graph> {package_graph: &'graph PackageGraph,graph: Graph<Node<'graph>, ()>,node_indices: IndexMap<&'graph PackageId, usize>,}impl<'graph> Annotations<'graph> {pub fn new(package_graph: &'graph PackageGraph,mut timings: HashMap<PackageId, Vec<timings::Message>>,) -> Self {let node_indices = package_graph.package_ids().enumerate().map(|(index, id)| (id, index)).collect::<IndexMap<&PackageId, usize>>();// Iterate over node_indicies.keys() to preserve orderinglet nodes = node_indices.keys().map(|id| Element::Node {weight: Node {id,timings: timings.remove(id),},});let package_set = package_graph.resolve_all();let edges = package_set.links(DependencyDirection::Forward).map(|link| Element::Edge {source: *node_indices.get(link.from().id()).unwrap(),target: *node_indices.get(link.to().id()).unwrap(),weight: (),});let graph = Graph::from_elements(nodes.chain(edges));Self {package_graph,graph,node_indices,}}pub fn root_packages(&self) -> Vec<PackageMetadata> {self.package_graph.resolve_all().root_packages(DependencyDirection::Forward).collect::<Vec<_>>()}fn node_index(&self, id: &PackageId) -> Option<NodeIndex> {let node_index = *self.node_indices.get(id)?;Some(NodeIndex::new(node_index))}pub fn pkg(&self, id: &PackageId) -> Option<&Node> {let node_index = self.node_index(id)?;Some(&self.graph[node_index])}pub fn packages(&self) -> impl Iterator<Item = &&PackageId> {self.node_indices.keys()}pub fn links(&self) -> impl Iterator<Item = (&PackageId, &PackageId)> {self.graph.raw_edges().iter().map(|edge| (self.graph[edge.source()].id, self.graph[edge.target()].id))}pub fn metadata(&self, id: &PackageId) -> Result<PackageMetadata, guppy::Error> {self.package_graph.metadata(id)}pub fn dependents(&self, id: &PackageId) -> Option<usize> {let node_index = self.node_index(id)?;let dependents = self.graph.edges_directed(node_index, Direction::Incoming).count();Some(dependents)}pub fn variable(&self,id: &PackageId,variable: Variable,measurement: Measurement,) -> Option<f64> {let exact_measurement = match variable {Variable::UnitDuration => {let node = self.pkg(id)?;let timings = node.timings.as_ref()?;let durations = timings.iter().map(|msg| msg.duration);if id.repr().contains("windows") {dbg!(id, timings);}durations.sum()}Variable::TotalDuration => {let node_index = self.node_index(id)?;let dependencies = self.graph.neighbors_directed(node_index, Direction::Outgoing);let timings = dependencies.filter_map(|dep| self.graph[dep].timings.as_ref()).flatten().map(|msg| msg.duration);let self_timings = self.variable(id, Variable::UnitDuration, measurement).unwrap_or(0_f64);timings.sum::<f64>() + self_timings}};Some(match measurement {Measurement::Exact => exact_measurement,Measurement::Relative => {let dependents = self.dependents(id)? as f64;if dependents.is_zero() {exact_measurement} else {exact_measurement / dependents}}})}}