• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pomsky-lang / pomsky / 12305481124

12 Dec 2024 09:57PM UTC coverage: 77.983% (-2.3%) from 80.275%
12305481124

push

github

Aloso
feat: support Rust regex engine, change help styling, many small improvements

191 of 509 new or added lines in 14 files covered. (37.52%)

4 existing lines in 3 files now uncovered.

4778 of 6127 relevant lines covered (77.98%)

350716.63 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

68.14
/pomsky-bin/src/args/parse.rs
1
use std::io::{stdin, stdout, IsTerminal};
2
use std::path::PathBuf;
3

4
use pomsky::{features::PomskyFeatures, options::RegexFlavor};
5

6
use crate::args::RegexEngine;
7
use crate::format::Logger;
8

9
use super::{
10
    CompileOptions, DiagnosticSet, GlobalOptions, Input, ParseArgsError, Subcommand, TestOptions,
11
};
12

13
#[derive(PartialEq)]
14
pub(super) enum Parsed {
15
    Options(Subcommand, GlobalOptions),
16
    Help(Help),
17
    Version,
18
    List(ListKind),
19
}
20

21
pub fn parse_args_inner(
32✔
22
    logger: &Logger,
32✔
23
    mut parser: lexopt::Parser,
32✔
24
) -> Result<Parsed, ParseArgsError> {
32✔
25
    RootParser::new().parse(logger, &mut parser)
32✔
26
}
32✔
27

28
#[derive(PartialEq)]
29
pub(super) enum Help {
30
    Short,
31
    Long,
32
    TestShort,
33
    TestLong,
34
}
35

36
#[derive(PartialEq)]
37
pub(super) enum ListKind {
38
    Shorthands,
39
}
40

41
struct RootParser {
42
    debug: bool,
43
    flavor: Option<RegexFlavor>,
44
    allowed_features: Option<PomskyFeatures>,
45
    warnings: DiagnosticSet,
46
    json: Option<bool>,
47
}
48

49
// we have to do this to deduplicate code without creating borrowcheck issues, because `parser` is a lending iterator
50
macro_rules! parse_root_arg {
51
    ($logger:expr, $arg:expr, $parser:expr, $root:expr) => {
52
        match $arg {
53
            Short('d') | Long("debug") => $root.debug.set_arg(true, "--debug")?,
54
            Short('f') | Long("flavor") => {
55
                $root
56
                    .flavor
57
                    .set_arg(super::flavors::parse_flavor($parser.value()?)?, "--flavor")?;
58
            }
59
            Short('W') | Long("warnings") => $root.warnings.parse($parser.value()?)?,
60
            Long("allowed-features") => $root.allowed_features.set_arg(
61
                super::features::parse_features($logger, $parser.value()?)?,
62
                "--allowed-features",
63
            )?,
64
            Long("json") => $root.json.set_arg(true, "--json")?,
65
            Short('h') => return Ok(Parsed::Help(Help::Short)),
66
            Long("help") => return Ok(Parsed::Help(Help::Long)),
67
            Short('V') | Long("version") => return Ok(Parsed::Version),
68
            _ => Err($arg.unexpected())?,
69
        }
70
    };
71
}
72

73
macro_rules! parse_compile_options {
74
    ($logger:expr, $arg:expr, $parser:expr, $self:expr) => {
75
        match $arg {
76
            Short('p') | Long("path") => $self.path.set_arg($parser.value()?.parse()?, "--path")?,
77
            Short('t') | Long("test") => {
78
                $self.test.set_arg(RegexEngine::parse($parser.value()?)?, "--test")?;
79
            }
80
            Short('n') | Long("no-new-line") => $self.no_new_line.set_arg(true, "--no-new-line")?,
81
            Value(val) if $self.input_value.is_none() => {
82
                $self.input_value = Some(val.into_string().map_err(lexopt::Error::from)?);
83
            }
84
            _ => parse_root_arg!($logger, $arg, $parser, $self.root),
85
        }
86
    };
87
}
88

89
impl RootParser {
90
    fn new() -> Self {
32✔
91
        Self {
32✔
92
            debug: false,
32✔
93
            flavor: None,
32✔
94
            allowed_features: None,
32✔
95
            warnings: DiagnosticSet::default(),
32✔
96
            json: None,
32✔
97
        }
32✔
98
    }
32✔
99

100
    fn parse(self, logger: &Logger, parser: &mut lexopt::Parser) -> Result<Parsed, ParseArgsError> {
32✔
101
        use lexopt::prelude::*;
102

103
        let Some(arg) = parser.next()? else {
32✔
104
            if stdin().is_terminal() && stdout().is_terminal() {
1✔
105
                return Ok(Parsed::Help(Help::Short));
×
106
            } else {
107
                return CompileParser::new(self).finish();
1✔
108
            }
109
        };
110
        match arg {
18✔
111
            Long("list") => {
×
112
                let list_arg = parser.value()?.string()?;
×
113
                if &list_arg != "shorthands" {
×
114
                    return Err(ParseArgsError::UnknownList(list_arg));
×
115
                };
×
116
                Ok(Parsed::List(ListKind::Shorthands))
×
117
            }
118
            Value(val) if val == "test" => TestParser::new(self).parse(logger, parser),
18✔
119
            arg => {
31✔
120
                let mut compile_parser = CompileParser::new(self);
31✔
121
                parse_compile_options!(logger, arg, parser, compile_parser);
18✔
122
                compile_parser.parse(logger, parser)
28✔
123
            }
124
        }
125
    }
32✔
126

127
    fn finish(self, subcommand: Subcommand) -> Result<Parsed, ParseArgsError> {
22✔
128
        Ok(Parsed::Options(
22✔
129
            subcommand,
22✔
130
            GlobalOptions {
22✔
131
                flavor: self.flavor,
22✔
132
                debug: self.debug,
22✔
133
                json: self.json.unwrap_or_default(),
22✔
134
                allowed_features: self.allowed_features.unwrap_or_default(),
22✔
135
                warnings: self.warnings,
22✔
136
            },
22✔
137
        ))
22✔
138
    }
22✔
139
}
140

141
struct CompileParser {
142
    root: RootParser,
143
    input_value: Option<String>,
144
    path: Option<PathBuf>,
145
    no_new_line: bool,
146
    test: Option<RegexEngine>,
147
}
148

149
impl CompileParser {
150
    fn new(root: RootParser) -> Self {
32✔
151
        Self { root, input_value: None, path: None, no_new_line: false, test: None }
32✔
152
    }
32✔
153

154
    fn parse(
28✔
155
        mut self,
28✔
156
        logger: &Logger,
28✔
157
        parser: &mut lexopt::Parser,
28✔
158
    ) -> Result<Parsed, ParseArgsError> {
28✔
159
        use lexopt::prelude::*;
160

161
        while let Some(arg) = parser.next()? {
50✔
162
            parse_compile_options!(logger, arg, parser, self);
5✔
163
        }
164
        self.finish()
23✔
165
    }
28✔
166

167
    fn finish(self) -> Result<Parsed, ParseArgsError> {
24✔
168
        let input = match (self.input_value, self.path) {
24✔
169
            (Some(input), None) => Input::Value(input),
15✔
170
            (None, Some(path)) => Input::File(path),
6✔
171
            (Some(_), Some(_)) => return Err(ParseArgsError::InputAndPath),
2✔
172
            (None, None) => Input::read_stdin()?,
1✔
173
        };
174

175
        self.root.finish(Subcommand::Compile(CompileOptions {
22✔
176
            input,
22✔
177
            no_new_line: self.no_new_line,
22✔
178
            test: self.test,
22✔
179
            in_test_suite: false,
22✔
180
        }))
22✔
181
    }
24✔
182
}
183

184
struct TestParser {
185
    root: RootParser,
186
    path: Option<PathBuf>,
187
    engine: Option<RegexEngine>,
188
    pass_with_no_tests: Option<bool>,
189
}
190

191
impl TestParser {
192
    fn new(root: RootParser) -> Self {
×
193
        Self { root, path: None, engine: None, pass_with_no_tests: None }
×
194
    }
×
195

NEW
196
    fn parse(
×
NEW
197
        mut self,
×
NEW
198
        logger: &Logger,
×
NEW
199
        parser: &mut lexopt::Parser,
×
NEW
200
    ) -> Result<Parsed, ParseArgsError> {
×
201
        use lexopt::prelude::*;
202

203
        while let Some(arg) = parser.next()? {
×
204
            match arg {
×
205
                Short('p') | Long("path") => {
×
206
                    self.path.set_arg(parser.value()?.parse()?, "--path")?
×
207
                }
208
                Short('e') | Long("engine") => {
×
209
                    self.engine.set_arg(RegexEngine::parse(parser.value()?)?, "--engine")?
×
210
                }
211
                Long("pass-with-no-tests") => {
×
212
                    self.pass_with_no_tests.set_arg(true, "--pass-with-no-tests")?
×
213
                }
214
                Short('h') => return Ok(Parsed::Help(Help::TestShort)),
×
215
                Long("help") => return Ok(Parsed::Help(Help::TestLong)),
×
NEW
216
                _ => parse_root_arg!(logger, arg, parser, self.root),
×
217
            }
218
        }
219
        self.finish()
×
220
    }
×
221

222
    fn finish(self) -> Result<Parsed, ParseArgsError> {
×
NEW
223
        let path = self.path.ok_or(ParseArgsError::NoPath)?;
×
224

225
        self.root.finish(Subcommand::Test(TestOptions {
×
226
            path,
×
227
            engine: self.engine,
×
228
            pass_with_no_tests: self.pass_with_no_tests.unwrap_or_default(),
×
229
        }))
×
230
    }
×
231
}
232

233
trait SetArg {
234
    type Set;
235

236
    fn set_arg(&mut self, value: Self::Set, name: &'static str) -> Result<(), ParseArgsError>;
237
}
238

239
impl SetArg for bool {
240
    type Set = bool;
241

242
    fn set_arg(&mut self, value: bool, name: &'static str) -> Result<(), ParseArgsError> {
6✔
243
        if *self == value {
6✔
244
            return Err(ParseArgsError::UnexpectedTwice(name));
1✔
245
        }
5✔
246
        *self = value;
5✔
247
        Ok(())
5✔
248
    }
6✔
249
}
250

251
impl<T> SetArg for Option<T> {
252
    type Set = T;
253

254
    fn set_arg(&mut self, value: T, name: &'static str) -> Result<(), ParseArgsError> {
24✔
255
        if self.is_some() {
24✔
256
            return Err(ParseArgsError::UnexpectedTwice(name));
1✔
257
        }
23✔
258
        *self = Some(value);
23✔
259
        Ok(())
23✔
260
    }
24✔
261
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc