js_regex/
lib.rs

1// Copyright (C) 2020 Quentin M. Kniep <hello@quentinkniep.com>
2// Distributed under terms of the MIT license.
3
4#[macro_use]
5extern crate lazy_static;
6
7mod reader;
8mod unicode;
9mod validator;
10
11pub use validator::{EcmaRegexValidator, EcmaVersion};
12
13#[cfg(test)]
14mod tests {
15    use super::*;
16
17    #[test]
18    fn valid_flags() {
19        let validator = EcmaRegexValidator::new(EcmaVersion::ES2018);
20        assert_eq!(validator.validate_flags("gimuys"), Ok(()));
21        assert_eq!(validator.validate_flags("gimuy"), Ok(()));
22        assert_eq!(validator.validate_flags("gim"), Ok(()));
23        assert_eq!(validator.validate_flags("g"), Ok(()));
24        assert_eq!(validator.validate_flags("i"), Ok(()));
25        assert_eq!(validator.validate_flags("m"), Ok(()));
26        assert_eq!(validator.validate_flags("s"), Ok(()));
27        assert_eq!(validator.validate_flags("u"), Ok(()));
28        assert_eq!(validator.validate_flags("y"), Ok(()));
29
30        assert_eq!(validator.validate_flags("gy"), Ok(()));
31        assert_eq!(validator.validate_flags("iy"), Ok(()));
32        assert_eq!(validator.validate_flags("my"), Ok(()));
33        assert_eq!(validator.validate_flags("uy"), Ok(()));
34    }
35
36    #[test]
37    fn duplicate_flags() {
38        let validator = EcmaRegexValidator::new(EcmaVersion::ES2018);
39        assert_eq!(validator.validate_flags("gimgu"), Err("Duplicated flag g".to_string()));
40        assert_eq!(validator.validate_flags("migg"), Err("Duplicated flag g".to_string()));
41        assert_eq!(validator.validate_flags("igi"), Err("Duplicated flag i".to_string()));
42
43        assert_eq!(validator.validate_flags("ii"), Err("Duplicated flag i".to_string()));
44        assert_eq!(validator.validate_flags("mm"), Err("Duplicated flag m".to_string()));
45        assert_eq!(validator.validate_flags("ss"), Err("Duplicated flag s".to_string()));
46        assert_eq!(validator.validate_flags("uu"), Err("Duplicated flag u".to_string()));
47        assert_eq!(validator.validate_flags("yy"), Err("Duplicated flag y".to_string()));
48    }
49
50    #[test]
51    fn invalid_flags() {
52        let validator = EcmaRegexValidator::new(EcmaVersion::ES2018);
53        assert_eq!(validator.validate_flags("gimuf"), Err("Invalid flag f".to_string()));
54        assert_eq!(validator.validate_flags("gI"), Err("Invalid flag I".to_string()));
55        assert_eq!(validator.validate_flags("a"), Err("Invalid flag a".to_string()));
56        assert_eq!(validator.validate_flags("1"), Err("Invalid flag 1".to_string()));
57    }
58
59    #[test]
60    fn validate_pattern_test() {
61        let mut validator = EcmaRegexValidator::new(EcmaVersion::ES2018);
62        assert_eq!(validator.validate_pattern("", false), Ok(()));
63        assert_eq!(validator.validate_pattern("[abc]de|fg", false), Ok(()));
64        assert_eq!(validator.validate_pattern("[abc]de|fg", true), Ok(()));
65        assert_eq!(validator.validate_pattern("^.$", false), Ok(()));
66        assert_eq!(validator.validate_pattern("^.$", true), Ok(()));
67        assert_eq!(validator.validate_pattern("foo\\[bar", false), Ok(()));
68        assert_eq!(validator.validate_pattern("foo\\[bar", true), Ok(()));
69        assert_eq!(validator.validate_pattern("\\w+\\s", false), Ok(()));
70        assert_eq!(validator.validate_pattern("(\\w+), (\\w+)", false), Ok(()));
71        assert_eq!(validator.validate_pattern("\\/\\/.*|\\/\\*[^]*\\*\\/", false), Ok(()));
72        assert_eq!(validator.validate_pattern("(\\d{1,2})-(\\d{1,2})-(\\d{4})", false), Ok(()));
73        assert_eq!(validator.validate_pattern("(?:\\d{3}|\\(\\d{3}\\))([-\\/\\.])\\d{3}\\1\\d{4}", false), Ok(()));
74        assert_eq!(validator.validate_pattern("https?:\\/\\/(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_\\+.~#?&//=]*)", false), Ok(()));
75
76        assert_eq!(validator.validate_pattern("\\p{Script=Greek}", true), Ok(()));
77        assert_eq!(validator.validate_pattern("\\p{Alphabetic}", true), Ok(()));
78
79        assert_ne!(validator.validate_pattern("\\", false), Ok(()));
80        assert_ne!(validator.validate_pattern("a**", false), Ok(()));
81        assert_ne!(validator.validate_pattern("++a", false), Ok(()));
82        assert_ne!(validator.validate_pattern("?a", false), Ok(()));
83        assert_ne!(validator.validate_pattern("a***", false), Ok(()));
84        assert_ne!(validator.validate_pattern("a++", false), Ok(()));
85        assert_ne!(validator.validate_pattern("a+++", false), Ok(()));
86        assert_ne!(validator.validate_pattern("a???", false), Ok(()));
87        assert_ne!(validator.validate_pattern("a????", false), Ok(()));
88        assert_ne!(validator.validate_pattern("*a", false), Ok(()));
89        assert_ne!(validator.validate_pattern("**a", false), Ok(()));
90        assert_ne!(validator.validate_pattern("+a", false), Ok(()));
91        assert_ne!(validator.validate_pattern("[{-z]", false), Ok(()));
92        assert_ne!(validator.validate_pattern("[a--z]", false), Ok(()));
93
94        assert_ne!(validator.validate_pattern("0{2,1}", false), Ok(()));
95        assert_ne!(validator.validate_pattern("x{1}{1,}", false), Ok(()));
96        assert_ne!(validator.validate_pattern("x{1,2}{1}", false), Ok(()));
97        assert_ne!(validator.validate_pattern("x{1,}{1}", false), Ok(()));
98        assert_ne!(validator.validate_pattern("x{0,1}{1,}", false), Ok(()));
99
100        assert_ne!(validator.validate_pattern("\\1(\\P{P\0[}()/", true), Ok(()));
101    }
102
103    #[test]
104    fn character_range_order() {
105        let mut validator = EcmaRegexValidator::new(EcmaVersion::ES2018);
106        assert_ne!(validator.validate_pattern("^[z-a]$", false), Ok(()));
107        assert_ne!(validator.validate_pattern("[b-ac-e]", false), Ok(()));
108        assert_ne!(validator.validate_pattern("[c-eb-a]", false), Ok(()));
109        assert_ne!(validator.validate_pattern("[a-dc-b]", false), Ok(()));
110
111        assert_ne!(validator.validate_pattern("[\\10b-G]", false), Ok(()));
112        assert_ne!(validator.validate_pattern("[\\ad-G]", false), Ok(()));
113        assert_ne!(validator.validate_pattern("[\\bd-G]", false), Ok(()));
114        assert_ne!(validator.validate_pattern("[\\Bd-G]", false), Ok(()));
115        assert_ne!(validator.validate_pattern("[\\db-G]", false), Ok(()));
116        assert_ne!(validator.validate_pattern("[\\Db-G]", false), Ok(()));
117        assert_ne!(validator.validate_pattern("[\\sb-G]", false), Ok(()));
118        assert_ne!(validator.validate_pattern("[\\Sb-G]", false), Ok(()));
119        assert_ne!(validator.validate_pattern("[\\wb-G]", false), Ok(()));
120        assert_ne!(validator.validate_pattern("[\\Wb-G]", false), Ok(()));
121        assert_ne!(validator.validate_pattern("[\\0b-G]", false), Ok(()));
122        assert_ne!(validator.validate_pattern("[\\td-G]", false), Ok(()));
123        assert_ne!(validator.validate_pattern("[\\nd-G]", false), Ok(()));
124        assert_ne!(validator.validate_pattern("[\\vd-G]", false), Ok(()));
125        assert_ne!(validator.validate_pattern("[\\fd-G]", false), Ok(()));
126        assert_ne!(validator.validate_pattern("[\\rd-G]", false), Ok(()));
127        assert_ne!(validator.validate_pattern("[\\c0001d-G]", false), Ok(()));
128        assert_ne!(validator.validate_pattern("[\\x0061d-G]", false), Ok(()));
129        assert_ne!(validator.validate_pattern("[\\u0061d-G]", false), Ok(()));
130
131        assert_ne!(validator.validate_pattern("[b-G\\10]", false), Ok(()));
132        assert_ne!(validator.validate_pattern("[d-G\\a]", false), Ok(()));
133        assert_ne!(validator.validate_pattern("[d-G\\b]", false), Ok(()));
134        assert_ne!(validator.validate_pattern("[d-G\\B]", false), Ok(()));
135        assert_ne!(validator.validate_pattern("[b-G\\d]", false), Ok(()));
136        assert_ne!(validator.validate_pattern("[b-G\\D]", false), Ok(()));
137        assert_ne!(validator.validate_pattern("[b-G\\s]", false), Ok(()));
138        assert_ne!(validator.validate_pattern("[b-G\\S]", false), Ok(()));
139        assert_ne!(validator.validate_pattern("[b-G\\w]", false), Ok(()));
140        assert_ne!(validator.validate_pattern("[b-G\\W]", false), Ok(()));
141        assert_ne!(validator.validate_pattern("[b-G\\0]", false), Ok(()));
142        assert_ne!(validator.validate_pattern("[d-G\\t]", false), Ok(()));
143        assert_ne!(validator.validate_pattern("[d-G\\n]", false), Ok(()));
144        assert_ne!(validator.validate_pattern("[d-G\\v]", false), Ok(()));
145        assert_ne!(validator.validate_pattern("[d-G\\f]", false), Ok(()));
146        assert_ne!(validator.validate_pattern("[d-G\\r]", false), Ok(()));
147        assert_ne!(validator.validate_pattern("[d-G\\c0001]", false), Ok(()));
148        assert_ne!(validator.validate_pattern("[d-G\\x0061]", false), Ok(()));
149        assert_ne!(validator.validate_pattern("[d-G\\u0061]", false), Ok(()));
150    }
151
152    #[test]
153    fn unicode_quantifier_without_atom() {
154        let mut validator = EcmaRegexValidator::new(EcmaVersion::ES2018);
155        assert_ne!(validator.validate_pattern("*", true), Ok(()));
156        assert_ne!(validator.validate_pattern("+", true), Ok(()));
157        assert_ne!(validator.validate_pattern("?", true), Ok(()));
158        assert_ne!(validator.validate_pattern("{1}", true), Ok(()));
159        assert_ne!(validator.validate_pattern("{1,}", true), Ok(()));
160        assert_ne!(validator.validate_pattern("{1,2}", true), Ok(()));
161
162        assert_ne!(validator.validate_pattern("*?", true), Ok(()));
163        assert_ne!(validator.validate_pattern("+?", true), Ok(()));
164        assert_ne!(validator.validate_pattern("??", true), Ok(()));
165        assert_ne!(validator.validate_pattern("{1}?", true), Ok(()));
166        assert_ne!(validator.validate_pattern("{1,}?", true), Ok(()));
167        assert_ne!(validator.validate_pattern("{1,2}?", true), Ok(()));
168    }
169
170    #[test]
171    fn unicode_incomplete_quantifier() {
172        let mut validator = EcmaRegexValidator::new(EcmaVersion::ES2018);
173        assert_ne!(validator.validate_pattern("a{", true), Ok(()));
174        assert_ne!(validator.validate_pattern("a{1", true), Ok(()));
175        assert_ne!(validator.validate_pattern("a{1,", true), Ok(()));
176        assert_ne!(validator.validate_pattern("a{1,2", true), Ok(()));
177
178        assert_ne!(validator.validate_pattern("{", true), Ok(()));
179        assert_ne!(validator.validate_pattern("{1", true), Ok(()));
180        assert_ne!(validator.validate_pattern("{1,", true), Ok(()));
181        assert_ne!(validator.validate_pattern("{1,2", true), Ok(()));
182    }
183
184    #[test]
185    fn unicode_single_bracket() {
186        let mut validator = EcmaRegexValidator::new(EcmaVersion::ES2018);
187        assert_ne!(validator.validate_pattern("(", true), Ok(()));
188        assert_ne!(validator.validate_pattern(")", true), Ok(()));
189        assert_ne!(validator.validate_pattern("[", true), Ok(()));
190        assert_ne!(validator.validate_pattern("]", true), Ok(()));
191        assert_ne!(validator.validate_pattern("{", true), Ok(()));
192        assert_ne!(validator.validate_pattern("}", true), Ok(()));
193    }
194
195    #[test]
196    fn unicode_escapes() {
197        let mut validator = EcmaRegexValidator::new(EcmaVersion::ES2018);
198        assert_eq!(validator.validate_pattern("\\u{10ffff}", true), Ok(()));
199        assert_ne!(validator.validate_pattern("\\u{110000}", true), Ok(()));
200        assert_eq!(validator.validate_pattern("\\u{110000}", false), Ok(()));
201        assert_eq!(validator.validate_pattern("foo\\ud803\\ude6dbar", true), Ok(()));
202        assert_eq!(validator.validate_pattern("(\u{12345}|\u{23456}).\\1", true), Ok(()));
203        assert_eq!(validator.validate_pattern("\u{12345}{3}", true), Ok(()));
204
205        // unicode escapes in character classes
206        assert_eq!(validator.validate_pattern("[\\u0062-\\u0066]oo", false), Ok(()));
207        assert_eq!(validator.validate_pattern("[\\u0062-\\u0066]oo", true), Ok(()));
208        assert_eq!(validator.validate_pattern("[\\u{0062}-\\u{0066}]oo", true), Ok(()));
209        assert_eq!(validator.validate_pattern("[\\u{62}-\\u{00000066}]oo", true), Ok(()));
210
211        // invalid escapes
212        assert_eq!(validator.validate_pattern("first\\u\\x\\z\\8\\9second", false), Ok(()));
213        assert_eq!(validator.validate_pattern("[\\u\\x\\z\\8\\9]", false), Ok(()));
214        assert_ne!(validator.validate_pattern("/\\u/u", true), Ok(()));
215        assert_ne!(validator.validate_pattern("/\\u12/u", true), Ok(()));
216        assert_ne!(validator.validate_pattern("/\\ufoo/u", true), Ok(()));
217        assert_ne!(validator.validate_pattern("/\\x/u", true), Ok(()));
218        assert_ne!(validator.validate_pattern("/\\xfoo/u", true), Ok(()));
219        assert_ne!(validator.validate_pattern("/\\z/u", true), Ok(()));
220        assert_ne!(validator.validate_pattern("/\\8/u", true), Ok(()));
221        assert_ne!(validator.validate_pattern("/\\9/u", true), Ok(()));
222    }
223}