Skip to main content

double_o/
commands_patterns.rs

1//! Pattern listing functionality for `oo patterns` commands.
2
3use std::path::Path;
4
5use crate::{init, learn, pattern};
6
7/// Print a single pattern entry with flags.
8///
9/// Helper function to reduce duplication in pattern listing code.
10/// Prints the command_match with optional success/failure flags.
11fn print_pattern_entry(cmd_match: &str, has_success: bool, has_failure: bool) {
12    if has_success || has_failure {
13        let mut flags = Vec::new();
14        if has_success {
15            flags.push("success");
16        }
17        if has_failure {
18            flags.push("failure");
19        }
20        println!("  {cmd_match}  [{}]", flags.join("] ["));
21    } else {
22        println!("  {cmd_match}");
23    }
24}
25
26/// Print pattern entries from a single directory, returning true if any were found.
27///
28/// Each line is printed with a two-space indent so callers can add section headers.
29pub fn list_patterns_in(dir: &Path) -> bool {
30    let entries = match std::fs::read_dir(dir) {
31        Ok(e) => e,
32        Err(_) => return false,
33    };
34
35    let mut found = false;
36    for entry in entries.flatten() {
37        let path = entry.path();
38        if path.extension().and_then(|e| e.to_str()) != Some("toml") {
39            continue;
40        }
41        let parsed = std::fs::read_to_string(&path)
42            .ok()
43            .and_then(|s| toml::from_str::<toml::Value>(&s).ok());
44
45        let cmd_match = parsed
46            .as_ref()
47            .and_then(|v| v.get("command_match")?.as_str().map(str::to_string));
48        let has_success = parsed.as_ref().and_then(|v| v.get("success")).is_some();
49        let has_failure = parsed.as_ref().and_then(|v| v.get("failure")).is_some();
50
51        if parsed.is_none() {
52            continue;
53        }
54        found = true;
55        let cmd_match = cmd_match.unwrap_or_else(|| "(unknown)".into());
56
57        print_pattern_entry(&cmd_match, has_success, has_failure);
58    }
59    found
60}
61
62/// List learned pattern files from a single directory (legacy test helper).
63pub fn cmd_patterns_in(dir: &Path) -> i32 {
64    if !list_patterns_in(dir) {
65        println!("no learned patterns yet");
66    }
67    0
68}
69
70/// List built-in patterns.
71fn list_builtins_in() -> bool {
72    let patterns = pattern::builtins();
73    if patterns.is_empty() {
74        return false;
75    }
76
77    for pat in patterns {
78        let cmd_match = pat.command_match.as_str();
79        let has_success = pat.success.is_some();
80        let has_failure = pat.failure.is_some();
81
82        print_pattern_entry(cmd_match, has_success, has_failure);
83    }
84    true
85}
86
87/// List patterns from both project-local and user config directories.
88pub fn cmd_patterns() -> i32 {
89    let project_dir = std::env::current_dir()
90        .map(|cwd| init::project_patterns_dir(&cwd))
91        .ok();
92    let user_dir = learn::patterns_dir();
93
94    // Show built-in patterns first
95    println!("Built-in ({} patterns):", pattern::builtins().len());
96    list_builtins_in();
97    println!();
98
99    if let Some(ref pdir) = project_dir {
100        if pdir.exists() {
101            println!("Project ({}):", pdir.display());
102            list_patterns_in(pdir);
103            println!();
104        }
105    }
106
107    println!("User ({}):", user_dir.display());
108    list_patterns_in(&user_dir);
109
110    0
111}