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