1#![cfg_attr(test, feature(assert_matches))]
2pub mod ast;
3pub mod check;
4mod context;
5mod cursor;
6mod env;
7pub mod errors;
8pub mod expr;
9mod input;
10pub mod parse;
11pub mod pattern;
12pub mod rules;
13mod test;
14
15pub use self::errors::{CheckFailedError, RelatedCheckError, RelatedError, TestFailed};
16#[cfg(test)]
17pub use self::test::TestContext;
18pub use self::test::{Test, TestResult};
19
20use clap::{builder::ValueParser, ArgAction, Args, ColorChoice, ValueEnum};
21use std::sync::Arc;
22
23pub(crate) mod common {
24 pub use std::{
25 borrow::Cow,
26 fmt,
27 ops::{ControlFlow, RangeBounds},
28 sync::Arc,
29 };
30
31 pub use either::Either::{self, Left, Right};
32 pub use litcheck::{
33 diagnostics::{
34 ArcSource, Diag, DiagResult, Diagnostic, Label, NamedSourceFile, Report, Source,
35 SourceFile, SourceSpan, Span, Spanned,
36 },
37 range::{self, Range},
38 text::{self, Newline},
39 StringInterner, Symbol,
40 };
41 pub use regex_automata::{meta::Regex, util::look::LookMatcher};
42 pub use smallvec::{smallvec, SmallVec};
43
44 pub use crate::ast::{Check, Constraint};
45 pub use crate::context::{Context, ContextExt, ContextGuard, MatchContext};
46 pub use crate::cursor::{Cursor, CursorGuard, CursorPosition};
47 pub use crate::env::{Env, LexicalScope, LexicalScopeExtend, LexicalScopeMut, ScopeGuard};
48 pub use crate::errors::{
49 CheckFailedError, RelatedCheckError, RelatedError, RelatedLabel, TestFailed,
50 };
51 pub use crate::expr::{BinaryOp, Expr, Number, NumberFormat, Value, VariableName};
52 pub use crate::input::Input;
53 pub use crate::pattern::{
54 AnyMatcher, CaptureInfo, MatchInfo, MatchResult, MatchType, Matcher, MatcherMut, Matches,
55 Pattern, PatternIdentifier, PatternSearcher, Searcher,
56 };
57 pub use crate::rules::{DynRule, Rule};
58 #[cfg(test)]
59 pub(crate) use crate::test::TestContext;
60 pub use crate::test::TestResult;
61 pub use crate::Config;
62}
63
64pub const DEFAULT_CHECK_PREFIXES: &[&str] = &["CHECK"];
65pub const DEFAULT_COMMENT_PREFIXES: &[&str] = &["COM", "RUN"];
66
67#[derive(Debug, Args)]
70pub struct Config {
71 #[arg(
73 default_value_t = false,
74 action(clap::ArgAction::SetTrue),
75 help_heading = "Input"
76 )]
77 pub allow_empty: bool,
78 #[arg(
82 long = "check-prefix",
83 value_name = "PREFIX",
84 default_value = "CHECK",
85 action(clap::ArgAction::Append),
86 value_parser(re_value_parser("^[A-Za-z][A-Za-z0-9_]*")),
87 help_heading = "Syntax"
88 )]
89 pub check_prefixes: Vec<Arc<str>>,
90 #[arg(
95 long = "comment-prefix",
96 value_name = "PREFIX",
97 default_value = "COM,RUN",
98 action(clap::ArgAction::Append),
99 value_parser(re_value_parser("^[A-Za-z][A-Za-z0-9_]*")),
100 help_heading = "Syntax"
101 )]
102 pub comment_prefixes: Vec<Arc<str>>,
103 #[arg(long, default_value_t = false, help_heading = "Syntax")]
106 pub allow_unused_prefixes: bool,
107 #[arg(
116 long = "strict-whitespace",
117 default_value_t = false,
118 help_heading = "Matching"
119 )]
120 pub strict_whitespace: bool,
121 #[arg(long, default_value_t = false, help_heading = "Matching")]
131 pub match_full_lines: bool,
132 #[arg(long, default_value_t = false, help_heading = "Matching")]
134 pub ignore_case: bool,
135 #[arg(long, value_name = "CHECK", help_heading = "Matching")]
144 pub implicit_check_not: Vec<String>,
145 #[arg(long, value_enum, value_name = "TYPE", default_value_t = Dump::Fail, help_heading = "Output")]
147 pub dump_input: Dump,
148 #[arg(
154 long,
155 value_enum,
156 value_name = "KIND",
157 default_value_t = DumpFilter::Error,
158 default_value_ifs([("dump-input", "fail", Some("error")), ("dump-input", "always", Some("all"))]),
159 help_heading = "Output"
160 )]
161 pub dump_input_filter: DumpFilter,
162 #[arg(long, default_value_t = false, help_heading = "Variables")]
168 pub enable_var_scope: bool,
169 #[arg(
173 long = "define",
174 short = 'D',
175 value_name = "NAME=VALUE",
176 help_heading = "Variables"
177 )]
178 pub variables: Vec<expr::CliVariable>,
179 #[arg(long, short = 'v', action = ArgAction::Count, help_heading = "Output")]
187 pub verbose: u8,
188 #[arg(
190 global(true),
191 value_enum,
192 long,
193 default_value_t = ColorChoice::Auto,
194 default_missing_value = "auto",
195 help_heading = "Output"
196 )]
197 pub color: ColorChoice,
198}
199impl Default for Config {
200 fn default() -> Self {
201 Self {
202 allow_empty: false,
203 check_prefixes: vec![Arc::from("CHECK".to_string().into_boxed_str())],
204 comment_prefixes: vec![
205 Arc::from("COM".to_string().into_boxed_str()),
206 Arc::from("RUN".to_string().into_boxed_str()),
207 ],
208 allow_unused_prefixes: false,
209 strict_whitespace: false,
210 match_full_lines: false,
211 ignore_case: false,
212 implicit_check_not: vec![],
213 dump_input: Default::default(),
214 dump_input_filter: Default::default(),
215 enable_var_scope: false,
216 variables: vec![],
217 verbose: 0,
218 color: Default::default(),
219 }
220 }
221}
222
223#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, ValueEnum)]
224pub enum Dump {
225 Help,
227 Always,
229 #[default]
231 Fail,
232 Never,
234}
235
236#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, ValueEnum)]
237pub enum DumpFilter {
238 All,
240 AnnotationFull,
242 Annotation,
244 #[default]
246 Error,
247}
248
249fn re_value_parser(r: &'static str) -> ValueParser {
250 use clap::{error::ErrorKind, Error};
251
252 ValueParser::from(move |s: &str| -> Result<Arc<str>, clap::Error> {
253 let re = regex::Regex::new(r).unwrap();
254 if re.is_match(s) {
255 Ok(Arc::from(s.to_owned().into_boxed_str()))
256 } else {
257 Err(Error::raw(
258 ErrorKind::ValueValidation,
259 "'{s}' does not match expected pattern `{r}`",
260 ))
261 }
262 })
263}