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