-
Notifications
You must be signed in to change notification settings - Fork 44
/
cli.rs
189 lines (167 loc) · 6.19 KB
/
cli.rs
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
use clap::{CommandFactory, Parser, Subcommand};
use std::collections::HashSet;
#[derive(Parser, Debug, Clone)]
#[clap(author, version, about, long_about = None)]
pub struct DinghyGeneralArgs {
/// Use a specific platform
#[clap(long, short)]
pub platform: Option<String>,
/// Make output more verbose, can be passed multiple times
#[clap(long, short, parse(from_occurrences))]
pub verbose: i8,
/// Make output less verbose, can be passed multiple times
#[clap(long, short, parse(from_occurrences))]
pub quiet: i8,
/// Force the use of an overlay during project build, can be passed multiple times
#[clap(long, short)]
pub overlay: Vec<String>,
/// Env variables to set on target device e.g. RUST_TRACE=trace, can be passed multiple times
#[clap(long, short)]
pub env: Vec<String>,
/// Cleanup target device after completion
#[clap(long, short)]
pub cleanup: bool,
/// Strip executable before running it on target
#[clap(long, short)]
pub strip: bool,
/// Device hint
#[clap(long, short)]
pub device: Option<String>,
/// Either a dinghy subcommand (see cargo dinghy all-dinghy-subcommands) or a
/// cargo one (see cargo --list)
// this one is here so that the help generated by clap makes sense
pub subcommand: Vec<String>,
}
#[derive(Parser, Debug)]
pub struct SubCommandWrapper {
#[clap(subcommand)]
subcommand: DinghySubcommand,
}
#[derive(Subcommand, Debug)]
pub enum DinghySubcommand {
#[clap(name = "devices")]
/// List devices that can be used with Dinghy for the selected platform
Devices {},
#[clap(name = "all-devices")]
/// List all devices that can be used with Dinghy
AllDevices {},
#[clap(name = "all-platforms")]
/// List all platforms known to dinghy
AllPlatforms {},
#[clap(name = "all-dinghy-subcommands")]
/// List all available dinghy subcommands
AllDinghySubcommands {},
#[clap(name = "runner")]
/// Dinghy runner, used internally to run executables on targets
Runner { args: Vec<String> },
#[clap(name = "run-with")]
/// Build an artifact and run it on a target device using the provided wrapper
RunWith {
/// Wrapper crate to use to run the lib
#[clap(long, short('c'))]
wrapper_crate: String,
// TODO support executables / scripts as wrappers
// /// Wrapper executable to use to run the lib
// #[clap(long, short('e'))]
// wrapper_executable: Option<String>,
/// Arguments to cargo build for the artifact
lib_build_args: Vec<String>,
},
}
#[derive(Debug)]
pub enum DinghyMode {
DinghySubcommand(DinghySubcommand),
CargoSubcommand { args: Vec<String> },
Naked,
}
#[derive(Debug)]
pub struct DinghyCli {
pub args: DinghyGeneralArgs,
pub mode: DinghyMode,
}
impl DinghyCli {
pub fn parse() -> Self {
log::debug!("args {:?}", std::env::args().collect::<Vec<_>>());
let args = std::env::args().skip(1).skip_while(|it| it == "dinghy");
#[derive(Debug, Default)]
struct SplitArgs {
general_args: Vec<String>,
subcommand: Vec<String>,
}
let args_taking_value = DinghyGeneralArgs::command()
.get_arguments()
.filter_map(|arg| {
if arg.is_takes_value_set() {
let mut values = vec![];
if let Some(shorts) = arg.get_short_and_visible_aliases() {
values
.append(&mut shorts.iter().map(|short| format!("-{}", short)).collect())
}
if let Some(longs) = arg.get_long_and_visible_aliases() {
values.append(&mut longs.iter().map(|long| format!("--{}", long)).collect())
}
if values.is_empty() {
None
} else {
Some(values)
}
} else {
None
}
})
.flatten()
.collect::<HashSet<_>>();
let split_args = args.fold(SplitArgs::default(), |mut split_args, elem| {
if !split_args.subcommand.is_empty() {
// we've started putting args in the sub command, let's continue
split_args.subcommand.push(elem)
} else {
if elem.starts_with("-") /* This is a new option */
|| split_args.general_args
.last()
.map(|it| args_taking_value.contains(it))
.unwrap_or(false)
/* value for the previous option */
{
split_args.general_args.push(elem)
} else {
// leve the start of the subcommand here so that clap can verify it is there
split_args.general_args.push(elem.clone());
// this is the start of the sub command
split_args.subcommand.push(elem)
}
}
split_args
});
let cli = DinghyCli {
args: Parser::parse_from(
vec!["dinghy".to_string()]
.into_iter()
.chain(split_args.general_args),
),
mode: split_args
.subcommand
.first()
.cloned()
.map(|subcommand| {
if DinghySubcommand::has_subcommand(&subcommand) {
DinghyMode::DinghySubcommand(
SubCommandWrapper::parse_from(
vec!["dinghy".to_string()]
.into_iter()
.chain(split_args.subcommand),
)
.subcommand,
)
} else {
DinghyMode::CargoSubcommand {
args: split_args.subcommand,
}
}
})
.unwrap_or(DinghyMode::Naked),
};
log::debug!("cli {:?}", cli);
cli
}
}