Skip to content

Commit 783c76b

Browse files
authored
fix: make URLPatternComponentResult::groups values optional (#35)
1 parent cdf29f5 commit 783c76b

8 files changed

+40
-27
lines changed

Cargo.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ repository = "https://github.com/denoland/rust-urlpattern"
88
license = "MIT"
99

1010
[dependencies]
11-
url = "2.2.2"
12-
regex = "1.4.3"
11+
url = "2.4.1"
12+
regex = "1.10.5"
1313
serde = { version = "1.0.127", features = ["derive"] }
1414
unic-ucd-ident = { version = "0.9.0", features = ["id"] }
1515

src/component.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,13 @@ impl<R: RegExp> Component<R> {
6767
pub(crate) fn create_match_result(
6868
&self,
6969
input: String,
70-
exec_result: Vec<&str>,
70+
exec_result: Vec<Option<&str>>,
7171
) -> crate::UrlPatternComponentResult {
7272
let groups = self
7373
.group_name_list
7474
.clone()
7575
.into_iter()
76-
.zip(exec_result.into_iter().map(str::to_owned))
76+
.zip(exec_result.into_iter().map(|s| s.map(str::to_owned)))
7777
.collect();
7878
crate::UrlPatternComponentResult { input, groups }
7979
}

src/lib.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ fn is_absolute_pathname(
202202
/// // Match the pattern against a URL.
203203
/// let url = "https://example.com/users/123".parse().unwrap();
204204
/// let result = pattern.exec(UrlPatternMatchInput::Url(url)).unwrap().unwrap();
205-
/// assert_eq!(result.pathname.groups.get("id").unwrap(), "123");
205+
/// assert_eq!(result.pathname.groups.get("id").unwrap().as_ref().unwrap(), "123");
206206
///# }
207207
/// ```
208208
#[derive(Debug)]
@@ -496,7 +496,7 @@ pub struct UrlPatternComponentResult {
496496
/// The matched input for this component.
497497
pub input: String,
498498
/// The values for all named groups in the pattern.
499-
pub groups: std::collections::HashMap<String, String>,
499+
pub groups: std::collections::HashMap<String, Option<String>>,
500500
}
501501

502502
#[cfg(test)]
@@ -525,7 +525,7 @@ mod tests {
525525
#[derive(Debug, Deserialize)]
526526
struct ComponentResult {
527527
input: String,
528-
groups: HashMap<String, String>,
528+
groups: HashMap<String, Option<String>>,
529529
}
530530

531531
#[derive(Deserialize)]
@@ -787,7 +787,7 @@ mod tests {
787787
if !exactly_empty_components
788788
.contains(&stringify!($component).to_owned())
789789
{
790-
groups.insert("0".to_owned(), "".to_owned());
790+
groups.insert("0".to_owned(), Some("".to_owned()));
791791
}
792792
UrlPatternComponentResult {
793793
input: "".to_owned(),

src/matcher.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,10 @@ impl<R: RegExp> Matcher<R> {
4949
}
5050
}
5151

52-
pub fn matches<'a>(&self, mut input: &'a str) -> Option<Vec<&'a str>> {
52+
pub fn matches<'a>(
53+
&self,
54+
mut input: &'a str,
55+
) -> Option<Vec<Option<&'a str>>> {
5356
let prefix_len = self.prefix.len();
5457
let suffix_len = self.suffix.len();
5558
let input_len = input.len();
@@ -82,7 +85,7 @@ impl<R: RegExp> Matcher<R> {
8285
return None;
8386
}
8487
}
85-
Some(vec![input])
88+
Some(vec![Some(input)])
8689
}
8790
InnerMatcher::RegExp { regexp, .. } => {
8891
regexp.as_ref().unwrap().matches(input)

src/parser.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ where
386386
if name_token.is_some() || regexp_or_wildcard_token.is_some() {
387387
let mut prefix = String::new();
388388
if let Some(char_token) = char_token {
389-
prefix = char_token.value.to_owned();
389+
char_token.value.clone_into(&mut prefix);
390390
}
391391
if !prefix.is_empty() && prefix != options.prefix_code_point {
392392
parser.pending_fixed_value.push_str(&prefix);

src/quirks.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ impl RegExp for EcmaRegexp {
176176
Ok(EcmaRegexp(pattern.to_string()))
177177
}
178178

179-
fn matches<'a>(&self, text: &'a str) -> Option<Vec<&'a str>> {
179+
fn matches<'a>(&self, text: &'a str) -> Option<Vec<Option<&'a str>>> {
180180
let regexp = regex::Regex::parse(&self.0).ok()?;
181181
regexp.matches(text)
182182
}

src/regexp.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ pub trait RegExp: Sized {
1111
/// of captures. The matches are returned in the order they appear in the
1212
/// regular expression. It is **not** prefixed with the full match. For groups
1313
/// that occur in the regular expression, but did not match, the corresponding
14-
/// capture should be the empty string ("").
14+
/// capture should be `None`.
1515
///
1616
/// Returns `None` if the text does not match the regular expression.
17-
fn matches<'a>(&self, text: &'a str) -> Option<Vec<&'a str>>;
17+
fn matches<'a>(&self, text: &'a str) -> Option<Vec<Option<&'a str>>>;
1818
}
1919

2020
impl RegExp for regex::Regex {
@@ -26,13 +26,13 @@ impl RegExp for regex::Regex {
2626
regex::Regex::new(pattern).map_err(|_| ())
2727
}
2828

29-
fn matches<'a>(&self, text: &'a str) -> Option<Vec<&'a str>> {
29+
fn matches<'a>(&self, text: &'a str) -> Option<Vec<Option<&'a str>>> {
3030
let captures = self.captures(text)?;
3131

3232
let captures = captures
3333
.iter()
3434
.skip(1)
35-
.map(|c| c.map(|m| m.as_str()).unwrap_or(""))
35+
.map(|c| c.map(|m| m.as_str()))
3636
.collect();
3737

3838
Some(captures)

src/testdata/urlpatterntestdata.json

+21-11
Original file line numberDiff line numberDiff line change
@@ -354,8 +354,9 @@
354354
{
355355
"pattern": [{ "pathname": "/foo/:bar?" }],
356356
"inputs": [{ "pathname": "/foo" }],
357+
"//": "The `null` below is translated to undefined in the test harness.",
357358
"expected_match": {
358-
"pathname": { "input": "/foo", "groups": { "bar": "" } }
359+
"pathname": { "input": "/foo", "groups": { "bar": null } }
359360
}
360361
},
361362
{
@@ -419,8 +420,9 @@
419420
{
420421
"pattern": [{ "pathname": "/foo/:bar*" }],
421422
"inputs": [{ "pathname": "/foo" }],
423+
"//": "The `null` below is translated to undefined in the test harness.",
422424
"expected_match": {
423-
"pathname": { "input": "/foo", "groups": { "bar": "" } }
425+
"pathname": { "input": "/foo", "groups": { "bar": null } }
424426
}
425427
},
426428
{
@@ -473,15 +475,17 @@
473475
"expected_obj": {
474476
"pathname": "/foo/*?"
475477
},
478+
"//": "The `null` below is translated to undefined in the test harness.",
476479
"expected_match": {
477-
"pathname": { "input": "/foo", "groups": { "0": "" } }
480+
"pathname": { "input": "/foo", "groups": { "0": null } }
478481
}
479482
},
480483
{
481484
"pattern": [{ "pathname": "/foo/*?" }],
482485
"inputs": [{ "pathname": "/foo" }],
486+
"//": "The `null` below is translated to undefined in the test harness.",
483487
"expected_match": {
484-
"pathname": { "input": "/foo", "groups": { "0": "" } }
488+
"pathname": { "input": "/foo", "groups": { "0": null } }
485489
}
486490
},
487491
{
@@ -657,15 +661,17 @@
657661
"expected_obj": {
658662
"pathname": "/foo/**"
659663
},
664+
"//": "The `null` below is translated to undefined in the test harness.",
660665
"expected_match": {
661-
"pathname": { "input": "/foo", "groups": { "0": "" } }
666+
"pathname": { "input": "/foo", "groups": { "0": null } }
662667
}
663668
},
664669
{
665670
"pattern": [{ "pathname": "/foo/**" }],
666671
"inputs": [{ "pathname": "/foo" }],
672+
"//": "The `null` below is translated to undefined in the test harness.",
667673
"expected_match": {
668-
"pathname": { "input": "/foo", "groups": { "0": "" } }
674+
"pathname": { "input": "/foo", "groups": { "0": null } }
669675
}
670676
},
671677
{
@@ -1812,9 +1818,10 @@
18121818
"hostname": "(sub.)?example.com",
18131819
"pathname": "/foo"
18141820
},
1821+
"//": "The `null` below is translated to undefined in the test harness.",
18151822
"expected_match": {
18161823
"protocol": { "input": "https", "groups": {} },
1817-
"hostname": { "input": "example.com", "groups": { "0": "" } },
1824+
"hostname": { "input": "example.com", "groups": { "0": null } },
18181825
"pathname": { "input": "/foo", "groups": {} }
18191826
}
18201827
},
@@ -1850,9 +1857,10 @@
18501857
"hostname": "(sub(?:.))?example.com",
18511858
"pathname": "/foo"
18521859
},
1860+
"//": "The `null` below is translated to undefined in the test harness.",
18531861
"expected_match": {
18541862
"protocol": { "input": "https", "groups": {} },
1855-
"hostname": { "input": "example.com", "groups": { "0": "" } },
1863+
"hostname": { "input": "example.com", "groups": { "0": null } },
18561864
"pathname": { "input": "/foo", "groups": {} }
18571865
}
18581866
},
@@ -2299,9 +2307,10 @@
22992307
"protocol": "data",
23002308
"pathname": "text/javascript,let x = 100/:tens?5;"
23012309
},
2310+
"//": "The `null` below is translated to undefined in the test harness.",
23022311
"expected_match": {
23032312
"protocol": { "input": "data", "groups": {} },
2304-
"pathname": { "input": "text/javascript,let x = 100/5;", "groups": { "tens": "" } }
2313+
"pathname": { "input": "text/javascript,let x = 100/5;", "groups": { "tens": null } }
23052314
}
23062315
},
23072316
{
@@ -2416,7 +2425,6 @@
24162425
"expected_obj": "error"
24172426
},
24182427
{
2419-
"skip": "bug in rust-url: https://github.com/servo/rust-url/pull/718",
24202428
"pattern": [{ "hostname": "bad|hostname" }],
24212429
"expected_obj": "error"
24222430
},
@@ -2618,13 +2626,15 @@
26182626
}
26192627
},
26202628
{
2629+
"skip": "only works in ecmascript variety of regex",
26212630
"pattern": [{ "pathname": "*{}**?" }],
26222631
"inputs": [{ "pathname": "foobar" }],
26232632
"expected_obj": {
26242633
"pathname": "*(.*)?"
26252634
},
2635+
"//": "The `null` below is translated to undefined in the test harness.",
26262636
"expected_match": {
2627-
"pathname": { "input": "foobar", "groups": { "0": "foobar", "1": "" }}
2637+
"pathname": { "input": "foobar", "groups": { "0": "foobar", "1": null }}
26282638
}
26292639
},
26302640
{

0 commit comments

Comments
 (0)