Skip to content
This repository was archived by the owner on Sep 9, 2025. It is now read-only.

Commit e00b0b9

Browse files
author
Hendrik van Antwerpen
committed
Support filtering JSON & HTML output
1 parent 06e8183 commit e00b0b9

4 files changed

Lines changed: 242 additions & 85 deletions

File tree

‎stack-graphs/src/json.rs‎

Lines changed: 152 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// Please see the LICENSE-APACHE or LICENSE-MIT files in this distribution for license details.
66
// ------------------------------------------------------------------------------------------------
77

8+
use itertools::Itertools;
89
use lsp_positions::Offset;
910
use lsp_positions::Position;
1011
use lsp_positions::Span;
@@ -37,58 +38,147 @@ use crate::paths::SymbolStack;
3738
#[error(transparent)]
3839
pub struct JsonError(#[from] serde_json::error::Error);
3940

41+
//-----------------------------------------------------------------------------
42+
// Filter
43+
44+
pub trait Filter {
45+
/// Return whether elements for the given file must be included.
46+
fn include_file(&self, graph: &StackGraph, file: &Handle<File>) -> bool;
47+
48+
/// Return whether the given node must be included.
49+
/// Nodes of excluded files are always excluded.
50+
fn include_node(&self, graph: &StackGraph, node: &Handle<Node>) -> bool;
51+
52+
/// Return whether the given edge must be included.
53+
/// Edges via excluded nodes are always excluded.
54+
fn include_edge(&self, graph: &StackGraph, source: &Handle<Node>, sink: &Handle<Node>) -> bool;
55+
56+
/// Return whether the given path must be included.
57+
/// Paths via excluded nodes or edges are always excluded.
58+
fn include_path(&self, graph: &StackGraph, paths: &Paths, path: &Path) -> bool;
59+
}
60+
61+
impl<F> Filter for F
62+
where
63+
F: Fn(&StackGraph, &Handle<File>) -> bool,
64+
{
65+
fn include_file(&self, graph: &StackGraph, file: &Handle<File>) -> bool {
66+
self(graph, file)
67+
}
68+
69+
fn include_node(&self, _graph: &StackGraph, _node: &Handle<Node>) -> bool {
70+
true
71+
}
72+
73+
fn include_edge(
74+
&self,
75+
_graph: &StackGraph,
76+
_source: &Handle<Node>,
77+
_sink: &Handle<Node>,
78+
) -> bool {
79+
true
80+
}
81+
82+
fn include_path(&self, _graph: &StackGraph, _paths: &Paths, _path: &Path) -> bool {
83+
true
84+
}
85+
}
86+
87+
/// Struct that ensures the implications of exclusions.
88+
/// For example, that nodes frome excluded files are not included, etc.
89+
struct ImplicationFilter<'a>(&'a dyn Filter);
90+
91+
impl Filter for ImplicationFilter<'_> {
92+
fn include_file(&self, graph: &StackGraph, file: &Handle<File>) -> bool {
93+
self.0.include_file(graph, file)
94+
}
95+
96+
fn include_node(&self, graph: &StackGraph, node: &Handle<Node>) -> bool {
97+
graph[*node]
98+
.id()
99+
.file()
100+
.map_or(true, |f| self.include_file(graph, &f))
101+
&& self.0.include_node(graph, node)
102+
}
103+
104+
fn include_edge(&self, graph: &StackGraph, source: &Handle<Node>, sink: &Handle<Node>) -> bool {
105+
self.include_node(graph, source)
106+
&& self.include_node(graph, sink)
107+
&& self.0.include_edge(graph, source, sink)
108+
}
109+
110+
fn include_path(&self, graph: &StackGraph, paths: &Paths, path: &Path) -> bool {
111+
path.edges
112+
.iter_unordered(paths)
113+
.map(|e| graph.node_for_id(e.source_node_id).unwrap())
114+
.chain(std::iter::once(path.end_node))
115+
.tuple_windows()
116+
.all(|(source, sink)| self.include_edge(graph, &source, &sink))
117+
&& self.0.include_path(graph, paths, path)
118+
}
119+
}
120+
40121
//-----------------------------------------------------------------------------
41122
// InStackGraph
42123

43-
struct InStackGraph<'a, T>(T, &'a StackGraph);
124+
struct InStackGraph<'a, T>(T, &'a StackGraph, &'a dyn Filter);
44125

45126
impl<'a, T> InStackGraph<'a, T> {
46127
fn with<U>(&'a self, u: U) -> InStackGraph<'a, U> {
47-
InStackGraph(u, self.1)
128+
InStackGraph(u, self.1, self.2)
48129
}
49130

50131
fn with_idx<Idx: Copy, U: ?Sized>(&'a self, idx: Idx) -> InStackGraph<'a, (Idx, &U)>
51132
where
52133
StackGraph: Index<Idx, Output = U>,
53134
{
54-
InStackGraph((idx, &self.1[idx]), self.1)
135+
InStackGraph((idx, &self.1[idx]), self.1, self.2)
55136
}
56137
}
57138

58139
//-----------------------------------------------------------------------------
59140
// StackGraph
60141

61-
impl StackGraph {
62-
pub fn to_json(&self) -> JsonStackGraph {
63-
JsonStackGraph(self)
142+
impl<'a> StackGraph {
143+
pub fn to_json(&'a self, f: &'a dyn Filter) -> JsonStackGraph {
144+
JsonStackGraph(self, f)
64145
}
65146
}
66147

67-
pub struct JsonStackGraph<'a>(&'a StackGraph);
148+
pub struct JsonStackGraph<'a>(&'a StackGraph, &'a dyn Filter);
68149

69150
impl<'a> JsonStackGraph<'a> {
70151
pub fn to_value(&self) -> Result<Value, JsonError> {
71-
Ok(serde_json::to_value(self.0)?)
152+
Ok(serde_json::to_value(self)?)
72153
}
73154

74155
pub fn to_string(&self) -> Result<String, JsonError> {
75-
Ok(serde_json::to_string(self.0)?)
156+
Ok(serde_json::to_string(self)?)
76157
}
77158

78159
pub fn to_string_pretty(&self) -> Result<String, JsonError> {
79-
Ok(serde_json::to_string_pretty(self.0)?)
160+
Ok(serde_json::to_string_pretty(self)?)
80161
}
81162
}
82163

83-
impl Serialize for StackGraph {
164+
impl Serialize for JsonStackGraph<'_> {
84165
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
85166
where
86167
S: Serializer,
87168
{
88169
let mut ser = serializer.serialize_struct("stack_graph", 2)?;
89-
ser.serialize_field("files", &InStackGraph(&Files, self))?;
90-
ser.serialize_field("nodes", &InStackGraph(&Nodes, self))?;
91-
ser.serialize_field("edges", &InStackGraph(&Edges, self))?;
170+
ser.serialize_field(
171+
"files",
172+
&InStackGraph(&Files, self.0, &ImplicationFilter(self.1)),
173+
)?;
174+
ser.serialize_field(
175+
"nodes",
176+
&InStackGraph(&Nodes, self.0, &ImplicationFilter(self.1)),
177+
)?;
178+
ser.serialize_field(
179+
"edges",
180+
&InStackGraph(&Edges, self.0, &ImplicationFilter(self.1)),
181+
)?;
92182
ser.end()
93183
}
94184
}
@@ -98,15 +188,16 @@ impl Serialize for StackGraph {
98188

99189
struct Files;
100190

101-
impl Serialize for InStackGraph<'_, &Files> {
191+
impl<'a> Serialize for InStackGraph<'a, &Files> {
102192
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
103193
where
104194
S: Serializer,
105195
{
106196
let graph = self.1;
197+
let filter = self.2;
107198

108199
let mut ser = serializer.serialize_seq(None)?;
109-
for file in graph.iter_files() {
200+
for file in graph.iter_files().filter(|f| filter.include_file(graph, f)) {
110201
ser.serialize_element(&self.with_idx(file))?;
111202
}
112203
ser.end()
@@ -133,8 +224,15 @@ impl Serialize for InStackGraph<'_, &Nodes> {
133224
where
134225
S: Serializer,
135226
{
227+
let graph = self.1;
228+
let filter = self.2;
229+
136230
let mut nodes = serializer.serialize_seq(None)?;
137-
for node in self.1.iter_nodes() {
231+
for node in self
232+
.1
233+
.iter_nodes()
234+
.filter(|n| filter.include_node(graph, &n))
235+
{
138236
nodes.serialize_element(&self.with_idx(node))?;
139237
}
140238
nodes.end()
@@ -304,9 +402,16 @@ impl Serialize for InStackGraph<'_, &Edges> {
304402
where
305403
S: Serializer,
306404
{
405+
let graph = self.1;
406+
let filter = self.2;
407+
307408
let mut ser = serializer.serialize_seq(None)?;
308409
for source in self.1.iter_nodes() {
309-
for edge in self.1.outgoing_edges(source) {
410+
for edge in self
411+
.1
412+
.outgoing_edges(source)
413+
.filter(|e| filter.include_edge(graph, &e.source, &e.sink))
414+
{
310415
ser.serialize_element(&self.with(&edge))?;
311416
}
312417
}
@@ -377,70 +482,67 @@ impl Serialize for InStackGraph<'_, &Offset> {
377482
//-----------------------------------------------------------------------------
378483
// InPaths
379484

380-
struct InPaths<'a, T>(T, &'a Paths, &'a StackGraph);
485+
struct InPaths<'a, T>(T, &'a Paths, &'a StackGraph, &'a dyn Filter);
381486

382487
impl<'a, T> InPaths<'a, T> {
383488
fn with<U>(&'a self, u: U) -> InPaths<'a, U> {
384-
InPaths(u, self.1, self.2)
489+
InPaths(u, self.1, self.2, self.3)
385490
}
386491

387492
fn in_stack_graph(&'a self) -> InStackGraph<'a, T>
388493
where
389494
T: Copy,
390495
{
391-
InStackGraph(self.0, self.2)
496+
InStackGraph(self.0, self.2, self.3)
392497
}
393498
}
394499

395500
//-----------------------------------------------------------------------------
396501
// Paths
397502

398503
impl<'a> Paths {
399-
pub fn to_json<F>(&'a mut self, graph: &'a StackGraph, f: F) -> JsonPaths<'_, F>
400-
where
401-
F: FnMut(&StackGraph, &Paths, &Path) -> bool,
402-
{
504+
pub fn to_json(&'a mut self, graph: &'a StackGraph, f: &'a dyn Filter) -> JsonPaths<'_> {
403505
JsonPaths(self, graph, f)
404506
}
405507
}
406508

407-
pub struct JsonPaths<'a, F>(&'a mut Paths, &'a StackGraph, F)
408-
where
409-
F: FnMut(&StackGraph, &Paths, &Path) -> bool;
509+
pub struct JsonPaths<'a>(&'a mut Paths, &'a StackGraph, &'a dyn Filter);
410510

411-
impl<'a, F> JsonPaths<'a, F>
412-
where
413-
F: FnMut(&StackGraph, &Paths, &Path) -> bool,
414-
{
511+
impl<'a> JsonPaths<'a> {
415512
pub fn to_value(&mut self) -> Result<Value, JsonError> {
416-
let paths = self.to_path_vec();
417-
Ok(serde_json::to_value(&InPaths(&paths, self.0, self.1))?)
513+
let filter = ImplicationFilter(self.2);
514+
let paths = Self::to_path_vec(self.1, self.0, &filter);
515+
Ok(serde_json::to_value(&InPaths(
516+
&paths, self.0, self.1, &filter,
517+
))?)
418518
}
419519

420520
pub fn to_string(&mut self) -> Result<String, JsonError> {
421-
let paths = self.to_path_vec();
422-
Ok(serde_json::to_string(&InPaths(&paths, self.0, self.1))?)
521+
let filter = ImplicationFilter(self.2);
522+
let paths = Self::to_path_vec(self.1, self.0, &filter);
523+
Ok(serde_json::to_string(&InPaths(
524+
&paths, self.0, self.1, &filter,
525+
))?)
423526
}
424527

425528
pub fn to_string_pretty(&mut self) -> Result<String, JsonError> {
426-
let paths = self.to_path_vec();
529+
let filter = ImplicationFilter(self.2);
530+
let paths = Self::to_path_vec(self.1, self.0, &filter);
427531
Ok(serde_json::to_string_pretty(&InPaths(
428-
&paths, self.0, self.1,
532+
&paths, self.0, self.1, &filter,
429533
))?)
430534
}
431535

432-
fn to_path_vec(&mut self) -> Vec<Path> {
433-
let mut paths = Vec::new();
434-
let f = &mut self.2;
435-
self.0
436-
.find_all_paths(self.1, self.1.iter_nodes(), |g, ps, p| {
437-
if f(g, ps, &p) {
438-
let mut p = p;
439-
p.edges.ensure_forwards(ps);
440-
paths.push(p);
441-
}
442-
});
443-
paths
536+
fn to_path_vec(graph: &StackGraph, paths: &mut Paths, filter: &dyn Filter) -> Vec<Path> {
537+
let mut path_vec = Vec::new();
538+
paths.find_all_paths(graph, graph.iter_nodes(), |g, ps, p| {
539+
if filter.include_path(g, ps, &p) {
540+
let mut p = p;
541+
p.edges.ensure_forwards(ps);
542+
path_vec.push(p);
543+
}
544+
});
545+
path_vec
444546
}
445547
}
446548

0 commit comments

Comments
 (0)