Skip to content

Commit

Permalink
Merge pull request #71 from emwalker/node-id
Browse files Browse the repository at this point in the history
Add NodeId to wrap usize
  • Loading branch information
jaytaph authored Oct 2, 2023
2 parents b56f51c + 77ca0b7 commit fc57eb3
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 81 deletions.
8 changes: 4 additions & 4 deletions src/bin/parser_test.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use gosub_engine::html5_parser::input_stream::InputStream;
use gosub_engine::html5_parser::node::NodeData;
use gosub_engine::html5_parser::node::{NodeData, NodeId};
use gosub_engine::html5_parser::parser::document::Document;
use gosub_engine::html5_parser::parser::Html5Parser;
use regex::Regex;
Expand Down Expand Up @@ -271,20 +271,20 @@ pub struct Error {
**/

fn match_document_tree(document: &Document, expected: &Vec<String>) -> bool {
match_node(0, -1, -1, document, expected);
match_node(NodeId::root(), -1, -1, document, expected);
true
}

fn match_node(
node_idx: usize,
node_idx: NodeId,
expected_id: isize,
indent: isize,
document: &Document,
expected: &Vec<String>,
) -> Option<usize> {
let node = document.get_node_by_id(node_idx).unwrap();

if node_idx > 0 {
if node_idx.is_positive() {
match &node.data {
NodeData::Element { name, .. } => {
let value = format!("|{}<{}>", " ".repeat((indent as usize * 2) + 1), name);
Expand Down
71 changes: 60 additions & 11 deletions src/html5_parser/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,63 @@ pub enum NodeData {
},
}

/// Id used to identify a node
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
pub struct NodeId(usize);

impl From<NodeId> for usize {
fn from(value: NodeId) -> Self {
value.0
}
}

impl From<usize> for NodeId {
fn from(value: usize) -> Self {
Self(value)
}
}

impl Default for &NodeId {
fn default() -> Self {
&NodeId(0)
}
}

impl NodeId {
// TODO: Drop Default derive and only use 0 for the root, or choose another id for the root
pub const ROOT_NODE: usize = 0;

pub fn root() -> Self {
Self(Self::ROOT_NODE)
}

pub fn is_positive(&self) -> bool {
self.0 > 0
}

pub fn is_root(&self) -> bool {
self.0 == Self::ROOT_NODE
}

pub fn next(&self) -> Self {
// Might panic
Self(self.0 + 1)
}

pub fn prev(&self) -> Self {
// Might panic
Self(self.0 - 1)
}
}

/// Node that resembles a DOM node
pub struct Node {
/// ID of the node, 0 is always the root / document node
pub id: usize,
pub id: NodeId,
/// parent of the node, if any
pub parent: Option<usize>,
pub parent: Option<NodeId>,
/// children of the node
pub children: Vec<usize>,
pub children: Vec<NodeId>,
/// name of the node, or empty when it's not a tag
pub name: String,
/// namespace of the node
Expand All @@ -65,7 +114,7 @@ impl Node {
/// Create a new document node
pub fn new_document() -> Self {
Node {
id: 0,
id: Default::default(),
parent: None,
children: vec![],
data: NodeData::Document {},
Expand All @@ -77,7 +126,7 @@ impl Node {
/// Create a new element node with the given name and attributes and namespace
pub fn new_element(name: &str, attributes: HashMap<String, String>, namespace: &str) -> Self {
Node {
id: 0,
id: Default::default(),
parent: None,
children: vec![],
data: NodeData::Element {
Expand All @@ -92,7 +141,7 @@ impl Node {
/// Create a new comment node
pub fn new_comment(value: &str) -> Self {
Node {
id: 0,
id: Default::default(),
parent: None,
children: vec![],
data: NodeData::Comment {
Expand All @@ -106,7 +155,7 @@ impl Node {
/// Create a new text node
pub fn new_text(value: &str) -> Self {
Node {
id: 0,
id: Default::default(),
parent: None,
children: vec![],
data: NodeData::Text {
Expand Down Expand Up @@ -251,7 +300,7 @@ mod test {
#[test]
fn test_new_document() {
let node = Node::new_document();
assert_eq!(node.id, 0);
assert_eq!(node.id, NodeId::default());
assert_eq!(node.parent, None);
assert!(node.children.is_empty());
assert_eq!(node.name, "".to_string());
Expand All @@ -264,7 +313,7 @@ mod test {
let mut attributes = HashMap::new();
attributes.insert("id".to_string(), "test".to_string());
let node = Node::new_element("div", attributes.clone(), HTML_NAMESPACE);
assert_eq!(node.id, 0);
assert_eq!(node.id, NodeId::default());
assert_eq!(node.parent, None);
assert!(node.children.is_empty());
assert_eq!(node.name, "div".to_string());
Expand All @@ -281,7 +330,7 @@ mod test {
#[test]
fn test_new_comment() {
let node = Node::new_comment("test");
assert_eq!(node.id, 0);
assert_eq!(node.id, NodeId::default());
assert_eq!(node.parent, None);
assert!(node.children.is_empty());
assert_eq!(node.name, "".to_string());
Expand All @@ -297,7 +346,7 @@ mod test {
#[test]
fn test_new_text() {
let node = Node::new_text("test");
assert_eq!(node.id, 0);
assert_eq!(node.id, NodeId::default());
assert_eq!(node.parent, None);
assert!(node.children.is_empty());
assert_eq!(node.name, "".to_string());
Expand Down
28 changes: 15 additions & 13 deletions src/html5_parser/node_arena.rs
Original file line number Diff line number Diff line change
@@ -1,42 +1,44 @@
use crate::html5_parser::node::Node;
use std::collections::HashMap;

use super::node::NodeId;

pub struct NodeArena {
nodes: HashMap<usize, Node>, // Current nodes
next_id: usize, // next id to use
nodes: HashMap<NodeId, Node>, // Current nodes
next_id: NodeId, // next id to use
}

impl NodeArena {
/// Create a new NodeArena
pub fn new() -> Self {
Self {
nodes: HashMap::new(),
next_id: 0,
next_id: Default::default(),
}
}

/// Get the node with the given id
pub fn get_node(&self, node_id: usize) -> Option<&Node> {
pub fn get_node(&self, node_id: NodeId) -> Option<&Node> {
self.nodes.get(&node_id)
}

/// Get the node with the given id as a mutable reference
pub fn get_mut_node(&mut self, node_id: usize) -> Option<&mut Node> {
pub fn get_mut_node(&mut self, node_id: NodeId) -> Option<&mut Node> {
self.nodes.get_mut(&node_id)
}

/// Add the node to the arena and return its id
pub fn add_node(&mut self, mut node: Node) -> usize {
pub fn add_node(&mut self, mut node: Node) -> NodeId {
let id = self.next_id;
self.next_id += 1;
self.next_id = id.next();

node.id = id;
self.nodes.insert(id, node);
id
}

/// Add the node as a child the parent node
pub fn attach_node(&mut self, parent_id: usize, node_id: usize) {
pub fn attach_node(&mut self, parent_id: NodeId, node_id: NodeId) {
//check if any children of node have parent as child
if parent_id == node_id || has_child_recursive(self, node_id, parent_id) {
return;
Expand All @@ -50,7 +52,7 @@ impl NodeArena {
}

/// Removes the node with the given id from the arena
fn remove_node(&mut self, node_id: usize) {
fn remove_node(&mut self, node_id: NodeId) {
// Remove children
if let Some(node) = self.nodes.get_mut(&node_id) {
for child_id in node.children.clone() {
Expand All @@ -68,7 +70,7 @@ impl NodeArena {
}
}

fn has_child_recursive(arena: &mut NodeArena, parent_id: usize, child_id: usize) -> bool {
fn has_child_recursive(arena: &mut NodeArena, parent_id: NodeId, child_id: NodeId) -> bool {
let node = arena.get_mut_node(parent_id).cloned();
if node.is_none() {
return false;
Expand All @@ -86,7 +88,7 @@ fn has_child_recursive(arena: &mut NodeArena, parent_id: usize, child_id: usize)
false
}

fn has_child(arena: &mut NodeArena, parent: Option<Node>, child_id: usize) -> bool {
fn has_child(arena: &mut NodeArena, parent: Option<Node>, child_id: NodeId) -> bool {
let parent_node = if let Some(node) = parent {
node
} else {
Expand Down Expand Up @@ -120,8 +122,8 @@ mod tests {
let node = Node::new_element("test", HashMap::new(), HTML_NAMESPACE);
let id = arena.add_node(node);
assert_eq!(arena.nodes.len(), 1);
assert_eq!(arena.next_id, 1);
assert_eq!(id, 0);
assert_eq!(arena.next_id, 1.into());
assert_eq!(id, NodeId::default());
}

#[test]
Expand Down
Loading

0 comments on commit fc57eb3

Please sign in to comment.