Move graph functionality into `graph` module

finchie
Apr 22, 2024, 6:57 AM
UXJFRBBL7IZ2PR7ZYNFGOJ6A7EH5ZVLYVFLA3HNFCNRVL6KWJUDAC

Dependencies

  • [2] UQJO24KB Use `forceatlas2` to construct graph layout
  • [3] B2L26LOA Store index of dependency nodes
  • [4] LOR3KOXG Parse JSON output from `cargo build --timings`
  • [5] ZPFD3275 Switch from `cargo_metadata`+`petgraph` to `guppy`
  • [6] Q3Z6XMP5 Migrate dependency tree to `petgraph::Graph`
  • [7] 7CVIL7UJ Create simple metadata parser
  • [8] JVYWRCPT Add basic chart visualisation
  • [9] O7TNUABZ Migrate `CrateMetadata::graph_data()` from `HashMap` to `petgraph` representation

Change contents

  • edit in src/main.rs at line 1
    [3.49][2.0:32]()
    use std::collections::HashMap;
  • replacement in src/main.rs at line 2
    [3.85][2.33:58](),[2.58][3.0:13](),[3.85][3.0:13](),[3.13][2.59:119](),[2.119][2.119:151](),[3.82][3.123:126](),[3.147][3.123:126](),[2.151][3.123:126](),[3.123][3.123:126]()
    use forceatlas2::Layout;
    use guppy::{
    graph::{DependencyDirection, PackageGraph, PackageSet},
    MetadataCommand, PackageId,
    };
    [3.85]
    [3.2711]
    use guppy::{graph::PackageGraph, MetadataCommand};
  • edit in src/main.rs at line 4
    [3.2712]
    [3.2712]
    mod graph;
  • edit in src/main.rs at line 6
    [3.2725][2.152:484](),[2.484][3.265:266](),[3.2725][3.265:266](),[3.265][3.265:266](),[3.266][2.485:1077]()
    fn graph_layout<'graph>(
    package_set: &'graph PackageSet,
    ) -> impl Iterator<Item = (&'graph PackageId, forceatlas2::Node<f64, 2>)> {
    let link_index: HashMap<&PackageId, usize> = package_set
    .package_ids(DependencyDirection::Forward)
    .enumerate()
    .map(|(index, id)| (id, index))
    .collect();
    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())))
    .map(|(from, to)| (*from.unwrap(), *to.unwrap()))
    .map(|edge| (edge, 1.0))
    .collect();
    let sizes = package_set
    .package_ids(DependencyDirection::Forward)
    .map(|_id| 1.0);
    let mut layout =
    Layout::from_graph_with_degree_mass(edges, sizes, forceatlas2::Settings::default());
    for _step in 0..1_000 {
    layout.iteration();
    }
  • edit in src/main.rs at line 7
    [2.1078][2.1078:1414](),[2.1414][3.83:259](),[3.266][3.83:259](),[3.259][2.1415:1503](),[3.259][3.429:430](),[2.1503][3.429:430](),[3.429][3.429:430](),[3.430][2.1504:1746](),[2.1746][3.541:680](),[3.541][3.541:680](),[3.680][3.761:762](),[3.761][3.761:762](),[3.762][3.681:1089](),[3.1089][3.1215:1216](),[3.1215][3.1215:1216](),[3.1216][3.1090:1174](),[3.1174][3.697:706](),[3.697][3.697:706]()
    // TODO: validate that PackageSet::package_ids() is stable; it is used twice (link_index, here)
    // so need to validate ordering is consistent (or, find a cleaner solution)
    layout
    .nodes
    .into_iter()
    .zip(package_set.package_ids(DependencyDirection::Forward))
    .map(|(node, id)| (id, node))
    }
    fn graph_data(graph: &PackageGraph) -> charming::series::GraphData {
    let categories = vec![charming::series::GraphCategory {
    name: String::from("default"),
    }];
    let package_set = graph.resolve_all();
    let layout = graph_layout(&package_set);
    let nodes = layout
    .map(|(id, node)| charming::series::GraphNode {
    id: id.repr().to_string(),
    name: graph.metadata(id).unwrap().name().to_string(),
    x: node.pos.x(),
    y: node.pos.y(),
    value: 1_f64,
    category: 0,
    symbol_size: 1_f64,
    label: None,
    })
    .collect();
    let links = graph
    .query_forward(graph.package_ids())
    .unwrap()
    .resolve()
    .links(DependencyDirection::Forward)
    .map(|link| link.endpoints())
    .map(|(source, target)| charming::series::GraphLink {
    source: source.id().repr().to_string(),
    target: target.id().repr().to_string(),
    value: None,
    })
    .collect();
    charming::series::GraphData {
    nodes,
    links,
    categories,
    }
    }
  • replacement in src/main.rs at line 18
    [3.1460][3.1289:1336]()
    .data(graph_data(&package_graph)),
    [3.1460]
    [3.1499]
    .data(graph::data(&package_graph)),
  • file addition: graph.rs (----------)
    [3.15]
    use std::collections::HashMap;
    use forceatlas2::Layout;
    use guppy::{
    graph::{DependencyDirection, PackageGraph, PackageSet},
    PackageId,
    };
    fn layout<'graph>(
    package_set: &'graph PackageSet,
    ) -> impl Iterator<Item = (&'graph PackageId, forceatlas2::Node<f64, 2>)> {
    let link_index: HashMap<&PackageId, usize> = package_set
    .package_ids(DependencyDirection::Forward)
    .enumerate()
    .map(|(index, id)| (id, index))
    .collect();
    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())))
    .map(|(from, to)| (*from.unwrap(), *to.unwrap()))
    .map(|edge| (edge, 1.0))
    .collect();
    let sizes = package_set
    .package_ids(DependencyDirection::Forward)
    .map(|_id| 1.0);
    let mut layout =
    Layout::from_graph_with_degree_mass(edges, sizes, forceatlas2::Settings::default());
    for _step in 0..1_000 {
    layout.iteration();
    }
    // TODO: validate that PackageSet::package_ids() is stable; it is used twice (link_index, here)
    // so need to validate ordering is consistent (or, find a cleaner solution)
    layout
    .nodes
    .into_iter()
    .zip(package_set.package_ids(DependencyDirection::Forward))
    .map(|(node, id)| (id, node))
    }
    pub fn data(graph: &PackageGraph) -> charming::series::GraphData {
    let categories = vec![charming::series::GraphCategory {
    name: String::from("default"),
    }];
    let package_set = graph.resolve_all();
    let layout = layout(&package_set);
    let nodes = layout
    .map(|(id, node)| charming::series::GraphNode {
    id: id.repr().to_string(),
    name: graph.metadata(id).unwrap().name().to_string(),
    x: node.pos.x(),
    y: node.pos.y(),
    value: 1_f64,
    category: 0,
    symbol_size: 1_f64,
    label: None,
    })
    .collect();
    let links = graph
    .query_forward(graph.package_ids())
    .unwrap()
    .resolve()
    .links(DependencyDirection::Forward)
    .map(|link| link.endpoints())
    .map(|(source, target)| charming::series::GraphLink {
    source: source.id().repr().to_string(),
    target: target.id().repr().to_string(),
    value: None,
    })
    .collect();
    charming::series::GraphData {
    nodes,
    links,
    categories,
    }
    }