-
Notifications
You must be signed in to change notification settings - Fork 31
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
[FEAT] Depth First Traversal Methods #19
base: master
Are you sure you want to change the base?
Conversation
Thanks for the PR to start the discussion. I'm interesting in learning more about some of the use-cases you have in mind. A few thoughts in response to your questions:
|
let uppercased_comments = root_xml_node.traverse(|xml_node| match xml_node {
XMLNode::Comment(comment) => true,
_ => false,
}).map(|XMLNode::Comment(comment)| comment.to_uppercase()) I think it becomes more interesting when combining it with the writing back out - allowing intermediate transformations on the XML while preserving the parts that are not of concern (comments, etc).
let some_elements = xml_node.traverse(|node| {
if let XMLNode::Element(element) = node {
return element.name == 'SomeElement';
}
return false;
}).filter(|node| {
if let XMLNode::Element(element) = node {
return element.name == 'SomeElement';
}
return false;
}); Although now that I write it out (and since its probably the most common use case) this is incredibly bad to write. There should probably just be a I have a related method I threw together for modifying a tree that might similarly useful pub fn traverse_and_process<I: Iterator<Item=XMLNode>>(
xml_tree: I,
processor: fn(&XMLNode) -> XMLNode,
) -> Vec<XMLNode> {
let mut new_tree = Vec::new();
for node in xml_tree {
new_tree.push(match node {
XMLNode::CData(_) => processor(&node),
XMLNode::Comment(_) => processor(&node),
XMLNode::ProcessingInstruction(_, _) => processor(&node),
XMLNode::Text(_) => processor(&node),
XMLNode::Element(element) => {
let mut new_element = element.clone();
new_element.children = traverse_and_process(element.children.into_iter(), processor);
processor(&XMLNode::Element(new_element))
}
})
}
new_tree
} Also not sure how you feel about returning Iterators vs Vectors. I like the completeness and simplicity of Vectors, but the usability and implied performance benefits of Iterators in terms of performing intermediate transformations/filters/maps/etc. |
In this example you wrote: let uppercased_comments = root_xml_node.traverse(|xml_node| match xml_node {
XMLNode::Comment(comment) => true,
_ => false,
}).map(|XMLNode::Comment(comment)| comment.to_uppercase()) I like your other suggestion better of using a let uppercased_comments = root_xml_node.traverse()
.filter(|node| node.as_comment().is_some())
.map(|XMLNode::Comment(comment)| comment.to_uppercase()) Where (BTW, this example of getting all comments is compelling to me, so I now agree we should support traversal over I guess I still don't fully understand the point of something like |
With something like Did a second pass at it here which allows removing/changing nodes
|
Just to confirm: did you mean to close this PR? |
No |
This adds traits and implementations to do depth first traversals (both pre-order and post-order). It's still rough and needs some work to organize the api and code, but it should provide some good utility to finding nodes in the tree efficiently.
Definitely still needs discussion before it would be ready for consideration for merging.
Additionally moved the parse and parse_all functions into the module scope rather than scoped to Element (though they are still on Element and delegated to the module scope functions to keep backwards compatibility). <- This needs to be moved into another PR I think.
I think some additional questions to be answered: