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 CONFIGURATION_HELP_HEADING: Option<&str> = Some("Configuration");
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 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::ViaConjure,
92
        value_parser = parse_parser,
93
        global = true,
94
        help_heading = CONFIGURATION_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 = CONFIGURATION_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(
108
        long,
109
        default_value_t = QuantifiedExpander::Native,
110
        value_parser = parse_comprehension_expander,
111
        global = true,
112
        help_heading = CONFIGURATION_HELP_HEADING
113
    )]
114
    pub comprehension_expander: QuantifiedExpander,
115

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

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

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

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

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

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

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

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

            
178
48
fn parse_comprehension_expander(input: &str) -> Result<QuantifiedExpander, String> {
179
48
    input.parse()
180
48
}
181

            
182
48
fn parse_rewriter(input: &str) -> Result<Rewriter, String> {
183
48
    input.parse()
184
48
}
185

            
186
48
fn parse_solver_family(input: &str) -> Result<SolverFamily, String> {
187
48
    input.parse()
188
48
}
189

            
190
48
fn parse_parser(input: &str) -> Result<InputParser, String> {
191
48
    input.parse()
192
48
}