1use std::path::PathBuf;
2
3use clap::{ArgAction, Parser, ValueEnum};
4use serde::Deserialize;
5
6use crate::i18n::Language;
7
8pub const DEFAULT_DOWNLOAD_CONCURRENCY: usize = 4;
9
10fn parse_concurrency(value: &str) -> Result<usize, String> {
11 let parsed = value
12 .parse::<usize>()
13 .map_err(|_| "concurrency must be a positive integer".to_string())?;
14 if parsed == 0 {
15 return Err("concurrency must be at least 1".to_string());
16 }
17 Ok(parsed)
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum, Default, Deserialize)]
21#[serde(rename_all = "lowercase")]
22pub enum PrefixProxyMode {
23 #[default]
24 Direct,
25 Fallback,
26 Prefer,
27}
28
29impl PrefixProxyMode {
30 pub fn as_str(self) -> &'static str {
31 match self {
32 Self::Direct => "direct",
33 Self::Fallback => "fallback",
34 Self::Prefer => "prefer",
35 }
36 }
37}
38
39#[derive(Debug, Parser, Clone)]
40#[command(name = "gh-download", version, long_about = None)]
41pub(crate) struct ParsedCli {
42 #[arg()]
43 pub repo: String,
44
45 #[arg()]
46 pub remote_path: String,
47
48 #[arg()]
49 pub local_target: PathBuf,
50
51 #[arg(long, value_name = "PATH")]
52 pub config: Option<PathBuf>,
53
54 #[arg(long = "ref", value_name = "REF")]
55 pub git_ref: Option<String>,
56
57 #[arg(long, value_name = "TOKEN")]
58 pub token: Option<String>,
59
60 #[arg(long, value_name = "URL")]
61 pub api_base: Option<String>,
62
63 #[arg(long, value_name = "URL")]
64 pub proxy_base: Option<String>,
65
66 #[arg(long = "prefix-mode", value_enum, value_name = "MODE")]
67 pub prefix_mode: Option<PrefixProxyMode>,
68
69 #[arg(
70 short = 'c',
71 long,
72 value_name = "N",
73 value_parser = parse_concurrency
74 )]
75 pub concurrency: Option<usize>,
76
77 #[arg(long = "lang", value_enum, value_name = "LANG")]
78 pub language: Option<Language>,
79
80 #[arg(long, action = ArgAction::SetTrue)]
81 pub overwrite: bool,
82
83 #[arg(long, action = ArgAction::SetTrue)]
84 pub json: bool,
85
86 #[arg(long, action = ArgAction::SetTrue)]
87 pub debug: bool,
88
89 #[arg(long, action = ArgAction::SetTrue)]
90 pub no_color: bool,
91}
92
93#[derive(Debug, Clone, PartialEq, Eq)]
94pub struct CliInvocation {
95 pub cli: Cli,
96 pub config_path: Option<PathBuf>,
97 pub explicit_concurrency: Option<usize>,
98}
99
100#[derive(Debug, Parser, Clone, PartialEq, Eq)]
101#[command(name = "gh-download", version, long_about = None)]
102pub struct Cli {
103 #[arg()]
104 pub repo: String,
105
106 #[arg()]
107 pub remote_path: String,
108
109 #[arg()]
110 pub local_target: PathBuf,
111
112 #[arg(long = "ref", value_name = "REF")]
113 pub git_ref: Option<String>,
114
115 #[arg(long, value_name = "TOKEN")]
116 pub token: Option<String>,
117
118 #[arg(long, value_name = "URL")]
119 pub api_base: Option<String>,
120
121 #[arg(long, value_name = "URL")]
122 pub proxy_base: Option<String>,
123
124 #[arg(long = "prefix-mode", value_enum, value_name = "MODE")]
125 pub prefix_mode: Option<PrefixProxyMode>,
126
127 #[arg(
128 short = 'c',
129 long,
130 value_name = "N",
131 default_value_t = DEFAULT_DOWNLOAD_CONCURRENCY,
132 value_parser = parse_concurrency
133 )]
134 pub concurrency: usize,
135
136 #[arg(long = "lang", value_enum, value_name = "LANG")]
137 pub language: Option<Language>,
138
139 #[arg(long, action = ArgAction::SetTrue)]
140 pub overwrite: bool,
141
142 #[arg(long, action = ArgAction::SetTrue)]
143 pub json: bool,
144
145 #[arg(long, action = ArgAction::SetTrue)]
146 pub debug: bool,
147
148 #[arg(long, action = ArgAction::SetTrue)]
149 pub no_color: bool,
150}
151
152#[derive(Debug, Clone, PartialEq, Eq)]
153pub struct ResolvedOptions {
154 pub repo: String,
155 pub remote_path: String,
156 pub local_target: PathBuf,
157 pub git_ref: Option<String>,
158 pub token: Option<String>,
159 pub api_base: String,
160 pub proxy_base: String,
161 pub prefix_mode: PrefixProxyMode,
162 pub concurrency: usize,
163 pub language: Language,
164 pub overwrite: bool,
165 pub json: bool,
166 pub debug: bool,
167 pub no_color: bool,
168}
169
170impl From<ParsedCli> for CliInvocation {
171 fn from(value: ParsedCli) -> Self {
172 let explicit_concurrency = value.concurrency;
173 let cli = Cli {
174 repo: value.repo,
175 remote_path: value.remote_path,
176 local_target: value.local_target,
177 git_ref: value.git_ref,
178 token: value.token,
179 api_base: value.api_base,
180 proxy_base: value.proxy_base,
181 prefix_mode: value.prefix_mode,
182 concurrency: explicit_concurrency.unwrap_or(DEFAULT_DOWNLOAD_CONCURRENCY),
183 language: value.language,
184 overwrite: value.overwrite,
185 json: value.json,
186 debug: value.debug,
187 no_color: value.no_color,
188 };
189
190 Self {
191 cli,
192 config_path: value.config,
193 explicit_concurrency,
194 }
195 }
196}