Skip to content

Commit d91b957

Browse files
committed
Make capture lazy
Fixes #1
1 parent 2a0a85c commit d91b957

File tree

2 files changed

+60
-39
lines changed

2 files changed

+60
-39
lines changed

ua-parser/src/lib.rs

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
#![warn(missing_docs)]
33
#![allow(clippy::empty_docs)]
44
#![doc = include_str!("../README.md")]
5-
6-
use regex::Captures;
75
use serde::Deserialize;
86

97
pub use regex_filtered::{BuildError, ParseError};
@@ -123,6 +121,7 @@ impl<'a> Extractor<'a> {
123121
pub mod user_agent {
124122
use serde::Deserialize;
125123
use std::borrow::Cow;
124+
use std::cell::LazyCell;
126125

127126
use crate::resolvers::{FallbackResolver, FamilyResolver};
128127
use regex_filtered::BuildError;
@@ -234,16 +233,16 @@ pub mod user_agent {
234233
/// but there is no group in the regex
235234
pub fn extract(&'a self, ua: &'a str) -> Option<ValueRef<'a>> {
236235
let (idx, re) = self.matcher.matching(ua).next()?;
237-
let c = re.captures(ua)?;
236+
let c = LazyCell::new(move || re.captures(ua));
238237

239238
let (f, v1, v2, v3, v4) = &self.repl[idx];
240239

241240
Some(ValueRef {
242-
family: f.resolve(&c),
243-
major: v1.resolve(&c),
244-
minor: v2.resolve(&c),
245-
patch: v3.resolve(&c),
246-
patch_minor: v4.resolve(&c),
241+
family: f.resolve(&c)?,
242+
major: v1.resolve(&c)?,
243+
minor: v2.resolve(&c)?,
244+
patch: v3.resolve(&c)?,
245+
patch_minor: v4.resolve(&c)?,
247246
})
248247
}
249248
}
@@ -300,6 +299,7 @@ pub mod user_agent {
300299
pub mod os {
301300
use serde::Deserialize;
302301
use std::borrow::Cow;
302+
use std::cell::LazyCell;
303303

304304
use regex_filtered::{BuildError, ParseError};
305305

@@ -396,16 +396,16 @@ pub mod os {
396396
/// returns `None` if the UA string could not be matched.
397397
pub fn extract(&'a self, ua: &'a str) -> Option<ValueRef<'a>> {
398398
let (idx, re) = self.matcher.matching(ua).next()?;
399-
let c = re.captures(ua)?;
399+
let c = LazyCell::new(move || re.captures(ua));
400400

401401
let (o, v1, v2, v3, v4) = &self.repl[idx];
402402

403403
Some(ValueRef {
404-
os: o.resolve(&c),
405-
major: v1.resolve(&c),
406-
minor: v2.resolve(&c),
407-
patch: v3.resolve(&c),
408-
patch_minor: v4.resolve(&c),
404+
os: o.resolve(&c)?,
405+
major: v1.resolve(&c)?,
406+
minor: v2.resolve(&c)?,
407+
patch: v3.resolve(&c)?,
408+
patch_minor: v4.resolve(&c)?,
409409
})
410410
}
411411
}
@@ -460,6 +460,7 @@ pub mod os {
460460
pub mod device {
461461
use serde::Deserialize;
462462
use std::borrow::Cow;
463+
use std::cell::LazyCell;
463464

464465
use regex_filtered::{BuildError, ParseError};
465466

@@ -558,14 +559,14 @@ pub mod device {
558559
/// the input.
559560
pub fn extract(&'a self, ua: &'a str) -> Option<ValueRef<'a>> {
560561
let (idx, re) = self.matcher.matching(ua).next()?;
561-
let c = re.captures(ua)?;
562+
let c = LazyCell::new(move || re.captures(ua));
562563

563564
let (d, v1, v2) = &self.repl[idx];
564565

565566
Some(ValueRef {
566-
device: d.resolve(&c),
567-
brand: v1.resolve(&c),
568-
model: v2.resolve(&c),
567+
device: d.resolve(&c)?,
568+
brand: v1.resolve(&c)?,
569+
model: v2.resolve(&c)?,
569570
})
570571
}
571572
}

ua-parser/src/resolvers.rs

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,17 @@
77
use crate::Error;
88
use regex::Captures;
99
use std::borrow::Cow;
10+
use std::cell::LazyCell;
1011

11-
fn get<'s>(c: &Captures<'s>, group: usize) -> Option<&'s str> {
12-
c.get(group).map(|g| g.as_str()).filter(|s| !s.is_empty())
12+
type C<'s, F> = LazyCell<Option<Captures<'s>>, F>;
13+
14+
fn get<'s, F>(c: &C<'s, F>, group: usize) -> Option<Option<&'s str>>
15+
where
16+
F: FnOnce() -> Option<Captures<'s>>,
17+
{
18+
LazyCell::force(c)
19+
.as_ref()
20+
.map(|c| c.get(group).map(|g| g.as_str()).filter(|s| !s.is_empty()))
1321
}
1422

1523
// TODO:
@@ -50,21 +58,24 @@ impl<'a> Resolver<'a> {
5058
}
5159
}
5260

53-
pub(crate) fn resolve(&'a self, c: &Captures<'a>) -> Cow<'a, str> {
54-
match self {
61+
pub(crate) fn resolve<F>(&'a self, c: &C<'a, F>) -> Option<Cow<'a, str>>
62+
where
63+
F: FnOnce() -> Option<Captures<'a>>,
64+
{
65+
Some(match self {
5566
Self::Replacement(s) => (**s).into(),
56-
Self::Capture(i) => get(c, *i).unwrap_or("").into(),
67+
Self::Capture(i) => get(c, *i)?.unwrap_or("").into(),
5768
Self::Template(t) => {
5869
let mut r = String::new();
59-
c.expand(t, &mut r);
70+
LazyCell::force(c).as_ref()?.expand(t, &mut r);
6071
let trimmed = r.trim();
6172
if r.len() == trimmed.len() {
6273
r.into()
6374
} else {
6475
trimmed.to_string().into()
6576
}
6677
}
67-
}
78+
})
6879
}
6980
}
7081

@@ -90,14 +101,17 @@ impl<'a> OptResolver<'a> {
90101
}
91102
}
92103

93-
pub(crate) fn resolve(&'a self, c: &Captures<'a>) -> Option<Cow<'a, str>> {
94-
match self {
104+
pub(crate) fn resolve<F>(&'a self, c: &C<'a, F>) -> Option<Option<Cow<'a, str>>>
105+
where
106+
F: FnOnce() -> Option<Captures<'a>>,
107+
{
108+
Some(match self {
95109
Self::None => None,
96110
Self::Replacement(s) => Some((**s).into()),
97-
Self::Capture(i) => get(c, *i).map(From::from),
111+
Self::Capture(i) => get(c, *i)?.map(From::from),
98112
Self::Template(t) => {
99113
let mut r = String::new();
100-
c.expand(t, &mut r);
114+
LazyCell::force(c).as_ref()?.expand(t, &mut r);
101115
let trimmed = r.trim();
102116
if trimmed.is_empty() {
103117
None
@@ -107,7 +121,7 @@ impl<'a> OptResolver<'a> {
107121
Some(trimmed.to_string().into())
108122
}
109123
}
110-
}
124+
})
111125
}
112126
}
113127

@@ -135,12 +149,15 @@ impl<'a> FamilyResolver<'a> {
135149
}
136150
}
137151

138-
pub(crate) fn resolve(&'a self, c: &super::Captures<'a>) -> Cow<'a, str> {
139-
match self {
140-
FamilyResolver::Capture => get(c, 1).unwrap_or("").into(),
152+
pub(crate) fn resolve<F>(&'a self, c: &C<'a, F>) -> Option<Cow<'a, str>>
153+
where
154+
F: FnOnce() -> Option<Captures<'a>>,
155+
{
156+
Some(match self {
157+
FamilyResolver::Capture => get(c, 1)?.unwrap_or("").into(),
141158
FamilyResolver::Replacement(s) => (**s).into(),
142-
FamilyResolver::Template(t) => t.replace("$1", get(c, 1).unwrap_or("")).into(),
143-
}
159+
FamilyResolver::Template(t) => t.replace("$1", get(c, 1)?.unwrap_or("")).into(),
160+
})
144161
}
145162
}
146163

@@ -161,11 +178,14 @@ impl<'a> FallbackResolver<'a> {
161178
Self::None
162179
}
163180
}
164-
pub(crate) fn resolve(&'a self, c: &super::Captures<'a>) -> Option<&'a str> {
165-
match self {
181+
pub(crate) fn resolve<F>(&'a self, c: &C<'a, F>) -> Option<Option<&'a str>>
182+
where
183+
F: FnOnce() -> Option<Captures<'a>>,
184+
{
185+
Some(match self {
166186
FallbackResolver::None => None,
167-
FallbackResolver::Capture(n) => get(c, *n),
187+
FallbackResolver::Capture(n) => get(c, *n)?,
168188
FallbackResolver::Replacement(r) => Some(r),
169-
}
189+
})
170190
}
171191
}

0 commit comments

Comments
 (0)