From d81109d81a3fbfc8cd0d08d304a04ad9f164c883 Mon Sep 17 00:00:00 2001 From: Vincent Esche Date: Sun, 24 Nov 2024 19:05:57 +0100 Subject: [PATCH] Add `--no-owns` CLI option for `dependencies` command --- src/command/dependencies/filter.rs | 24 ++++ src/command/dependencies/options.rs | 4 + tests/dependencies.rs | 10 ++ .../snapshots/dependencies__help__smoke.snap | 2 + ...pendencies__selection__no_owns__smoke.snap | 127 ++++++++++++++++++ 5 files changed, 167 insertions(+) create mode 100644 tests/snapshots/dependencies__selection__no_owns__smoke.snap diff --git a/src/command/dependencies/filter.rs b/src/command/dependencies/filter.rs index 6bb552c..e12da60 100644 --- a/src/command/dependencies/filter.rs +++ b/src/command/dependencies/filter.rs @@ -233,6 +233,30 @@ impl<'a> Filter<'a> { debug_assert!(graph.contains_node(root_idx), "{}", ROOT_DROP_ERR_MSG); + if self.options.selection.no_owns { + // drop all "owns" edges: + graph.retain_edges(|graph, edge_idx| !matches!(&graph[edge_idx], Relationship::Owns)); + + // By removing the structural "owns" edges from the graph, that make it connected, + // we are likely to end up with a graph consisting of mostly individual unconnected nodes, + // which now basically contain no "dependency" information and thus aren't interesting to us. + // + // We thus also drop all nodes that don't have any remaining edges connected to them + // (with the exception of the crate's node, for reasons): + graph.retain_nodes(|graph, node_idx| { + let out_degree = graph + .neighbors_directed(node_idx, Direction::Outgoing) + .count(); + let in_degree = graph + .neighbors_directed(node_idx, Direction::Incoming) + .count(); + + node_idx == root_idx || (out_degree + in_degree) > 0 + }); + } + + debug_assert!(graph.contains_node(root_idx), "{}", ROOT_DROP_ERR_MSG); + Ok(graph) } diff --git a/src/command/dependencies/options.rs b/src/command/dependencies/options.rs index a6cac04..ef9148f 100644 --- a/src/command/dependencies/options.rs +++ b/src/command/dependencies/options.rs @@ -107,6 +107,10 @@ pub struct SelectionOptions { #[clap(long = "no-modules")] pub no_modules: bool, + /// Filter out structural "owns" edges from graph. + #[clap(long = "no-owns")] + pub no_owns: bool, + /// Filter out sysroot crates (`std`, `core` & friends) from graph. #[arg(long = "no-sysroot")] pub no_sysroot: bool, diff --git a/tests/dependencies.rs b/tests/dependencies.rs index 62cf8b4..633a7e2 100644 --- a/tests/dependencies.rs +++ b/tests/dependencies.rs @@ -351,6 +351,16 @@ mod selection { ); } + mod no_owns { + test_cmd!( + args: "dependencies \ + --no-owns", + success: true, + color_mode: ColorMode::Plain, + project: smoke + ); + } + mod no_traits { test_cmd!( args: "dependencies \ diff --git a/tests/snapshots/dependencies__help__smoke.snap b/tests/snapshots/dependencies__help__smoke.snap index 81a057a..5c43044 100644 --- a/tests/snapshots/dependencies__help__smoke.snap +++ b/tests/snapshots/dependencies__help__smoke.snap @@ -1,5 +1,6 @@ --- source: tests/dependencies.rs +assertion_line: 5 expression: output snapshot_kind: text --- @@ -27,6 +28,7 @@ Options: --no-externs Filter out extern items from extern crates from graph --no-fns Filter out functions (e.g. fns, async fns, const fns) from graph --no-modules Filter out modules (e.g. `mod foo`, `mod foo {}`) from graph + --no-owns Filter out structural "owns" edges from graph --no-sysroot Filter out sysroot crates (`std`, `core` & friends) from graph --no-traits Filter out traits (e.g. trait, unsafe trait) from graph --no-types Filter out types (e.g. structs, unions, enums) from graph diff --git a/tests/snapshots/dependencies__selection__no_owns__smoke.snap b/tests/snapshots/dependencies__selection__no_owns__smoke.snap new file mode 100644 index 0000000..127b53f --- /dev/null +++ b/tests/snapshots/dependencies__selection__no_owns__smoke.snap @@ -0,0 +1,127 @@ +--- +source: tests/dependencies.rs +expression: output +snapshot_kind: text +--- +COMMAND: +dependencies +--no-owns + +STDERR: + +STDOUT: +digraph { + + graph [ + label="smoke", + labelloc=t, + + pad=0.4, + + // Consider rendering the graph using a different layout algorithm, such as: + // [dot, neato, twopi, circo, fdp, sfdp] + layout=neato, + overlap=false, + splines="line", + rankdir=LR, + + fontname="Helvetica", + fontsize="36", + ]; + + node [ + fontname="monospace", + fontsize="10", + shape="record", + style="filled", + ]; + + edge [ + fontname="monospace", + fontsize="10", + ]; + + "alloc::fmt" [label="external mod|alloc::fmt", fillcolor="#81c169"]; // "mod" node + "alloc::string::String" [label="external struct|alloc::string::String", fillcolor="#81c169"]; // "struct" node + "bool" [label="external builtin|bool", fillcolor="#81c169"]; // "builtin" node + "core::cmp" [label="external mod|core::cmp", fillcolor="#81c169"]; // "mod" node + "core::future::future::Future" [label="external trait|core::future::future::Future", fillcolor="#81c169"]; // "trait" node + "core::marker::Sized" [label="external trait|core::marker::Sized", fillcolor="#81c169"]; // "trait" node + "core::ops" [label="external mod|core::ops", fillcolor="#81c169"]; // "mod" node + "i32" [label="external builtin|i32", fillcolor="#81c169"]; // "builtin" node + "smoke" [label="crate|smoke", fillcolor="#5397c8"]; // "crate" node + "smoke::derives::Dummy" [label="pub(self) struct|smoke::derives::Dummy", fillcolor="#db5367"]; // "struct" node + "smoke::derives::Dummy::clone" [label="pub(self) fn|smoke::derives::Dummy::clone", fillcolor="#db5367"]; // "fn" node + "smoke::functions::Crate" [label="pub(self) type|smoke::functions::Crate", fillcolor="#db5367"]; // "type" node + "smoke::functions::Local" [label="pub(self) struct|smoke::functions::Local", fillcolor="#db5367"]; // "struct" node + "smoke::functions::Std" [label="pub(self) type|smoke::functions::Std", fillcolor="#db5367"]; // "type" node + "smoke::functions::body" [label="pub(self) fn|smoke::functions::body", fillcolor="#db5367"]; // "fn" node + "smoke::functions::inputs" [label="pub(self) fn|smoke::functions::inputs", fillcolor="#db5367"]; // "fn" node + "smoke::functions::outputs" [label="pub(self) fn|smoke::functions::outputs", fillcolor="#db5367"]; // "fn" node + "smoke::hierarchy" [label="pub(crate) mod|smoke::hierarchy", fillcolor="#f8c04c"]; // "mod" node + "smoke::methods::Crate" [label="pub(self) type|smoke::methods::Crate", fillcolor="#db5367"]; // "type" node + "smoke::methods::Dummy::body" [label="pub(self) fn|smoke::methods::Dummy::body", fillcolor="#db5367"]; // "fn" node + "smoke::methods::Dummy::inputs" [label="pub(self) fn|smoke::methods::Dummy::inputs", fillcolor="#db5367"]; // "fn" node + "smoke::methods::Dummy::outputs" [label="pub(self) fn|smoke::methods::Dummy::outputs", fillcolor="#db5367"]; // "fn" node + "smoke::methods::Local" [label="pub(self) struct|smoke::methods::Local", fillcolor="#db5367"]; // "struct" node + "smoke::methods::Std" [label="pub(self) type|smoke::methods::Std", fillcolor="#db5367"]; // "type" node + "smoke::target::Target" [label="pub struct|smoke::target::Target", fillcolor="#81c169"]; // "struct" node + "smoke::uses" [label="pub(crate) mod|smoke::uses", fillcolor="#f8c04c"]; // "mod" node + "smoke::uses::cycle::node_0" [label="pub(self) mod|smoke::uses::cycle::node_0", fillcolor="#db5367"]; // "mod" node + "smoke::uses::cycle::node_1" [label="pub(self) mod|smoke::uses::cycle::node_1", fillcolor="#db5367"]; // "mod" node + "smoke::uses::cycle::node_1::node_2" [label="pub(self) mod|smoke::uses::cycle::node_1::node_2", fillcolor="#db5367"]; // "mod" node + "smoke::visibility::dummy::kinds::AsyncFunction" [label="pub(self) async fn|smoke::visibility::dummy::kinds::AsyncFunction", fillcolor="#db5367"]; // "async fn" node + "smoke::visibility::dummy::kinds::Struct" [label="pub(self) struct|smoke::visibility::dummy::kinds::Struct", fillcolor="#db5367"]; // "struct" node + "smoke::visibility::dummy::kinds::Trait" [label="pub(self) trait|smoke::visibility::dummy::kinds::Trait", fillcolor="#db5367"]; // "trait" node + "smoke::visibility::dummy::kinds::TraitAlias" [label="pub(self) type|smoke::visibility::dummy::kinds::TraitAlias", fillcolor="#db5367"]; // "type" node + "smoke::visibility::dummy::kinds::TypeAlias" [label="pub(self) type|smoke::visibility::dummy::kinds::TypeAlias", fillcolor="#db5367"]; // "type" node + "str" [label="external builtin|str", fillcolor="#81c169"]; // "builtin" node + + "smoke::derives::Dummy::clone" -> "bool" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::derives::Dummy::clone" -> "smoke::derives::Dummy" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::functions::Crate" -> "smoke::target::Target" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::functions::Std" -> "alloc::string::String" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::functions::body" -> "alloc::string::String" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::functions::body" -> "i32" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::functions::body" -> "smoke::functions::Local" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::functions::body" -> "smoke::target::Target" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::functions::body" -> "str" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::functions::inputs" -> "alloc::string::String" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::functions::inputs" -> "i32" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::functions::inputs" -> "smoke::functions::Local" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::functions::inputs" -> "smoke::target::Target" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::functions::inputs" -> "str" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::functions::outputs" -> "alloc::string::String" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::functions::outputs" -> "i32" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::functions::outputs" -> "smoke::functions::Local" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::functions::outputs" -> "smoke::target::Target" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::functions::outputs" -> "str" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::methods::Crate" -> "smoke::target::Target" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::methods::Dummy::body" -> "alloc::string::String" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::methods::Dummy::body" -> "i32" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::methods::Dummy::body" -> "smoke::methods::Local" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::methods::Dummy::body" -> "smoke::target::Target" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::methods::Dummy::body" -> "str" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::methods::Dummy::inputs" -> "alloc::string::String" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::methods::Dummy::inputs" -> "i32" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::methods::Dummy::inputs" -> "smoke::methods::Local" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::methods::Dummy::inputs" -> "smoke::target::Target" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::methods::Dummy::inputs" -> "str" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::methods::Dummy::outputs" -> "alloc::string::String" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::methods::Dummy::outputs" -> "i32" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::methods::Dummy::outputs" -> "smoke::methods::Local" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::methods::Dummy::outputs" -> "smoke::target::Target" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::methods::Dummy::outputs" -> "str" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::methods::Std" -> "alloc::string::String" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::uses" -> "alloc::fmt" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::uses" -> "core::cmp" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::uses" -> "core::ops" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::uses" -> "smoke::hierarchy" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::uses::cycle::node_0" -> "smoke::uses::cycle::node_1" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::uses::cycle::node_1::node_2" -> "smoke::uses::cycle::node_0" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::visibility::dummy::kinds::AsyncFunction" -> "core::future::future::Future" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::visibility::dummy::kinds::AsyncFunction" -> "core::marker::Sized" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::visibility::dummy::kinds::TraitAlias" -> "smoke::visibility::dummy::kinds::Trait" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + "smoke::visibility::dummy::kinds::TypeAlias" -> "smoke::visibility::dummy::kinds::Struct" [label="uses", color="#7f7f7f", style="dashed"] [constraint=false]; // "uses" edge + +}