1use anyhow::Result;
2use clap::Parser;
3
4use crate::cli::GardenOptions;
5use crate::{constants, display, model, query};
6
7#[derive(Parser, Clone, Debug)]
9#[command(author, about, long_about)]
10pub struct ListOptions {
11 #[arg(short, long, default_value_t = false)]
13 all: bool,
14 #[arg(long, short = 'C', default_value_t = false)]
16 no_commands: bool,
17 #[arg(long = "commands", short = 'c', default_value_t = false)]
19 only_commands: bool,
20 #[arg(long, short = 'N', default_value_t = false)]
22 no_gardens: bool,
23 #[arg(long, short = 'G', default_value_t = false)]
25 no_groups: bool,
26 #[arg(short, long, default_value_t = false)]
28 reverse: bool,
29 #[arg(
31 long,
32 short,
33 require_equals = true,
34 num_args = 0..=1,
35 default_value_t = model::TreeSortMode::None,
36 default_missing_value = "name",
37 value_name = "MODE",
38 value_parser = model::TreeSortMode::parse_from_str,
39 )]
40 sort: model::TreeSortMode,
41 #[arg(short, long, action = clap::ArgAction::Count)]
43 verbose: u8,
44 #[arg(short, long, default_value_t = false)]
46 worktrees: bool,
47 #[arg(long, short, default_value = "*")]
49 trees: String,
50 queries: Vec<String>,
52}
53
54pub fn main(app_context: &model::ApplicationContext, options: &mut ListOptions) -> Result<()> {
56 if options.queries.is_empty() {
57 options.queries.push(string!("@*"));
58 }
59 list(app_context, options)
60}
61
62fn list(app_context: &model::ApplicationContext, options: &ListOptions) -> Result<()> {
64 let config = app_context.get_root_config();
65 let display_all = options.all;
66 let display_worktrees = options.worktrees;
67 let show_commands = !options.no_commands;
68 let only_commands = options.only_commands;
69 let show_gardens = !only_commands && !options.no_gardens;
70 let show_groups = !only_commands && !options.no_groups;
71 let verbose = app_context.options.verbose + options.verbose;
72 let mut needs_newline = false;
73
74 if app_context.options.debug_level(constants::DEBUG_LEVEL_LIST) > 0 {
75 debug!("queries: {:?}", options.queries);
76 }
77
78 for query in &options.queries {
79 let mut contexts =
81 query::resolve_and_filter_trees(app_context, config, query, &options.trees);
82 match options.sort {
83 model::TreeSortMode::None => (),
84 model::TreeSortMode::Name => {
85 contexts.sort_by(|context_a, context_b| context_a.tree.cmp(&context_b.tree));
86 }
87 model::TreeSortMode::Time => {
88 contexts.sort_by(|context_a, context_b| {
89 let config_a = match context_a.config {
90 Some(config_id) => app_context.get_config(config_id),
91 None => config,
92 };
93 let config_b = match context_b.config {
94 Some(config_id) => app_context.get_config(config_id),
95 None => config,
96 };
97 match (
98 config_a
99 .trees
100 .get(&context_a.tree)
101 .and_then(|tree| tree.pathbuf())
102 .and_then(|pathbuf| pathbuf.metadata().ok())
103 .and_then(|metadata| metadata.modified().ok()),
104 config_b
105 .trees
106 .get(&context_b.tree)
107 .and_then(|tree| tree.pathbuf())
108 .and_then(|pathbuf| pathbuf.metadata().ok())
109 .and_then(|metadata| metadata.modified().ok()),
110 ) {
111 (Some(a), Some(b)) => a.cmp(&b),
112 (None, Some(_)) => std::cmp::Ordering::Less,
113 (Some(_), None) => std::cmp::Ordering::Greater,
114 (None, None) => std::cmp::Ordering::Equal,
115 }
116 });
117 }
118 }
119 if options.reverse {
120 contexts.reverse();
121 }
122 for (idx, context) in contexts.iter().enumerate() {
124 let config = match context.config {
125 Some(config_id) => app_context.get_config(config_id),
126 None => config,
127 };
128 let tree = match config.trees.get(&context.tree) {
129 Some(tree) => tree,
130 None => continue,
131 };
132 let path = match tree.path_as_ref() {
133 Ok(path) => path,
134 Err(_) => continue,
135 };
136 if !std::path::PathBuf::from(&path).exists() {
138 if needs_newline {
139 println!();
140 }
141 display::print_missing_tree(tree, path, verbose);
142 if display_all {
143 if !only_commands {
144 display::print_tree_extended_details(
145 app_context,
146 context,
147 tree,
148 display_worktrees,
149 );
150 }
151 if show_commands && !tree.commands.is_empty() {
152 display::print_commands(&tree.commands);
153 }
154 }
155 needs_newline = display_all;
156 continue;
157 }
158
159 if tree.is_symlink {
160 if needs_newline {
161 println!();
162 }
163 display::print_symlink_tree_entry(tree, path, verbose);
164 needs_newline = false;
165 continue;
166 }
167
168 if idx > 0 {
169 println!();
170 }
171 display::print_tree(tree, config.tree_branches, verbose, false, false);
172 if !only_commands {
173 display::print_tree_extended_details(app_context, context, tree, display_worktrees);
174 }
175 if show_commands && !tree.commands.is_empty() {
176 display::print_commands(&tree.commands);
177 }
178 needs_newline = true;
179 }
180 }
181
182 if show_groups && !config.groups.is_empty() {
183 println!();
184 display::print_groups(&config.groups);
185 }
186
187 if show_gardens && !config.gardens.is_empty() {
188 println!();
189 display::print_gardens(&config.gardens);
190 }
191
192 if show_commands && !config.commands.is_empty() {
193 println!();
194 display::print_commands(&config.commands);
195 }
196
197 Ok(())
198}