code_analyze_core/
formatter_defuse.rs1use std::fmt::Write;
6use std::path::Path;
7
8use crate::formatter::{snippet_one_line, strip_base_path};
9
10pub fn format_focused_paginated_defuse(
13 paginated_sites: &[crate::types::DefUseSite],
14 total: usize,
15 symbol: &str,
16 offset: usize,
17 base_path: Option<&Path>,
18 _verbose: bool,
19) -> String {
20 let mut output = String::new();
21
22 let page_size = paginated_sites.len();
23 let (start, end) = if page_size == 0 {
24 (0, 0)
25 } else {
26 (offset + 1, offset + page_size)
27 };
28
29 let _ = writeln!(
30 output,
31 "DEF-USE SITES {symbol} ({start}-{end} of {total})"
32 );
33
34 let write_sites: Vec<_> = paginated_sites
36 .iter()
37 .filter(|s| {
38 matches!(
39 s.kind,
40 crate::types::DefUseKind::Write | crate::types::DefUseKind::WriteRead
41 )
42 })
43 .collect();
44
45 if !write_sites.is_empty() {
46 output.push_str(" WRITES\n");
47 for site in write_sites {
48 let file_display = strip_base_path(Path::new(&site.file), base_path);
49 let scope_str = site
50 .enclosing_scope
51 .as_ref()
52 .map(|s| format!("{}()", s))
53 .unwrap_or_default();
54 let snippet = snippet_one_line(&site.snippet);
55 let wr_label = if site.kind == crate::types::DefUseKind::WriteRead {
56 " [write_read]"
57 } else {
58 ""
59 };
60 let _ = writeln!(
61 output,
62 " {file_display}:{} {scope_str} {snippet}{wr_label}",
63 site.line
64 );
65 }
66 }
67
68 let read_sites: Vec<_> = paginated_sites
70 .iter()
71 .filter(|s| matches!(s.kind, crate::types::DefUseKind::Read))
72 .collect();
73
74 if !read_sites.is_empty() {
75 output.push_str(" READS\n");
76 for site in read_sites {
77 let file_display = strip_base_path(Path::new(&site.file), base_path);
78 let scope_str = site
79 .enclosing_scope
80 .as_ref()
81 .map(|s| format!("{}()", s))
82 .unwrap_or_default();
83 let snippet = snippet_one_line(&site.snippet);
84 let _ = writeln!(
85 output,
86 " {file_display}:{} {scope_str} {snippet}",
87 site.line
88 );
89 }
90 }
91
92 output
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98 use crate::types::{DefUseKind, DefUseSite};
99
100 fn site(kind: DefUseKind, line: usize, scope: Option<&str>, file: &str) -> DefUseSite {
101 DefUseSite {
102 kind,
103 symbol: "x".to_string(),
104 file: file.to_string(),
105 line,
106 column: 0,
107 snippet: "prev\nlet x = 1;\nnext".to_string(),
108 enclosing_scope: scope.map(String::from),
109 }
110 }
111
112 #[test]
113 fn test_format_paginated_defuse_writes_and_reads() {
114 let sites = vec![
116 site(DefUseKind::Write, 10, Some("init"), "src/main.rs"),
117 site(DefUseKind::WriteRead, 20, None, "src/lib.rs"),
118 site(DefUseKind::Read, 30, Some("run"), "src/main.rs"),
119 ];
120 let base = Path::new("/project");
121
122 let output = format_focused_paginated_defuse(&sites, 3, "x", 0, Some(base), false);
124
125 assert!(output.contains("DEF-USE SITES x (1-3 of 3)"));
127 assert!(output.contains("WRITES"));
128 assert!(output.contains("src/main.rs:10 init()"));
129 assert!(output.contains("[write_read]"));
130 assert!(output.contains("READS"));
131 assert!(output.contains("src/main.rs:30 run()"));
132 }
133
134 #[test]
135 fn test_format_paginated_defuse_no_base_path() {
136 let sites = vec![site(DefUseKind::Read, 5, None, "/abs/path/file.rs")];
138
139 let output = format_focused_paginated_defuse(&sites, 1, "x", 0, None, false);
141
142 assert!(output.contains("/abs/path/file.rs:5"));
144 assert!(!output.contains("WRITES"));
145 assert!(output.contains("READS"));
146 }
147}