1use std::path::PathBuf;
2
3use clap::{Args, Parser, Subcommand};
4
5use clap_complete::Shell;
6use conjure_cp::settings::{
7 DEFAULT_MINION_DISCRETE_THRESHOLD, Parser as InputParser, QuantifiedExpander, Rewriter,
8 SolverFamily,
9};
10use conjure_cp::solver::adaptors::MinionValueOrder;
11
12use crate::{pretty, solve, test_solve};
13
14pub(crate) const DEBUG_HELP_HEADING: Option<&str> = Some("Debug");
15pub(crate) const LOGGING_HELP_HEADING: Option<&str> = Some("Logging & Output");
16pub(crate) const CONFIGURATION_HELP_HEADING: Option<&str> = Some("Configuration");
17pub(crate) const OPTIMISATIONS_HELP_HEADING: Option<&str> = Some("Optimisations");
18
19#[derive(Clone, Debug, Subcommand)]
21pub enum Command {
22 Solve(solve::Args),
24 PrintJsonSchema,
26 TestSolve(test_solve::Args),
31 Completion(CompletionArgs),
33 Pretty(pretty::Args),
34 ServerLSP,
36}
37
38#[derive(Clone, Debug, Parser)]
40#[command(
41 author,
42 about = "Conjure Oxide: Automated Constraints Modelling Toolkit",
43 before_help = "Full documentation can be found online at: https://conjure-cp.github.io/conjure-oxide"
44)]
45pub struct Cli {
46 #[command(subcommand)]
47 pub subcommand: Command,
48
49 #[command(flatten)]
50 pub global_args: GlobalArgs,
51
52 #[arg(long = "version", short = 'V')]
54 pub version: bool,
55}
56
57#[derive(Debug, Clone, Args)]
58pub struct GlobalArgs {
59 #[arg(long, value_name = "EXTRA_RULE_SETS", global = true)]
61 pub extra_rule_sets: Vec<String>,
62
63 #[arg(long, short = 'v', help = "Log verbosely to stderr", global = true, help_heading = LOGGING_HELP_HEADING)]
65 pub verbose: bool,
66
67 #[arg(
72 long,
73 overrides_with = "_no_check_equally_applicable_rules",
74 default_value_t = false,
75 global = true,
76 help_heading= DEBUG_HELP_HEADING
77 )]
78 pub check_equally_applicable_rules: bool,
79
80 #[arg(long, global = true, help_heading=LOGGING_HELP_HEADING)]
82 pub rule_trace: Option<PathBuf>,
83
84 #[arg(long, global = true, help_heading=LOGGING_HELP_HEADING)]
89 pub rule_trace_aggregates: Option<PathBuf>,
90
91 #[arg(long, default_value_t = false, global = true, help_heading=LOGGING_HELP_HEADING)]
96 pub rule_trace_cdp: bool,
97
98 #[arg(long, global = true, help_heading=LOGGING_HELP_HEADING)]
102 pub rule_trace_verbose: Option<PathBuf>,
103
104 #[arg(long, global = true, help_heading = DEBUG_HELP_HEADING)]
108 pub _no_check_equally_applicable_rules: bool,
109
110 #[arg(
114 long,
115 default_value_t = InputParser::ViaConjure,
116 value_parser = parse_parser,
117 global = true,
118 help_heading = CONFIGURATION_HELP_HEADING
119 )]
120 pub parser: InputParser,
121
122 #[arg(long, default_value_t = Rewriter::Naive, value_parser = parse_rewriter, global = true, help_heading = CONFIGURATION_HELP_HEADING)]
126 pub rewriter: Rewriter,
127
128 #[arg(
132 long,
133 default_value_t = QuantifiedExpander::Native,
134 value_parser = parse_comprehension_expander,
135 global = true,
136 help_heading = CONFIGURATION_HELP_HEADING
137 )]
138 pub comprehension_expander: QuantifiedExpander,
139
140 #[arg(
146 long,
147 value_name = "SOLVER",
148 value_parser = parse_solver_family,
149 default_value = "minion",
150 short = 's',
151 global = true,
152 help_heading = CONFIGURATION_HELP_HEADING
153 )]
154 pub solver: SolverFamily,
155
156 #[arg(
160 long,
161 default_value_t = DEFAULT_MINION_DISCRETE_THRESHOLD,
162 global = true,
163 help_heading = CONFIGURATION_HELP_HEADING
164 )]
165 pub minion_discrete_threshold: usize,
166
167 #[arg(
171 long,
172 value_name = "ORDER",
173 value_parser = parse_minion_value_order,
174 global = true,
175 help_heading = CONFIGURATION_HELP_HEADING
176 )]
177 pub minion_valorder: Option<MinionValueOrder>,
178
179 #[arg(long,global=true, value_names=["filename"], next_line_help=true, help_heading=LOGGING_HELP_HEADING)]
188 pub save_solver_input_file: Option<PathBuf>,
189
190 #[arg(long, global = true, help_heading = OPTIMISATIONS_HELP_HEADING)]
194 pub solver_timeout: Option<humantime::Duration>,
195
196 #[arg(long, default_value_t = false, global = true, help_heading = LOGGING_HELP_HEADING)]
198 pub log: bool,
199
200 #[arg(long, value_name = "LOGFILE", global = true, help_heading = LOGGING_HELP_HEADING)]
202 pub logfile: Option<PathBuf>,
203
204 #[arg(long, value_name = "JSON LOGFILE", global = true, help_heading = LOGGING_HELP_HEADING)]
206 pub logfile_json: Option<PathBuf>,
207}
208
209#[derive(Debug, Clone, Args)]
210pub struct CompletionArgs {
211 #[arg(value_enum)]
213 pub shell: Shell,
214}
215
216#[derive(Debug, Clone, Copy, clap::ValueEnum)]
217pub enum ShellTypes {
218 Bash,
219 Zsh,
220 Fish,
221 PowerShell,
222 Elvish,
223}
224
225fn parse_comprehension_expander(input: &str) -> Result<QuantifiedExpander, String> {
226 input.parse()
227}
228
229fn parse_rewriter(input: &str) -> Result<Rewriter, String> {
230 input.parse::<Rewriter>()
231}
232
233fn parse_solver_family(input: &str) -> Result<SolverFamily, String> {
234 input.parse()
235}
236
237fn parse_parser(input: &str) -> Result<InputParser, String> {
238 input.parse()
239}
240
241fn parse_minion_value_order(input: &str) -> Result<MinionValueOrder, String> {
242 match input {
243 "ascend" => Ok(MinionValueOrder::Ascend),
244 "descend" => Ok(MinionValueOrder::Descend),
245 "random" => Ok(MinionValueOrder::Random),
246 other => Err(format!(
247 "unknown minion value order '{other}', expected one of: ascend, descend, random"
248 )),
249 }
250}