1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
use clap::{Args, Parser, Subcommand};
use std::path::PathBuf;
#[derive(Parser)]
#[command(
version,
about = "Configuration backup and restore",
arg_required_else_help = true
)]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
}
#[derive(Subcommand)]
pub enum Commands {
/// Back up files from a YAML configuration
Backup(FileArgs),
/// Restore files from a backup
Restore(RestoreArgs),
/// Check backup manifest and configured backup paths
Verify {
/// Path to the YAML configuration
config_file: PathBuf,
},
/// Compare a backup with the files it would restore
Diff {
/// Path to the YAML configuration
config_file: PathBuf,
/// Show changed paths without content differences
#[arg(long)]
summary: bool,
},
/// Run configured hooks
Hook {
#[command(flatten)]
config: ConfigArgs,
/// Run a named hook. Select hooks interactively when omitted
#[arg(short, long)]
name: Option<String>,
},
/// Detect configuration paths from Homebrew casks
Detect {
/// Homebrew cask names. Detect all installed casks when omitted
casks: Vec<String>,
},
/// Find ingredients in the GitHub library
Ingredient {
#[command(subcommand)]
command: IngredientCommand,
},
/// Manage the ingredient and library-list caches
Cache {
#[command(subcommand)]
command: CacheCommand,
},
/// Print a commented YAML configuration template
Template,
/// Generate shell completion script
Completions {
/// Shell to generate completions for
#[arg(value_enum)]
shell: clap_complete::Shell,
},
}
#[derive(Subcommand)]
pub enum IngredientCommand {
/// Print an ingredient's YAML, downloading and caching it when needed
Show {
/// Ingredient name from the GitHub library
name: String,
},
/// Search available ingredients in the GitHub library
Search {
/// Case-insensitive ingredient name search (quote queries containing spaces)
query: String,
},
}
#[derive(Subcommand)]
pub enum CacheCommand {
/// Show cache location, contents, and freshness without network access
Status,
/// List downloaded ingredients without network access
List,
/// Show one downloaded ingredient's cache metadata
Show {
/// Name of the cached ingredient
name: String,
},
/// Refresh one ingredient, all downloaded ingredients, the catalog, or both
#[command(
override_usage = "cockup cache refresh [NAME|--ingredients|--catalog]",
after_help = "With no choice, refresh all downloaded ingredients and the catalog.",
group(clap::ArgGroup::new("scope").multiple(false).args(["name", "ingredients", "catalog"]))
)]
Refresh {
/// Refresh this ingredient (ask before downloading when absent)
name: Option<String>,
/// Refresh all downloaded ingredients only
#[arg(long)]
ingredients: bool,
/// Refresh the cached GitHub library list only
#[arg(long)]
catalog: bool,
},
/// Delete cached ingredients or the library list
#[command(group(clap::ArgGroup::new("target")
.required(true)
.multiple(false)
.args(["names", "ingredients", "catalog"])))]
Delete {
/// One or more cached ingredient names
names: Vec<String>,
/// Delete only the cached library list
#[arg(long)]
catalog: bool,
/// Delete all downloaded ingredient YAML and metadata caches
#[arg(long)]
ingredients: bool,
},
/// Remove the entire Cockup cache directory
Clean,
}
#[derive(Args)]
pub struct ConfigArgs {
/// Path to the YAML configuration
pub config_file: PathBuf,
/// Run configured hooks without asking for confirmation
#[arg(short = 'a', long)]
pub approve_hooks: bool,
}
#[derive(Args)]
pub struct FileArgs {
#[command(flatten)]
pub config: ConfigArgs,
/// Show planned file changes without writing files or running hooks
#[arg(long)]
pub dry_run: bool,
}
#[derive(Args)]
#[command(
override_usage = "cockup restore [OPTIONS] [--override|--skip-existing] <CONFIG_FILE>",
after_help = "By default, ask before replacing conflicting local files and symlinks.",
group(clap::ArgGroup::new("existing_files").multiple(false).args(["override", "skip_existing"]))
)]
pub struct RestoreArgs {
#[command(flatten)]
pub file: FileArgs,
/// Replace existing local files without asking
#[arg(short = 'o', long)]
pub r#override: bool,
/// Keep existing local files and restore only missing paths
#[arg(short = 's', long)]
pub skip_existing: bool,
}