double_o/
commands_patterns.rs1use std::io::{self, Write};
4use std::path::Path;
5
6use crate::{init, learn, pattern};
7
8const EMPTY_USER_PATTERNS_LINE: &str = " no learned patterns yet";
10
11fn write_pattern_entry<W: Write>(
16 out: &mut W,
17 cmd_match: &str,
18 has_success: bool,
19 has_failure: bool,
20) -> io::Result<()> {
21 if has_success || has_failure {
22 let mut flags = Vec::new();
23 if has_success {
24 flags.push("success");
25 }
26 if has_failure {
27 flags.push("failure");
28 }
29 writeln!(out, " {cmd_match} [{}]", flags.join("] ["))
30 } else {
31 writeln!(out, " {cmd_match}")
32 }
33}
34
35fn with_path(path: &Path, e: &io::Error) -> io::Error {
37 io::Error::new(e.kind(), format!("{}: {e}", path.display()))
38}
39
40fn write_patterns_in<W: Write>(out: &mut W, dir: &Path) -> io::Result<bool> {
47 let entries = match std::fs::read_dir(dir) {
48 Ok(e) => e,
49 Err(e) if e.kind() == io::ErrorKind::NotFound => return Ok(false),
50 Err(e) => return Err(with_path(dir, &e)),
51 };
52
53 let mut found = false;
54 for entry in entries {
55 let path = entry.map_err(|e| with_path(dir, &e))?.path();
56 if path.extension().and_then(|e| e.to_str()) != Some("toml") {
57 continue;
58 }
59 let is_file = match pattern::is_pattern_file(&path) {
64 Ok(b) => b,
65 Err(e) => return Err(with_path(&path, &e)),
66 };
67 if !is_file {
68 continue;
69 }
70 let text = match std::fs::read_to_string(&path) {
71 Ok(text) => text,
72 Err(e)
74 if matches!(
75 e.kind(),
76 io::ErrorKind::InvalidData | io::ErrorKind::NotFound
77 ) =>
78 {
79 continue;
80 }
81 Err(e) => return Err(with_path(&path, &e)),
82 };
83 let Ok(parsed) = toml::from_str::<toml::Value>(&text) else {
84 continue;
85 };
86 found = true;
87
88 let cmd_match = parsed
89 .get("command_match")
90 .and_then(|v| v.as_str())
91 .unwrap_or("(unknown)");
92 let has_success = parsed.get("success").is_some();
93 let has_failure = parsed.get("failure").is_some();
94
95 write_pattern_entry(out, cmd_match, has_success, has_failure)?;
96 }
97 Ok(found)
98}
99
100pub fn list_patterns_in(dir: &Path) -> bool {
106 match write_patterns_in(&mut io::stdout().lock(), dir) {
107 Ok(found) => found,
108 Err(e) => {
109 eprintln!("oo: {e}");
110 false
111 }
112 }
113}
114
115pub fn cmd_patterns_in(dir: &Path) -> i32 {
121 let result = write_patterns_in(&mut io::stdout().lock(), dir);
122 match result {
123 Ok(true) => 0,
124 Ok(false) => {
125 println!("{EMPTY_USER_PATTERNS_LINE}");
126 0
127 }
128 Err(e) => {
129 eprintln!("oo: {e}");
130 1
131 }
132 }
133}
134
135fn write_user_section<W: Write>(out: &mut W, dir: &Path) -> io::Result<()> {
137 writeln!(out, "User ({}):", dir.display())?;
138 if !write_patterns_in(out, dir)? {
139 writeln!(out, "{EMPTY_USER_PATTERNS_LINE}")?;
140 }
141 Ok(())
142}
143
144fn write_builtins<W: Write>(out: &mut W) -> io::Result<()> {
146 for pat in pattern::builtins() {
147 let cmd_match = pat.command_match.as_str();
148 let has_success = pat.success.is_some();
149 let has_failure = pat.failure.is_some();
150
151 write_pattern_entry(out, cmd_match, has_success, has_failure)?;
152 }
153 Ok(())
154}
155
156fn write_all_patterns<W: Write>(out: &mut W) -> io::Result<()> {
158 let project_dir = std::env::current_dir()
159 .map(|cwd| init::project_patterns_dir(&cwd))
160 .ok();
161 let user_dir = learn::patterns_dir();
162
163 writeln!(out, "Built-in ({} patterns):", pattern::builtins().len())?;
165 write_builtins(out)?;
166 writeln!(out)?;
167
168 if let Some(ref pdir) = project_dir {
169 if pdir.exists() {
170 writeln!(out, "Project ({}):", pdir.display())?;
171 write_patterns_in(out, pdir)?;
172 writeln!(out)?;
173 }
174 }
175
176 write_user_section(out, &user_dir)
177}
178
179pub fn cmd_patterns() -> i32 {
181 match write_all_patterns(&mut io::stdout().lock()) {
182 Ok(()) => 0,
183 Err(e) => {
184 eprintln!("oo: {e}");
185 1
186 }
187 }
188}
189
190#[cfg(test)]
191#[path = "patterns_empty_tests.rs"]
192mod tests;