-
Notifications
You must be signed in to change notification settings - Fork 346
Improve syntax violations interface and preserve Copy #433
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve syntax violations interface and preserve Copy #433
Conversation
Wouldn't it be better to introduce a separate trait rather than use a different |
The extension scenarios I could imagine would be best applied to the introduced |
Well, per my understanding of your trait example in #430, the following could adapt back to the current impl SyntaxViolationSink for Fn(SyntaxViolation) {
fn syntax_violation(&self, violation: SyntaxViolation) {
self(violation)
}
} But its more interface surface if SyntaxViolationSink is exported, and I think it needs to be for external closure-based usage? Note that the ViolationFn of this PR doesn't need to be, and isn't exported. Otherwise it should roughly be the same amount of code. I'm not seeing the extension advantage and I doubt there is practical uses of a Sink without the context of the other parse options, the url input str being parsed, and any ParseError? |
I hope I've adequately addressed @nox's concern on extensiblity and implementation strategy. @SimonSapin: Could you review the remaining aspects of this, now with the breaking change concern hopefully out of the way? |
tests/unit.rs
Outdated
}; | ||
let url = Url::options(). | ||
log_syntax_violation(Some(&|s| violation.set(Some(s.to_owned())))). | ||
parse("http:////mozilla.org:42").unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is badly indented, the periods shouldn't be at the end of lines, but at the start.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Converted to periods-on-the-left style in all PR changes.
/// `syntax_violation_callback` and is implemented as an adaptor for the | ||
/// latter, passing the `SyntaxViolation` description. Only the last value | ||
/// passed to either method will be used by a parser. | ||
#[deprecated] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure the method should immediately be made deprecated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its not required for my use case. But why not give deprecation warning to those (relatively few) users of this feature to migrate to Fn(SyntaxViolation)
, calling v.description()
themselves if that is all they want? If deprecated in the next minor release (merged), then this could be removed in some future major release, as cleanup.
@@ -186,7 +186,7 @@ impl HeapSizeOf for Url { | |||
pub struct ParseOptions<'a> { | |||
base_url: Option<&'a Url>, | |||
encoding_override: encoding::EncodingOverride, | |||
log_syntax_violation: Option<&'a Fn(&'static str)>, | |||
violation_fn: ViolationFn<'a>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please store an Option<ViolationFn<
'a>>and remove the
NoOp` variant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I did that, it would complicate things. Firstly, I couldn't directly implement the helper methods call
and call_if
, given the outer type is now Option. Secondly it would complicate those methods even if I could. See the comments around call_if
below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. Why not make the methods on Parser
instead of ViolationFn
? This would let you use Option<T>
, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, for preview, in 95a7b4a I reinstate Option and move the necessary methods back to the Parser. This change would reduce the overall diff for the feature, keeping more status-quo with the parser. However it spreads around the if let Some(vfn) ...
and the *_if
function can't be used outside of the Parser
impl. Input
already uses ViolationFn::call
. If the *_if
variant is ever needed elsewhere this will prove a liability.
I personally still think the current PR is better without this change, but if you prefer 95a7b4a, I will include it in this branch and PR.
src/lib.rs
Outdated
/// # } | ||
/// # run().unwrap(); | ||
/// ``` | ||
pub fn syntax_violation_callback<F>(mut self, new: Option<&'a F>) -> Self |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove F
and pass a trait object here too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Simplified in 6017325. This was legacy from my prior Rc version with pass by value requiring <F>
, but I see that it has no remaining value.
src/parser.rs
Outdated
} | ||
} | ||
|
||
pub fn call_if<F>(self, v: SyntaxViolation, test: F) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please just use if
where you currently call call_if
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Violation#call_if
is direct replacement for the original Parser#syntax_violation_if
. I think this was introduced as and remains a useful optimization and convenience in that it does an early check for NoOp
(short-circuit eval of test). Beyond that I have been trying not to change every call by needing to rewrite with if
blocks, which would be a much more extensive change of the Parser
, and require more scrutiny.
While I could have kept and just modified the original Parser method in place, I think this PR better collects those concerns under ViolationFn
. I added comments to these ViolationFn
methods to hopefully clear this up. Hope that is sufficient.
@@ -216,11 +263,56 @@ impl<'i> Iterator for Input<'i> { | |||
} | |||
} | |||
|
|||
/// Wrapper for syntax violation callback functions. | |||
#[derive(Copy, Clone)] | |||
pub enum ViolationFn<'a> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make this pub(crate)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changing this to pub(crate)
fails with the following errors on the parser module public, but crate private, structs Input
and Parser
. I suspect that all of these cases would need to be made pub(crate)
as well, or module private, but I'm reluctant to add that to this PR, as it expands scope and risk. If it is worth it, consider as a follow-on change?
error[E0446]: private type `parser::ViolationFn<'_>` in public interface
--> src/parser.rs:166:5
|
166 | / pub fn with_log(original_input: &'i str, vfn: ViolationFn) -> Self {
167 | | let input = original_input.trim_matches(c0_control_or_space);
168 | | if vfn.is_set() {
169 | | if input.len() < original_input.len() {
... |
176 | | Input { chars: input.chars() }
177 | | }
| |_____^ can't leak private type
error[E0446]: private type `parser::ViolationFn<'a>` in public interface
--> src/parser.rs:319:5
|
319 | pub violation_fn: ViolationFn<'a>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type
~~~
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess that's ok, I just wanted to make sure ViolationFn
doesn't escape the crate.
This looks ok. Please squash the commits accordingly and I'll r=me it. |
* New SyntaxViolation enum with description the same as prior strings * Replaced Fn signature in ParseOptions and Parser with ViolationFn wrapper enum for old and new functions * ParseOptions: Copy is preserved
96148cf
to
255df06
Compare
@bors-servo r+ |
📌 Commit 255df06 has been approved by |
Improve syntax violations interface and preserve Copy This is an alternative implementation of #430 which avoids introducing an `Rc<Fn>` and preserves `Copy` for `ParseOptions`. Thus, based on my current understanding with the prior review, it is completely backward compatible and with no breaking changes. Its a little less elegant in that, without the `Rc<Fn>`, I can not simple wrap the deprecated log_syntax_violation Fn with an adapting closure. But by moving the two possible function references into a new enum `ViolationFn` it is reasonably well contained. The interface for users is effectively the same, so I think the backward compatibility advantage makes this preferable to #430. Updated summary: ### Summary of changes This makes programmatic use of syntax violations more practical while further centralizing this concern in the parser, which should be a maintenance win. * New `SyntaxViolation` enum with descriptions the same as the prior violation static strings. This enables programmatic use of these violations by variant, e.g. ignoring some, warning on others, providing translated descriptions, etc. Enum implementation uses the same macro expansion pattern as was used for `ParseError`. * New `syntax_violation_callback` signature in `ParseOptions` and `Parser`. * Deprecated `log_syntax_violation` and re-implemented, passing the same violation descriptions for backward compatibility. * Unit tests for old `log_syntax_violation` compatibility, and new `syntax_violation_callback` * Includes rustdoc for old and new interfaces including tested example usage of new callback, as requested in #308. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/rust-url/433) <!-- Reviewable:end -->
☀️ Test successful - status-travis |
This is an alternative implementation of #430 which avoids introducing an
Rc<Fn>
and preservesCopy
forParseOptions
. Thus, based on my current understanding with the prior review, it is completely backward compatible and with no breaking changes. Its a little less elegant in that, without theRc<Fn>
, I can not simple wrap the deprecated log_syntax_violation Fn with an adapting closure. But by moving the two possible function references into a new enumViolationFn
it is reasonably well contained. The interface for users is effectively the same, so I think the backward compatibility advantage makes this preferable to #430. Updated summary:Summary of changes
This makes programmatic use of syntax violations more practical while further centralizing this concern in the parser, which should be a maintenance win.
New
SyntaxViolation
enum with descriptions the same as the prior violation static strings. This enables programmatic use of these violations by variant, e.g. ignoring some, warning on others, providing translated descriptions, etc. Enum implementation uses the same macro expansion pattern as was used forParseError
.New
syntax_violation_callback
signature inParseOptions
andParser
.Deprecated
log_syntax_violation
and re-implemented, passing the same violation descriptions for backward compatibility.Unit tests for old
log_syntax_violation
compatibility, and newsyntax_violation_callback
Includes rustdoc for old and new interfaces including tested example usage of new callback, as requested in Add examples to
ParseOptions
#308.This change is