lazy_regex/
lib.rs

1/*!
2
3With lazy-regex macros, regular expressions
4
5* are checked at compile time, with clear error messages
6* are wrapped in `once_cell` lazy static initializers so that they're compiled only once
7* can hold flags as suffix: `let case_insensitive_regex = regex!("ab*"i);`
8* are defined in a less verbose way
9
10The [`regex!`] macro returns references to normal instances of [`regex::Regex`] or [`regex::bytes::Regex`] so all the usual features are available.
11
12But most often, you won't even use the `regex!` macro but the other macros which are specialized for testing a match, replacing, or capturing groups in some common situations:
13
14* [Test a match](#test-a-match) with [`regex_is_match!`]
15* [Extract a value](#extract-a-value) with [`regex_find!`]
16* [Capture](#capture) with [`regex_captures!`]
17* [Iter on captures](#iter-on-captures) with [`regex_captures_iter!`]
18* [Replace with captured groups](#replace-with-captured-groups) with [`regex_replace!`] and [`regex_replace_all!`]
19* [Switch over patterns](#switch-over-patterns) with [`regex_switch!`]
20
21They support the `B` flag for the `regex::bytes::Regex` variant.
22
23All macros exist with a `bytes_` prefix for building `bytes::Regex`, so you also have [`bytes_regex!`], [`bytes_regex_is_match!`], [`bytes_regex_find!`], [`bytes_regex_captures!`], [`bytes_regex_replace!`], [`bytes_regex_replace_all!`], and [`bytes_regex_switch!`].
24
25Some structs of the regex crate are reexported to ease dependency managment.
26
27# Build Regexes
28
29```rust
30use lazy_regex::regex;
31
32// build a simple regex
33let r = regex!("sa+$");
34assert_eq!(r.is_match("Saa"), false);
35
36// build a regex with flag(s)
37let r = regex!("sa+$"i);
38assert_eq!(r.is_match("Saa"), true);
39
40// you can use a raw literal
41let r = regex!(r#"^"+$"#);
42assert_eq!(r.is_match("\"\""), true);
43
44// or a raw literal with flag(s)
45let r = regex!(r#"^\s*("[a-t]*"\s*)+$"#i);
46assert_eq!(r.is_match(r#" "Aristote" "Platon" "#), true);
47
48// build a regex that operates on &[u8]
49let r = regex!("(byte)?string$"B);
50assert_eq!(r.is_match(b"bytestring"), true);
51
52// there's no problem using the multiline definition syntax
53let r = regex!(r"(?x)
54    (?P<name>\w+)
55    -
56    (?P<version>[0-9.]+)
57");
58assert_eq!(r.find("This is lazy_regex-2.2!").unwrap().as_str(), "lazy_regex-2.2");
59// (look at the regex_captures! macro to easily extract the groups)
60
61```
62```compile_fail
63// this line doesn't compile because the regex is invalid:
64let r = regex!("(unclosed");
65
66```
67Supported regex flags: [`i`, `m`, `s`, `x`, `U`][regex::RegexBuilder], and you may also use `B` to build a bytes regex.
68
69The following regexes are equivalent:
70* `bytes_regex!("^ab+$"i)`
71* `bytes_regex!("(?i)^ab+$")`
72* `regex!("^ab+$"iB)`
73* `regex!("(?i)^ab+$"B)`
74
75They're all case insensitive instances of `regex::bytes::Regex`.
76
77
78# Test a match
79
80```rust
81use lazy_regex::*;
82
83let b = regex_is_match!("[ab]+", "car");
84assert_eq!(b, true);
85let b = bytes_regex_is_match!("[ab]+", b"car");
86assert_eq!(b, true);
87```
88
89doc: [`regex_is_match!`]
90
91
92# Extract a value
93
94```rust
95use lazy_regex::regex_find;
96
97let f_word = regex_find!(r"\bf\w+\b", "The fox jumps.");
98assert_eq!(f_word, Some("fox"));
99let f_word = regex_find!(r"\bf\w+\b"B, b"The forest is silent.");
100assert_eq!(f_word, Some(b"forest" as &[u8]));
101```
102
103doc: [`regex_find!`]
104
105# Capture
106
107```rust
108use lazy_regex::regex_captures;
109
110let (_, letter) = regex_captures!("([a-z])[0-9]+"i, "form A42").unwrap();
111assert_eq!(letter, "A");
112
113let (whole, name, version) = regex_captures!(
114    r"(\w+)-([0-9.]+)", // a literal regex
115    "This is lazy_regex-2.0!", // any expression
116).unwrap();
117assert_eq!(whole, "lazy_regex-2.0");
118assert_eq!(name, "lazy_regex");
119assert_eq!(version, "2.0");
120```
121
122There's no limit to the size of the tuple.
123It's checked at compile time to ensure you have the right number of capturing groups.
124
125You receive `""` for optional groups with no value.
126
127See [`regex_captures!`]
128
129# Iter on captures
130
131```rust
132use lazy_regex::regex_captures_iter;
133
134let hay = "'Citizen Kane' (1941), 'The Wizard of Oz' (1939), 'M' (1931).";
135let mut movies = vec![];
136let iter = regex_captures_iter!(r"'([^']+)'\s+\(([0-9]{4})\)", hay);
137for (_, [title, year]) in iter.map(|c| c.extract()) {
138    movies.push((title, year.parse::<i64>().unwrap()));
139}
140assert_eq!(movies, vec![
141    ("Citizen Kane", 1941),
142    ("The Wizard of Oz", 1939),
143    ("M", 1931),
144]);
145```
146
147See [`regex_captures_iter!`]
148
149# Replace with captured groups
150
151The [`regex_replace!`] and [`regex_replace_all!`] macros bring once compilation and compilation time checks to the `replace` and `replace_all` functions.
152
153## Replace with a closure
154
155```rust
156use lazy_regex::regex_replace_all;
157
158let text = "Foo8 fuu3";
159let text = regex_replace_all!(
160    r"\bf(\w+)(\d)"i,
161    text,
162    |_, name, digit| format!("F<{}>{}", name, digit),
163);
164assert_eq!(text, "F<oo>8 F<uu>3");
165```
166The number of arguments given to the closure is checked at compilation time to match the number of groups in the regular expression.
167
168If it doesn't match you get a clear error message at compilation time.
169
170## Replace with another kind of Replacer
171
172```rust
173use lazy_regex::regex_replace_all;
174let text = "UwU";
175let output = regex_replace_all!("U", text, "O");
176assert_eq!(&output, "OwO");
177```
178
179# Switch over patterns
180
181Execute the expression bound to the first matching regex, with named captured groups declared as varibles:
182
183```rust
184use lazy_regex::regex_switch;
185#[derive(Debug, PartialEq)]
186pub enum ScrollCommand {
187    Top,
188    Bottom,
189    Lines(i32),
190    Pages(i32),
191    JumpTo(String),
192}
193impl std::str::FromStr for ScrollCommand {
194    type Err = &'static str;
195    fn from_str(s: &str) -> Result<Self, Self::Err> {
196        regex_switch!(s,
197            "^scroll-to-top$" => Self::Top,
198            "^scroll-to-bottom$" => Self::Bottom,
199            r"^scroll-lines?\((?<n>[+-]?\d{1,4})\)$" => Self::Lines(n.parse().unwrap()),
200            r"^scroll-pages?\((?<n>[+-]?\d{1,4})\)$" => Self::Pages(n.parse().unwrap()),
201            r"^jump-to\((?<name>\w+)\)$" => Self::JumpTo(name.to_string()),
202        ).ok_or("unknown command")
203    }
204}
205assert_eq!("scroll-lines(42)".parse(), Ok(ScrollCommand::Lines(42)));
206assert_eq!("scroll-lines(XLII)".parse::<ScrollCommand>(), Err("unknown command"));
207```
208
209doc: [`regex_switch!`]
210
211# Shared lazy static
212
213When a regular expression is used in several functions, you sometimes don't want
214to repeat it but have a shared static instance.
215
216The [`regex!`] macro, while being backed by a lazy static regex, returns a reference.
217
218If you want to have a shared lazy static regex, use the [`lazy_regex!`] macro:
219
220```rust
221use lazy_regex::*;
222
223pub static GLOBAL_REX: Lazy<Regex> = lazy_regex!("^ab+$"i);
224```
225
226Like for the other macros, the regex is static, checked at compile time, and lazily built at first use.
227
228doc: [`lazy_regex!`]
229
230*/
231
232pub use {
233    lazy_regex_proc_macros::{
234        lazy_regex,
235        regex,
236        regex_captures,
237        regex_captures_iter,
238        regex_find,
239        regex_if,
240        regex_is_match,
241        regex_replace,
242        regex_replace_all,
243        regex_switch,
244        bytes_lazy_regex,
245        bytes_regex,
246        bytes_regex_captures,
247        bytes_regex_find,
248        bytes_regex_if,
249        bytes_regex_is_match,
250        bytes_regex_replace,
251        bytes_regex_replace_all,
252        bytes_regex_switch,
253    },
254    once_cell::sync::Lazy,
255};
256
257#[cfg(not(feature = "lite"))]
258pub use {
259    regex::{
260        self,
261        Captures, Regex, RegexBuilder,
262        bytes::{
263            Regex as BytesRegex,
264            RegexBuilder as BytesRegexBuilder
265        },
266    },
267};
268
269#[cfg(feature = "lite")]
270pub use {
271    regex_lite::{
272        self as regex,
273        Captures, Regex, RegexBuilder,
274    },
275};
276
277