1use crate::error::Result;
2use crate::git::CliOps;
3
4pub fn run(path: String, anchor: Option<String>, format: String, compact: bool) -> Result<()> {
6 let repo_dir = std::env::current_dir().map_err(|e| crate::error::ChronicleError::Io {
7 source: e,
8 location: snafu::Location::default(),
9 })?;
10 let git_ops = CliOps::new(repo_dir);
11
12 let output =
13 crate::read::lookup::build_lookup(&git_ops, &path, anchor.as_deref()).map_err(|e| {
14 crate::error::ChronicleError::Git {
15 source: e,
16 location: snafu::Location::default(),
17 }
18 })?;
19
20 match format.as_str() {
21 "json" => {
22 let json = if compact {
23 let mut compact_out = serde_json::json!({
24 "contracts": output.contracts,
25 "dependencies": output.dependencies,
26 "decisions": output.decisions,
27 "recent_history": output.recent_history,
28 "open_follow_ups": output.open_follow_ups,
29 "staleness": output.staleness,
30 });
31 if let Some(ref k) = output.knowledge {
32 compact_out["knowledge"] = serde_json::to_value(k).unwrap_or_default();
33 }
34 serde_json::to_string_pretty(&compact_out)
35 } else {
36 serde_json::to_string_pretty(&output)
37 }
38 .map_err(|e| crate::error::ChronicleError::Json {
39 source: e,
40 location: snafu::Location::default(),
41 })?;
42 println!("{json}");
43 }
44 _ => {
45 println!("Lookup for: {}", output.file);
46 println!();
47
48 if !output.contracts.is_empty() {
49 println!("Contracts:");
50 for c in &output.contracts {
51 let anchor_str = c
52 .anchor
53 .as_ref()
54 .map(|a| format!(":{}", a))
55 .unwrap_or_default();
56 println!(
57 " [{}] {}{}: {}",
58 c.source, c.file, anchor_str, c.description
59 );
60 }
61 println!();
62 }
63
64 if !output.dependencies.is_empty() {
65 println!("Dependencies:");
66 for d in &output.dependencies {
67 let anchor_str = d
68 .anchor
69 .as_ref()
70 .map(|a| format!(":{}", a))
71 .unwrap_or_default();
72 println!(
73 " {}{} -> {}:{} ({})",
74 d.file, anchor_str, d.target_file, d.target_anchor, d.assumption
75 );
76 }
77 println!();
78 }
79
80 if !output.decisions.is_empty() {
81 println!("Decisions:");
82 for d in &output.decisions {
83 println!(" [{}] {}: {}", d.stability, d.what, d.why);
84 }
85 println!();
86 }
87
88 if !output.recent_history.is_empty() {
89 println!("Recent history:");
90 for h in &output.recent_history {
91 println!(
92 " {} {}: {}",
93 &h.commit[..7.min(h.commit.len())],
94 h.timestamp,
95 h.intent
96 );
97 }
98 println!();
99 }
100
101 if !output.open_follow_ups.is_empty() {
102 println!("Open follow-ups:");
103 for f in &output.open_follow_ups {
104 println!(" {} {}", &f.commit[..7.min(f.commit.len())], f.follow_up);
105 }
106 println!();
107 }
108
109 if let Some(ref knowledge) = output.knowledge {
110 if !knowledge.conventions.is_empty() {
111 println!("Applicable conventions:");
112 for c in &knowledge.conventions {
113 println!(" [{}] {}", c.id, c.rule);
114 }
115 println!();
116 }
117 if !knowledge.boundaries.is_empty() {
118 println!("Module boundaries:");
119 for b in &knowledge.boundaries {
120 println!(" [{}] {}: {}", b.id, b.owns, b.boundary);
121 }
122 println!();
123 }
124 if !knowledge.anti_patterns.is_empty() {
125 println!("Anti-patterns:");
126 for a in &knowledge.anti_patterns {
127 println!(" [{}] Don't: {} -> {}", a.id, a.pattern, a.instead);
128 }
129 println!();
130 }
131 }
132
133 if !output.staleness.is_empty() {
134 let stale_entries: Vec<_> = output.staleness.iter().filter(|s| s.stale).collect();
135 if !stale_entries.is_empty() {
136 println!("Stale annotations:");
137 for s in &stale_entries {
138 println!(
139 " {} ({} commits behind)",
140 &s.annotation_commit[..7.min(s.annotation_commit.len())],
141 s.commits_since
142 );
143 }
144 println!();
145 }
146 }
147
148 if output.contracts.is_empty()
149 && output.dependencies.is_empty()
150 && output.decisions.is_empty()
151 && output.recent_history.is_empty()
152 && output.open_follow_ups.is_empty()
153 && output.staleness.is_empty()
154 {
155 println!(" (no context found)");
156 }
157 }
158 }
159
160 Ok(())
161}