dodot_lib/commands/list.rs
1//! `list` command — show all available packs.
2
3use serde::Serialize;
4
5use crate::packs;
6use crate::packs::orchestration::ExecutionContext;
7use crate::Result;
8
9#[derive(Debug, Clone, Serialize)]
10pub struct ListResult {
11 pub packs: Vec<ListPack>,
12}
13
14#[derive(Debug, Clone, Serialize)]
15pub struct ListPack {
16 /// User-facing pack name. For prefixed packs (`010-nvim`) this is
17 /// the stripped form (`nvim`); for unprefixed packs it equals the
18 /// directory name.
19 pub name: String,
20 pub ignored: bool,
21}
22
23/// List all packs in the dotfiles root.
24///
25/// Packs appear in the order dodot would apply them (lexicographic by
26/// on-disk directory name); see the `packs` module docs for the pack
27/// ordering contract. Goes through [`packs::scan_packs`] so list
28/// output respects the same `pack.ignore` patterns and surfaces the
29/// same scan-time errors (empty-stem prefix directories, ordering
30/// collisions) that every other command does — `dodot list` should
31/// never show ambiguous duplicates that `dodot up` would refuse.
32pub fn list(ctx: &ExecutionContext) -> Result<ListResult> {
33 let root_config = ctx.config_manager.root_config()?;
34 let scanned = packs::scan_packs(
35 ctx.fs.as_ref(),
36 ctx.paths.dotfiles_root(),
37 &root_config.pack.ignore,
38 )?;
39
40 // Two streams in: active packs (already carry display_name) and
41 // dodotignore-marked dirs (raw names). Merge into one list with
42 // the `ignored` flag, preserving lex order on the on-disk name
43 // so the displayed order still matches deploy order.
44 let mut entries: Vec<(String, ListPack)> = Vec::new();
45 for p in scanned.packs {
46 entries.push((
47 p.name.clone(),
48 ListPack {
49 name: p.display_name,
50 ignored: false,
51 },
52 ));
53 }
54 for dir in scanned.ignored {
55 let display = packs::display_name_for(&dir).to_string();
56 entries.push((
57 dir,
58 ListPack {
59 name: display,
60 ignored: true,
61 },
62 ));
63 }
64 entries.sort_by(|a, b| a.0.cmp(&b.0));
65
66 Ok(ListResult {
67 packs: entries.into_iter().map(|(_, p)| p).collect(),
68 })
69}