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
use {
crate::ConcreteJobRef,
anyhow::{
bail,
Result,
},
clap::{
CommandFactory,
Parser,
},
termimad::ansi,
};
static INTRO: &str = "
**bacon** watches your rust project and runs jobs in background.
Documentation at https://dystroy.org/bacon
";
/// Launch arguments
#[derive(Debug, Parser)]
#[command(
author,
about,
version,
disable_version_flag = true,
disable_help_flag = true
)]
pub struct Args {
/// Print help information
#[arg(long)]
pub help: bool,
/// Print the version
#[arg(long)]
pub version: bool,
/// Print the path to the prefs file, create it if it doesn't exist
#[clap(long)]
pub prefs: bool,
/// Start in summary mode
#[clap(short = 's', long)]
pub summary: bool,
/// Start in full mode (not summary)
#[clap(short = 'S', long)]
pub no_summary: bool,
/// Start with lines wrapped
#[clap(short = 'w', long)]
pub wrap: bool,
/// Start with lines not wrapped
#[clap(short = 'W', long)]
pub no_wrap: bool,
/// Start with gui vertical order reversed
#[clap(long)]
pub reverse: bool,
/// Start with standard gui order (focus on top)
#[clap(long)]
pub no_reverse: bool,
/// List available jobs
#[clap(short = 'l', long)]
pub list_jobs: bool,
/// Don't access the network (jobs may use it, though)
#[clap(long)]
pub offline: bool,
/// Create a bacon.toml file, ready to be customized
#[clap(long)]
pub init: bool,
/// Job to launch eg `check`, `clippy`, customized ones, ...
#[clap(short = 'j', long, value_name = "job")]
pub job: Option<ConcreteJobRef>,
/// Ignore features of both the package and the bacon job
#[clap(long)]
pub no_default_features: bool,
/// Comma separated list of features to ask cargo to compile with
/// (if the job defines some, they're merged)
#[clap(long, value_name = "features")]
pub features: Option<String>,
/// Activate all available features
#[clap(long)]
pub all_features: bool,
/// Export locations in `.bacon-locations` file
#[clap(short = 'e', long)]
pub export_locations: bool,
/// Don't export locations
#[clap(short = 'E', long)]
pub no_export_locations: bool,
/// Path to watch (must be a rust directory or inside)
#[clap(short = 'p', long, value_name = "path")]
pub path: Option<String>,
#[clap()]
/// What to do: either a job, or a path, or both
pub args: Vec<String>,
#[clap(last = true)]
/// Arguments given to the job
pub additional_job_args: Vec<String>,
}
impl Args {
/// positional arguments in bacon command are a convenience
/// allowing to skip writing `-j`, `-p`, or both.
/// To be used, they must be copied to the `job` or
/// `path` values.
pub fn fix(&mut self) -> Result<()> {
let mut args = self.args.drain(..);
match (
args.next(),
args.next(),
self.job.is_none(),
self.path.is_none(),
) {
(Some(a), b, true, true) => {
if a.contains('.') || a.contains('/') {
// a is a path, it can't be job
self.path = Some(a);
self.job = b.map(|b| b.as_str().into());
} else {
self.job = Some(a.as_str().into());
self.path = b;
}
}
(Some(_), Some(_), _, _) => {
bail!("Too many arguments");
}
(Some(a), None, true, false) => {
self.job = Some(a.as_str().into());
}
(Some(a), None, false, true) => {
self.path = Some(a);
}
(Some(a), None, false, false) => {
bail!("Unexpected argument {:?}", a);
}
_ => {}
}
Ok(())
}
pub fn print_help(&self) {
let mut printer = clap_help::Printer::new(Args::command())
.with("introduction", INTRO)
.without("author");
let skin = printer.skin_mut();
skin.headers[0].compound_style.set_fg(ansi(204));
skin.bold.set_fg(ansi(204));
//skin.italic = termimad::CompoundStyle::with_fg(ansi(2));
printer.print_help();
}
}