Skip to content

Commit 95a7b4a

Browse files
committed
Reinstate Option over ViolationFn, drop NoOp, decentralize
This moves some logically ViolationFn functions back into the Parser and further spreads the use of `if let Some(vfn)`. It does however reduce the total number of changes for the feature.
1 parent 96148cf commit 95a7b4a

File tree

2 files changed

+36
-51
lines changed

2 files changed

+36
-51
lines changed

src/lib.rs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ impl HeapSizeOf for Url {
186186
pub struct ParseOptions<'a> {
187187
base_url: Option<&'a Url>,
188188
encoding_override: encoding::EncodingOverride,
189-
violation_fn: ViolationFn<'a>,
189+
violation_fn: Option<ViolationFn<'a>>,
190190
}
191191

192192
impl<'a> ParseOptions<'a> {
@@ -216,10 +216,7 @@ impl<'a> ParseOptions<'a> {
216216
/// passed to either method will be used by a parser.
217217
#[deprecated]
218218
pub fn log_syntax_violation(mut self, new: Option<&'a Fn(&'static str)>) -> Self {
219-
self.violation_fn = match new {
220-
Some(f) => ViolationFn::OldFn(f),
221-
None => ViolationFn::NoOp
222-
};
219+
self.violation_fn = new.map(|f| ViolationFn::OldFn(f));
223220
self
224221
}
225222

@@ -246,10 +243,7 @@ impl<'a> ParseOptions<'a> {
246243
/// # run().unwrap();
247244
/// ```
248245
pub fn syntax_violation_callback(mut self, new: Option<&'a Fn(SyntaxViolation)>) -> Self {
249-
self.violation_fn = match new {
250-
Some(f) => ViolationFn::NewFn(f),
251-
None => ViolationFn::NoOp
252-
};
246+
self.violation_fn = new.map(|f| ViolationFn::NewFn(f));
253247
self
254248
}
255249

@@ -402,7 +396,7 @@ impl Url {
402396
ParseOptions {
403397
base_url: None,
404398
encoding_override: EncodingOverride::utf8(),
405-
violation_fn: ViolationFn::NoOp,
399+
violation_fn: None,
406400
}
407401
}
408402

src/parser.rs

Lines changed: 32 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -160,12 +160,12 @@ pub struct Input<'i> {
160160

161161
impl<'i> Input<'i> {
162162
pub fn new(input: &'i str) -> Self {
163-
Input::with_log(input, ViolationFn::NoOp)
163+
Input::with_log(input, None)
164164
}
165165

166-
pub fn with_log(original_input: &'i str, vfn: ViolationFn) -> Self {
166+
pub fn with_log(original_input: &'i str, vfn: Option<ViolationFn>) -> Self {
167167
let input = original_input.trim_matches(c0_control_or_space);
168-
if vfn.is_set() {
168+
if let Some(vfn) = vfn {
169169
if input.len() < original_input.len() {
170170
vfn.call(SyntaxViolation::C0SpaceIgnored)
171171
}
@@ -268,7 +268,6 @@ impl<'i> Iterator for Input<'i> {
268268
pub enum ViolationFn<'a> {
269269
NewFn(&'a (Fn(SyntaxViolation) + 'a)),
270270
OldFn(&'a (Fn(&'static str) + 'a)),
271-
NoOp
272271
}
273272

274273
impl<'a> ViolationFn<'a> {
@@ -277,27 +276,6 @@ impl<'a> ViolationFn<'a> {
277276
match self {
278277
ViolationFn::NewFn(f) => f(v),
279278
ViolationFn::OldFn(f) => f(v.description()),
280-
ViolationFn::NoOp => {}
281-
}
282-
}
283-
284-
/// Call with a violation, if provided test returns true. Avoids
285-
/// the test entirely if `NoOp`.
286-
pub fn call_if<F>(self, v: SyntaxViolation, test: F)
287-
where F: Fn() -> bool
288-
{
289-
match self {
290-
ViolationFn::NewFn(f) => if test() { f(v) },
291-
ViolationFn::OldFn(f) => if test() { f(v.description()) },
292-
ViolationFn::NoOp => {} // avoid test
293-
}
294-
}
295-
296-
/// True if not `NoOp`
297-
pub fn is_set(self) -> bool {
298-
match self {
299-
ViolationFn::NoOp => false,
300-
_ => true
301279
}
302280
}
303281
}
@@ -307,7 +285,6 @@ impl<'a> fmt::Debug for ViolationFn<'a> {
307285
match *self {
308286
ViolationFn::NewFn(_) => write!(f, "NewFn(Fn(SyntaxViolation))"),
309287
ViolationFn::OldFn(_) => write!(f, "OldFn(Fn(&'static str))"),
310-
ViolationFn::NoOp => write!(f, "NoOp")
311288
}
312289
}
313290
}
@@ -316,7 +293,7 @@ pub struct Parser<'a> {
316293
pub serialization: String,
317294
pub base_url: Option<&'a Url>,
318295
pub query_encoding_override: EncodingOverride,
319-
pub violation_fn: ViolationFn<'a>,
296+
pub violation_fn: Option<ViolationFn<'a>>,
320297
pub context: Context,
321298
}
322299

@@ -333,11 +310,26 @@ impl<'a> Parser<'a> {
333310
serialization: serialization,
334311
base_url: None,
335312
query_encoding_override: EncodingOverride::utf8(),
336-
violation_fn: ViolationFn::NoOp,
313+
violation_fn: None,
337314
context: Context::Setter,
338315
}
339316
}
340317

318+
fn syntax_violation(&self, v: SyntaxViolation) {
319+
if let Some(vfn) = self.violation_fn {
320+
vfn.call(v)
321+
}
322+
}
323+
324+
fn syntax_violation_if<F: Fn() -> bool>(&self, v: SyntaxViolation, test: F) {
325+
// Skip test if not logging.
326+
if let Some(vfn) = self.violation_fn {
327+
if test() {
328+
vfn.call(v)
329+
}
330+
}
331+
}
332+
341333
/// https://url.spec.whatwg.org/#concept-basic-url-parser
342334
pub fn parse_url(mut self, input: &str) -> ParseResult<Url> {
343335
let input = Input::with_log(input, self.violation_fn);
@@ -397,7 +389,7 @@ impl<'a> Parser<'a> {
397389
self.serialization.push(':');
398390
match scheme_type {
399391
SchemeType::File => {
400-
self.violation_fn.call_if(ExpectedFileDoubleSlash, || !input.starts_with("//"));
392+
self.syntax_violation_if(ExpectedFileDoubleSlash, || !input.starts_with("//"));
401393
let base_file_url = self.base_url.and_then(|base| {
402394
if base.scheme() == "file" { Some(base) } else { None }
403395
});
@@ -417,7 +409,7 @@ impl<'a> Parser<'a> {
417409
}
418410
}
419411
// special authority slashes state
420-
self.violation_fn.call_if(ExpectedDoubleSlash, || {
412+
self.syntax_violation_if(ExpectedDoubleSlash, || {
421413
input.clone().take_while(|&c| matches!(c, '/' | '\\'))
422414
.collect::<String>() != "//"
423415
});
@@ -551,10 +543,10 @@ impl<'a> Parser<'a> {
551543
}
552544
}
553545
Some('/') | Some('\\') => {
554-
self.violation_fn.call_if(Backslash, || first_char == Some('\\'));
546+
self.syntax_violation_if(Backslash, || first_char == Some('\\'));
555547
// file slash state
556548
let (next_char, input_after_next_char) = input_after_first_char.split_first();
557-
self.violation_fn.call_if(Backslash, || next_char == Some('\\'));
549+
self.syntax_violation_if(Backslash, || next_char == Some('\\'));
558550
if matches!(next_char, Some('/') | Some('\\')) {
559551
// file host state
560552
self.serialization.push_str("file://");
@@ -706,7 +698,7 @@ impl<'a> Parser<'a> {
706698
Some('/') | Some('\\') => {
707699
let (slashes_count, remaining) = input.count_matching(|c| matches!(c, '/' | '\\'));
708700
if slashes_count >= 2 {
709-
self.violation_fn.call_if(SyntaxViolation::ExpectedDoubleSlash, || {
701+
self.syntax_violation_if(SyntaxViolation::ExpectedDoubleSlash, || {
710702
input.clone().take_while(|&c| matches!(c, '/' | '\\'))
711703
.collect::<String>() != "//"
712704
});
@@ -770,9 +762,9 @@ impl<'a> Parser<'a> {
770762
match c {
771763
'@' => {
772764
if last_at.is_some() {
773-
self.violation_fn.call(SyntaxViolation::UnencodedAtSign)
765+
self.syntax_violation(SyntaxViolation::UnencodedAtSign)
774766
} else {
775-
self.violation_fn.call(SyntaxViolation::EmbeddedCredentials)
767+
self.syntax_violation(SyntaxViolation::EmbeddedCredentials)
776768
}
777769
last_at = Some((char_count, remaining.clone()))
778770
},
@@ -970,7 +962,7 @@ impl<'a> Parser<'a> {
970962
match input.split_first() {
971963
(Some('/'), remaining) => input = remaining,
972964
(Some('\\'), remaining) => if scheme_type.is_special() {
973-
self.violation_fn.call(SyntaxViolation::Backslash);
965+
self.syntax_violation(SyntaxViolation::Backslash);
974966
input = remaining
975967
},
976968
_ => {}
@@ -998,7 +990,7 @@ impl<'a> Parser<'a> {
998990
},
999991
'\\' if self.context != Context::PathSegmentSetter &&
1000992
scheme_type.is_special() => {
1001-
self.violation_fn.call(SyntaxViolation::Backslash);
993+
self.syntax_violation(SyntaxViolation::Backslash);
1002994
ends_with_slash = true;
1003995
break
1004996
},
@@ -1039,7 +1031,7 @@ impl<'a> Parser<'a> {
10391031
self.serialization.push(':');
10401032
}
10411033
if *has_host {
1042-
self.violation_fn.call(SyntaxViolation::FileWithHostAndWindowsDrive);
1034+
self.syntax_violation(SyntaxViolation::FileWithHostAndWindowsDrive);
10431035
*has_host = false; // FIXME account for this in callers
10441036
}
10451037
}
@@ -1181,7 +1173,7 @@ impl<'a> Parser<'a> {
11811173
pub fn parse_fragment(&mut self, mut input: Input) {
11821174
while let Some((c, utf8_c)) = input.next_utf8() {
11831175
if c == '\0' {
1184-
self.violation_fn.call(SyntaxViolation::NullInFragment)
1176+
self.syntax_violation(SyntaxViolation::NullInFragment)
11851177
} else {
11861178
self.check_url_code_point(c, &input);
11871179
self.serialization.extend(utf8_percent_encode(utf8_c,
@@ -1191,8 +1183,7 @@ impl<'a> Parser<'a> {
11911183
}
11921184

11931185
fn check_url_code_point(&self, c: char, input: &Input) {
1194-
let vfn = self.violation_fn;
1195-
if vfn.is_set() {
1186+
if let Some(vfn) = self.violation_fn {
11961187
if c == '%' {
11971188
let mut input = input.clone();
11981189
if !matches!((input.next(), input.next()), (Some(a), Some(b))

0 commit comments

Comments
 (0)