1
use std::path::PathBuf;
2

            
3
use clap::{Args, Parser, Subcommand};
4

            
5
use clap_complete::Shell;
6
use conjure_cp::settings::{Parser as InputParser, QuantifiedExpander, Rewriter, SolverFamily};
7

            
8
use crate::{pretty, solve, test_solve};
9

            
10
pub(crate) const DEBUG_HELP_HEADING: Option<&str> = Some("Debug");
11
pub(crate) const LOGGING_HELP_HEADING: Option<&str> = Some("Logging & Output");
12
pub(crate) const EXPERIMENTAL_HELP_HEADING: Option<&str> = Some("Experimental");
13
pub(crate) const OPTIMISATIONS_HELP_HEADING: Option<&str> = Some("Optimisations");
14

            
15
/// All subcommands of conjure-oxide
16
#[derive(Clone, Debug, Subcommand)]
17
pub enum Command {
18
    /// Solve a model
19
    Solve(solve::Args),
20
    /// Print the JSON info file schema
21
    PrintJsonSchema,
22
    /// Tests whether the Essence model is solvable with Conjure Oxide, and whether it gets the
23
    /// same solutions as Conjure.
24
    ///
25
    /// Return-code will be 0 if the solutions match, 1 if they don't, and >1 on crash.
26
    TestSolve(test_solve::Args),
27
    /// Generate a completion script for the shell provided
28
    Completion(CompletionArgs),
29
    Pretty(pretty::Args),
30
    // Run the language server
31
    ServerLSP,
32
}
33

            
34
/// Global command line arguments.
35
#[derive(Clone, Debug, Parser)]
36
#[command(
37
    author,
38
    about = "Conjure Oxide: Automated Constraints Modelling Toolkit",
39
    before_help = "Full documentation can be found online at: https://conjure-cp.github.io/conjure-oxide"
40
)]
41
pub struct Cli {
42
    #[command(subcommand)]
43
    pub subcommand: Command,
44

            
45
    #[command(flatten)]
46
    pub global_args: GlobalArgs,
47

            
48
    /// Print version
49
    #[arg(long = "version", short = 'V')]
50
    pub version: bool,
51
}
52

            
53
#[derive(Debug, Clone, Args)]
54
pub struct GlobalArgs {
55
    /// Extra rule sets to enable
56
    #[arg(long, value_name = "EXTRA_RULE_SETS", global = true)]
57
    pub extra_rule_sets: Vec<String>,
58

            
59
    /// Log verbosely
60
    #[arg(long, short = 'v', help = "Log verbosely to stderr", global = true, help_heading = LOGGING_HELP_HEADING)]
61
    pub verbose: bool,
62

            
63
    // --no-x flag disables --x flag : https://jwodder.github.io/kbits/posts/clap-bool-negate/
64
    /// Check for multiple equally applicable rules, exiting if any are found.
65
    ///
66
    /// Only compatible with the default rewriter.
67
    #[arg(
68
        long,
69
        overrides_with = "_no_check_equally_applicable_rules",
70
        default_value_t = false,
71
        global = true,
72
        help_heading= DEBUG_HELP_HEADING
73
    )]
74
    pub check_equally_applicable_rules: bool,
75

            
76
    /// Output file for the human readable rule trace.
77
    #[arg(long, global = true, help_heading=LOGGING_HELP_HEADING)]
78
    pub human_rule_trace: Option<PathBuf>,
79

            
80
    /// Do not check for multiple equally applicable rules [default].
81
    ///
82
    /// Only compatible with the default rewriter.
83
    #[arg(long, global = true, help_heading = DEBUG_HELP_HEADING)]
84
    pub _no_check_equally_applicable_rules: bool,
85

            
86
    /// Which parser to use.
87
    ///
88
    /// Possible values: `tree-sitter`, `via-conjure`.
89
    #[arg(
90
        long,
91
        default_value_t = InputParser::TreeSitter,
92
        value_parser = parse_parser,
93
        global = true,
94
        help_heading = EXPERIMENTAL_HELP_HEADING
95
    )]
96
    pub parser: InputParser,
97

            
98
    /// Which rewriter to use.
99
    ///
100
    /// Possible values: `naive`, `morph`.
101
    #[arg(long, default_value_t = Rewriter::Naive, value_parser = parse_rewriter, global = true, help_heading = EXPERIMENTAL_HELP_HEADING)]
102
    pub rewriter: Rewriter,
103

            
104
    /// Which strategy to use for expanding quantified variables in comprehensions.
105
    ///
106
    /// Possible values: `native`, `via-solver`, `via-solver-ac`.
107
    #[arg(long, default_value_t = QuantifiedExpander::Native, value_parser = parse_quantified_expander, global = true, help_heading = OPTIMISATIONS_HELP_HEADING)]
108
    pub quantified_expander: QuantifiedExpander,
109

            
110
    /// Solver family to use.
111
    ///
112
    /// Possible values: `minion`, `sat`, `sat-log`, `sat-direct`, `sat-order`;
113
    /// with `smt` feature: `smt` and `smt-<ints>-<matrices>[-nodiscrete]`
114
    /// where `<ints>` is `lia` or `bv`, and `<matrices>` is `arrays` or `atomic`.
115
    #[arg(
116
        long,
117
        value_name = "SOLVER",
118
        value_parser = parse_solver_family,
119
        default_value = "minion",
120
        short = 's',
121
        global = true
122
    )]
123
    pub solver: SolverFamily,
124

            
125
    /// Save a solver input file to <filename>.
126
    ///
127
    /// This input file will be in a format compatible by the command-line
128
    /// interface of the selected solver. For example, when the solver is Minion,
129
    /// a valid .minion file will be output.
130
    ///
131
    /// This file is for informational purposes only; the results of running
132
    /// this file cannot be used by Conjure Oxide in any way.
133
    #[arg(long,global=true, value_names=["filename"], next_line_help=true, help_heading=LOGGING_HELP_HEADING)]
134
    pub save_solver_input_file: Option<PathBuf>,
135

            
136
    /// Exit after all comprehensions have been unrolled, printing the number of expressions at that point.
137
    ///
138
    /// This is only compatible with the default rewriter.
139
    ///
140
    /// This flag is useful to compare how comprehension optimisations, such as expand-ac, effect
141
    /// rewriting.
142
    #[arg(long, default_value_t = false, global = true, help_heading = DEBUG_HELP_HEADING)]
143
    pub exit_after_unrolling: bool,
144

            
145
    /// Stop the solver after the given timeout.
146
    ///
147
    /// Currently only SMT supports this feature.
148
    #[arg(long, global = true, help_heading = OPTIMISATIONS_HELP_HEADING)]
149
    pub solver_timeout: Option<humantime::Duration>,
150

            
151
    /// Generate log files
152
    #[arg(long, default_value_t = false, global = true, help_heading = LOGGING_HELP_HEADING)]
153
    pub log: bool,
154

            
155
    /// Output file for conjure-oxide's text logs
156
    #[arg(long, value_name = "LOGFILE", global = true, help_heading = LOGGING_HELP_HEADING)]
157
    pub logfile: Option<PathBuf>,
158

            
159
    /// Output file for conjure-oxide's json logs
160
    #[arg(long, value_name = "JSON LOGFILE", global = true, help_heading = LOGGING_HELP_HEADING)]
161
    pub logfile_json: Option<PathBuf>,
162
}
163

            
164
#[derive(Debug, Clone, Args)]
165
pub struct CompletionArgs {
166
    /// Shell type for which to generate the completion script
167
    #[arg(value_enum)]
168
    pub shell: Shell,
169
}
170

            
171
#[derive(Debug, Clone, Copy, clap::ValueEnum)]
172
pub enum ShellTypes {
173
    Bash,
174
    Zsh,
175
    Fish,
176
    PowerShell,
177
    Elvish,
178
}
179

            
180
32
fn parse_quantified_expander(input: &str) -> Result<QuantifiedExpander, String> {
181
32
    input.parse()
182
32
}
183

            
184
32
fn parse_rewriter(input: &str) -> Result<Rewriter, String> {
185
32
    input.parse()
186
32
}
187

            
188
32
fn parse_solver_family(input: &str) -> Result<SolverFamily, String> {
189
32
    input.parse()
190
32
}
191

            
192
32
fn parse_parser(input: &str) -> Result<InputParser, String> {
193
32
    input.parse()
194
32
}