Skip to main content

litcheck_lit/config/
mod.rs

1mod expr;
2mod features;
3pub(crate) mod substitutions;
4
5pub use self::expr::*;
6pub use self::features::FeatureSet;
7pub use self::substitutions::{
8    InvalidSubstitutionPatternError, ScopedSubstitutionSet, SubstitutionSet,
9};
10
11use std::{fmt, path::PathBuf, sync::Arc};
12
13use clap::Args;
14use litcheck::diagnostics::{DefaultSourceManager, SourceManagerSync};
15use regex::Regex;
16
17pub type Variable = litcheck::variables::Variable<String>;
18
19pub struct Config {
20    pub options: Options,
21    pub source_manager: Arc<dyn SourceManagerSync>,
22}
23
24impl Config {
25    pub fn new(tests: Vec<String>) -> Self {
26        Self {
27            options: Options {
28                tests,
29                workers: None,
30                params: vec![],
31                quiet: false,
32                verbose: false,
33                all: false,
34                no_execute: false,
35                search_paths: vec![],
36                timeout: None,
37                filter: None,
38                filter_out: None,
39                target: None,
40            },
41            source_manager: Arc::from(DefaultSourceManager::default()),
42        }
43    }
44
45    #[inline(always)]
46    pub fn source_manager(&self) -> &dyn SourceManagerSync {
47        &self.source_manager
48    }
49
50    /// Returns true if a test with `name` should be selected for execution
51    pub fn is_selected(&self, name: &str) -> bool {
52        let is_selected = self
53            .options
54            .filter
55            .as_ref()
56            .map(|filter| filter.is_match(name))
57            .unwrap_or(true);
58        let is_excluded = self
59            .options
60            .filter_out
61            .as_ref()
62            .map(|filter_out| filter_out.is_match(name))
63            .unwrap_or(false);
64        is_selected && !is_excluded
65    }
66
67    pub fn host(&self) -> target_lexicon::Triple {
68        target_lexicon::Triple::host()
69    }
70
71    pub fn target(&self) -> target_lexicon::Triple {
72        self.options
73            .target
74            .clone()
75            .unwrap_or_else(target_lexicon::Triple::host)
76    }
77}
78
79impl Default for Config {
80    #[inline]
81    fn default() -> Self {
82        Self::new(vec![])
83    }
84}
85
86impl fmt::Debug for Config {
87    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88        fmt::Debug::fmt(&self.options, f)
89    }
90}
91#[derive(Debug, Args)]
92pub struct Options {
93    /// The test files or directories to search for tests
94    #[arg(value_name = "TESTS", required(true), trailing_var_arg(true))]
95    pub tests: Vec<String>,
96    /// Run `N` tests in parallel
97    ///
98    /// By default, this is automatically chosen to match the number of available CPUs
99    #[arg(long, short = 'j', value_name = "N")]
100    pub workers: Option<usize>,
101    /// Add a user-defined parameter NAME with the given optional VALUE.
102    #[arg(long = "param", short = 'D', value_name = "NAME=[VALUE]")]
103    pub params: Vec<Variable>,
104    /// Suppress any output except for test failures.
105    #[arg(long, short = 'q', default_value_t = false, help_heading = "Output")]
106    pub quiet: bool,
107    /// Show more information on test failures, for example, the entire test output instead
108    /// of just the result.
109    ///
110    /// Each command is printed before it is executed, along with where the command was derived from.
111    #[arg(long, short = 'v', default_value_t = false, help_heading = "Output")]
112    pub verbose: bool,
113    /// Enable `-v`, but for all tests, not just failed tests
114    #[arg(
115        long = "show-all",
116        short = 'a',
117        default_value_t = false,
118        help_heading = "Output"
119    )]
120    pub all: bool,
121    /// Don't execute any tests (assume PASS).
122    ///
123    /// For example, this will still parse test cases for RUN commands, but will not
124    /// actually execute those commands to run the tests.
125    #[arg(
126        long = "no-execute",
127        default_value_t = false,
128        help_heading = "Execution Options"
129    )]
130    pub no_execute: bool,
131    /// Specify an additional PATH to use when searching for executables in tests.
132    #[arg(
133        long = "path",
134        value_name = "PATH",
135        value_parser(canonical_path_parser()),
136        help_heading = "Execution Options"
137    )]
138    pub search_paths: Vec<PathBuf>,
139    /// Spend at most N seconds running each individual test.
140    ///
141    /// 0 means no time limit, and is the default value.
142    #[arg(
143        long,
144        value_name = "N",
145        default_value = "0",
146        help_heading = "Execution Options"
147    )]
148    pub timeout: Option<u64>,
149    /// Run only those tests whose name matches the given regular expression.
150    #[arg(long, env = "LIT_FILTER", help_heading = "Selection Options")]
151    pub filter: Option<Regex>,
152    /// Exclude those tests whose name matches the given regular expression.
153    #[arg(long, env = "LIT_FILTER_OUT", help_heading = "Selection Options")]
154    pub filter_out: Option<Regex>,
155    /// If specified, will set available features based on the provided target triple.
156    ///
157    /// Expects a string like 'x86_64-apple-darwin'.
158    ///
159    /// The default target is the host
160    #[arg(
161        long,
162        value_parser(target_triple_parser()),
163        value_name = "TRIPLE",
164        help_heading = "Selection Options"
165    )]
166    pub target: Option<target_lexicon::Triple>,
167}
168
169fn target_triple_parser() -> clap::builder::ValueParser {
170    use clap::{builder::ValueParser, error::ErrorKind, Error};
171    ValueParser::from(|s: &str| -> Result<target_lexicon::Triple, clap::Error> {
172        s.parse::<target_lexicon::Triple>().map_err(|err| {
173            Error::raw(
174                ErrorKind::ValueValidation,
175                format!("invalid target triple '{s}': {err}"),
176            )
177        })
178    })
179}
180
181fn canonical_path_parser() -> clap::builder::ValueParser {
182    use clap::{builder::ValueParser, error::ErrorKind, Error};
183    ValueParser::from(|s: &str| -> Result<PathBuf, clap::Error> {
184        let path = std::path::Path::new(s);
185        path.canonicalize().map_err(|err| {
186            Error::raw(
187                ErrorKind::ValueValidation,
188                format!("invalid path '{s}': {err}"),
189            )
190        })
191    })
192}