tui_dispatch_debug/
cli.rs1use clap::Args;
2use std::path::PathBuf;
3
4use crate::debug::ActionLoggerConfig;
5use crate::pattern_utils::split_patterns_csv;
6
7#[derive(Args, Debug, Clone, Default)]
9#[command(next_help_heading = "Debug")]
10pub struct DebugCliArgs {
11 #[arg(long = "debug")]
13 pub enabled: bool,
14
15 #[arg(long = "debug-render-once")]
17 pub render_once: bool,
18
19 #[arg(long = "debug-state-in")]
21 pub state_in: Option<PathBuf>,
22
23 #[arg(long = "debug-actions-in")]
25 pub actions_in: Option<PathBuf>,
26
27 #[arg(long = "debug-actions-out")]
29 pub actions_out: Option<PathBuf>,
30
31 #[arg(long = "debug-actions-include")]
33 pub actions_include: Option<String>,
34
35 #[arg(long = "debug-actions-exclude")]
37 pub actions_exclude: Option<String>,
38
39 #[arg(long = "debug-actions-include-categories")]
41 pub actions_include_categories: Option<String>,
42
43 #[arg(long = "debug-actions-exclude-categories")]
45 pub actions_exclude_categories: Option<String>,
46
47 #[arg(long = "debug-actions-include-names")]
49 pub actions_include_names: Option<String>,
50
51 #[arg(long = "debug-actions-exclude-names")]
53 pub actions_exclude_names: Option<String>,
54
55 #[arg(long = "debug-state-schema-out")]
57 pub state_schema_out: Option<PathBuf>,
58
59 #[arg(long = "debug-actions-schema-out")]
61 pub actions_schema_out: Option<PathBuf>,
62
63 #[arg(long = "debug-replay-timeout", default_value_t = 30)]
65 pub replay_timeout: u64,
66}
67
68impl DebugCliArgs {
69 pub fn action_filter(&self) -> ActionLoggerConfig {
70 let mut include_patterns = split_patterns(self.actions_include.as_deref());
71 include_patterns.extend(category_filters(self.actions_include_categories.as_deref()));
72 include_patterns.extend(name_filters(self.actions_include_names.as_deref()));
73
74 let mut exclude_patterns = split_patterns(self.actions_exclude.as_deref());
75 exclude_patterns.extend(category_filters(self.actions_exclude_categories.as_deref()));
76 exclude_patterns.extend(name_filters(self.actions_exclude_names.as_deref()));
77
78 if include_patterns.is_empty() && exclude_patterns.is_empty() {
79 ActionLoggerConfig::default()
80 } else {
81 ActionLoggerConfig::with_patterns(include_patterns, exclude_patterns)
82 }
83 }
84
85 pub fn auto_fetch(&self) -> bool {
86 self.state_in.is_none() && self.actions_in.is_none()
87 }
88}
89
90fn split_patterns(value: Option<&str>) -> Vec<String> {
91 value.map(split_patterns_csv).unwrap_or_default()
92}
93
94fn category_filters(value: Option<&str>) -> Vec<String> {
95 split_patterns(value)
96 .into_iter()
97 .map(|category| format!("cat:{}", category.to_ascii_lowercase()))
98 .collect()
99}
100
101fn name_filters(value: Option<&str>) -> Vec<String> {
102 split_patterns(value)
103 .into_iter()
104 .map(|name| format!("name:{name}"))
105 .collect()
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn test_action_filter_defaults() {
114 let args = DebugCliArgs::default();
115 let filter = args.action_filter();
116 assert!(!filter.should_log("Tick"));
117 assert!(filter.should_log("Connect"));
118 }
119
120 #[test]
121 fn test_action_filter_include_disables_default_excludes() {
122 let args = DebugCliArgs {
123 actions_include: Some("Tick".to_string()),
124 ..DebugCliArgs::default()
125 };
126 let filter = args.action_filter();
127 assert!(filter.should_log("Tick"));
128 }
129
130 #[test]
131 fn test_action_filter_category_and_name_flags() {
132 let args = DebugCliArgs {
133 actions_include_categories: Some("search".to_string()),
134 actions_exclude_names: Some("SearchSubmit".to_string()),
135 ..DebugCliArgs::default()
136 };
137 let filter = args.action_filter();
138 assert!(filter.should_log("SearchStart"));
139 assert!(!filter.should_log("SearchSubmit"));
140 assert!(!filter.should_log("Connect"));
141 }
142}