1use std::path::PathBuf;
2
3use clap::{Args, Subcommand, ValueEnum};
4
5#[derive(Copy, Clone, Debug, Eq, PartialEq, ValueEnum)]
6pub enum PresetTemplateKind {
7 App,
8 Shell,
9 Sys,
10}
11
12#[derive(Copy, Clone, Debug, Eq, PartialEq, ValueEnum)]
13pub enum PresetReportFormat {
14 Text,
15 Json,
16}
17
18#[derive(Copy, Clone, Debug, Eq, PartialEq, ValueEnum)]
19pub enum PresetPlatform {
20 Macos,
21 Linux,
22 Windows,
23}
24
25#[derive(Args, Debug)]
26pub struct ExportCommand {
27 #[arg(value_name = "DIR")]
29 pub dir: Option<PathBuf>,
30 #[arg(long, short = 'f')]
32 pub force: bool,
33}
34
35#[derive(Args, Debug)]
36pub struct CopyCommand {
37 #[arg(value_name = "KIND/NAME", value_parser = parse_copy_target)]
39 pub target: String,
40 #[arg(long, short = 'f')]
42 pub force: bool,
43}
44
45pub(crate) fn parse_copy_target(value: &str) -> Result<String, String> {
46 if value.contains('\\') {
47 return Err(format!(
48 "invalid preset target '{value}': expected app/name, shell/name, or sys/name"
49 ));
50 }
51
52 let mut parts = value.split('/');
53 let kind = parts.next().unwrap_or_default();
54 let name = parts.next().unwrap_or_default();
55 if parts.next().is_some()
56 || !matches!(kind, "app" | "shell" | "sys")
57 || name.is_empty()
58 || matches!(name, "." | "..")
59 {
60 return Err(format!(
61 "invalid preset target '{value}': expected app/name, shell/name, or sys/name"
62 ));
63 }
64 Ok(value.to_string())
65}
66
67#[derive(Args, Debug)]
68pub struct LinkCommand {
69 #[arg(value_name = "PATH")]
71 pub path: PathBuf,
72 #[arg(long)]
74 pub create: bool,
75 #[arg(long)]
77 pub live: bool,
78}
79
80#[derive(Args, Debug)]
81pub struct OverlayLinkCommand {
82 #[arg(value_name = "PATH", conflicts_with = "git")]
84 pub path: Option<PathBuf>,
85 #[arg(long, value_name = "URL")]
88 pub git: Option<String>,
89 #[arg(long, value_name = "BRANCH", requires = "git")]
91 pub branch: Option<String>,
92 #[arg(long)]
94 pub create: bool,
95}
96
97#[derive(Subcommand, Debug)]
98pub enum OverlayCommands {
99 Link(OverlayLinkCommand),
101 Unlink,
103 Info,
105}
106
107#[derive(Subcommand, Debug)]
108pub enum PresetCommands {
109 New {
111 #[arg(value_enum)]
112 kind: PresetTemplateKind,
113 #[arg(long, short = 'f')]
115 force: bool,
116 },
117 Schema {
119 #[arg(long, value_enum, default_value_t = PresetReportFormat::Text)]
121 format: PresetReportFormat,
122 },
123 Validate {
125 #[arg(value_name = "PATH", default_value = ".")]
127 path: PathBuf,
128 #[arg(long, value_enum, default_value_t = PresetReportFormat::Text)]
130 format: PresetReportFormat,
131 },
132 Lint {
134 #[arg(value_name = "PATH", default_value = ".")]
136 path: PathBuf,
137 #[arg(long, value_enum, default_value_t = PresetReportFormat::Text)]
139 format: PresetReportFormat,
140 #[arg(long)]
142 deny_warnings: bool,
143 },
144 Plan {
146 #[arg(value_name = "CATEGORY", default_value = ".")]
148 path: PathBuf,
149 #[arg(long, value_enum)]
151 platform: PresetPlatform,
152 #[arg(long, value_enum, default_value_t = PresetReportFormat::Text)]
154 format: PresetReportFormat,
155 },
156 Test {
158 #[arg(value_name = "CATEGORY", default_value = ".")]
160 path: PathBuf,
161 #[arg(long, value_enum, default_value_t = PresetReportFormat::Text)]
163 format: PresetReportFormat,
164 },
165 Pack {
167 #[arg(value_name = "CATEGORY", default_value = ".")]
169 path: PathBuf,
170 #[arg(long, value_name = "FILE")]
172 output: PathBuf,
173 #[arg(long, short = 'f')]
175 force: bool,
176 #[arg(long, value_enum, default_value_t = PresetReportFormat::Text)]
178 format: PresetReportFormat,
179 },
180 Migrate {
182 #[arg(value_name = "PATH")]
184 path: Option<PathBuf>,
185 #[arg(long, conflicts_with = "yes")]
187 dry_run: bool,
188 #[arg(long)]
190 yes: bool,
191 #[arg(long, value_enum, default_value_t = PresetReportFormat::Text)]
193 format: PresetReportFormat,
194 },
195 Export(ExportCommand),
197 Copy(CopyCommand),
199 Link(LinkCommand),
201 Unlink,
203 Overlay {
205 #[command(subcommand)]
206 command: OverlayCommands,
207 },
208 Pull,
210}