From ab46fb0341564344cd7428c51141f3f6b044309a Mon Sep 17 00:00:00 2001 From: Joshua Thijssen Date: Tue, 8 Oct 2024 19:12:24 +0200 Subject: [PATCH 1/2] Benchmarks are running again --- Cargo.lock | 72 +++ Cargo.toml | 1 + README.md | 2 +- benches/node_update.rs | 24 +- crates/gosub_html5/src/document/document.rs | 5 + crates/gosub_html5/src/document/task_queue.rs | 138 +++-- crates/gosub_html5/src/html5parser.rs | 3 +- crates/gosub_shared/Cargo.toml | 4 +- crates/gosub_shared/src/document.rs | 45 +- crates/gosub_shared/src/lib.rs | 3 +- crates/gosub_shared/src/location.rs | 2 - crates/gosub_shared/src/mock.rs | 534 ++++++++++++++++++ crates/gosub_shared/src/node_id.rs | 2 - docs/diag.md | 9 - 14 files changed, 775 insertions(+), 69 deletions(-) create mode 100644 crates/gosub_shared/src/mock.rs diff --git a/Cargo.lock b/Cargo.lock index 0562150..9bf80e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -483,6 +483,12 @@ dependencies = [ "syn", ] +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + [[package]] name = "either" version = "1.13.0" @@ -567,6 +573,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fragile" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" + [[package]] name = "getrandom" version = "0.2.15" @@ -613,6 +625,7 @@ dependencies = [ "gosub_html5", "gosub_renderer", "gosub_shared", + "mockall", "test-case", ] @@ -658,6 +671,7 @@ dependencies = [ "getrandom", "js-sys", "lazy_static", + "mockall", "rand 0.9.0-alpha.2", "thiserror", "url", @@ -990,6 +1004,32 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "mockall" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c28b3fb6d753d28c20e826cd46ee611fda1cf3cde03a443a974043247c065a" +dependencies = [ + "cfg-if", + "downcast", + "fragile", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "341014e7f530314e9a1fdbc7400b244efea7122662c96bfa248c31da5bfb2020" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "new_debug_unreachable" version = "1.0.6" @@ -1237,6 +1277,32 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "predicates" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" +dependencies = [ + "anstyle", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" + +[[package]] +name = "predicates-tree" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" +dependencies = [ + "predicates-core", + "termtree", +] + [[package]] name = "proc-macro2" version = "1.0.86" @@ -1731,6 +1797,12 @@ version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" +[[package]] +name = "termtree" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" + [[package]] name = "test-case" version = "3.3.1" diff --git a/Cargo.toml b/Cargo.toml index 9e4873f..05da2e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ gosub_shared = { path = "./crates/gosub_shared", features = [] } gosub_html5 = { path = "./crates/gosub_html5", features = [] } gosub_css3 = { path = "./crates/gosub_css3", features = [] } gosub_renderer = { path = "./crates/gosub_renderer", features = [] } +mockall = "0.13.0" [dev-dependencies] criterion = { version = "0.5", features = ["html_reports"] } diff --git a/README.md b/README.md index d2307f4..f8cf2a6 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ TODO / Test cases we need to cover: - [X] allow user to parse a html5 string and return (random) nodes in a DOM document from it. - [X] attach CSS stylesheets to the DOM - [X] let user query the DOM for nodes -- [ ] let user query the DOM for nodes with a specific CSS selector +- [X] ~~let user query the DOM for nodes with a specific CSS selector~~ - [X] allow user that adds an attribute to a (element) node automatically set the named_ids in the document. - [X] allow user to modify node data from document element (don't know it this is needed / feasable) - [X] add document task queue diff --git a/benches/node_update.rs b/benches/node_update.rs index cefb15b..de3bce1 100644 --- a/benches/node_update.rs +++ b/benches/node_update.rs @@ -1,14 +1,15 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use gosub_css3::MyCssSystem; +use gosub_css3::css3parser::MyCss3Parser; +use gosub_css3::stylesheet::{CssDeclaration, CssRule, CssStylesheet, CssValue}; use gosub_html5::document::builder::DocumentBuilder; use gosub_html5::document::document::MyDocument; use gosub_html5::html5parser::MyHtmlParser; -use gosub_renderer::backend::MyRenderBackend; -use gosub_renderer::layouter::MyLayouter; -use gosub_renderer::render_tree::MyRenderTree; -use gosub_renderer::tree_drawer::MyTreeDrawer; +use gosub_renderer::backend::ratatui::backend::MyRatatuiRenderBackend; +use gosub_renderer::layouter::basic_layouter::BasicLayouter; +use gosub_renderer::render_tree::render_tree::MyRenderTree; +use gosub_renderer::tree_drawer::tree_drawer::MyTreeDrawer; use gosub_shared::node_id::NodeId; -use gosub_shared::traits::css_system::HasCssSystem; +use gosub_shared::traits::css_system::{HasCssParser, HasCssSystem}; use gosub_shared::traits::document::{Document, HasDocument}; use gosub_shared::traits::html5_parser::{HasHtmlParser, HtmlParser}; use gosub_shared::traits::layouter::HasLayouter; @@ -22,7 +23,10 @@ use gosub_shared::traits::tree_drawer::HasTreeDrawer; struct MyModuleConfiguration; impl HasCssSystem for MyModuleConfiguration { - type CssSystem = MyCssSystem; + type CssStylesheet = CssStylesheet; + type CssRule = CssRule; + type CssDeclaration = CssDeclaration; + type CssValue = CssValue; } impl HasDocument for MyModuleConfiguration { @@ -35,7 +39,7 @@ impl HasHtmlParser for MyModuleConfiguration { } impl HasLayouter for MyModuleConfiguration { - type Layouter = MyLayouter; + type Layouter = BasicLayouter; } impl HasRenderTree for MyModuleConfiguration { @@ -47,9 +51,11 @@ impl HasTreeDrawer for MyModuleConfiguration { } impl HasRenderBackend for MyModuleConfiguration { - type RenderBackend = MyRenderBackend; + type RenderBackend = MyRatatuiRenderBackend; } +impl HasCssParser for MyModuleConfiguration { type CssParser = MyCss3Parser; } + impl ModuleConfiguration for MyModuleConfiguration {} fn bench_test_attach(c: &mut Criterion) { diff --git a/crates/gosub_html5/src/document/document.rs b/crates/gosub_html5/src/document/document.rs index 74eb52c..6885cab 100644 --- a/crates/gosub_html5/src/document/document.rs +++ b/crates/gosub_html5/src/document/document.rs @@ -137,3 +137,8 @@ impl Document for MyDocument { qp.query(query) } } + + +#[cfg(test)] +mod tests { +} \ No newline at end of file diff --git a/crates/gosub_html5/src/document/task_queue.rs b/crates/gosub_html5/src/document/task_queue.rs index a0a0386..9e83ec6 100644 --- a/crates/gosub_html5/src/document/task_queue.rs +++ b/crates/gosub_html5/src/document/task_queue.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; +use anyhow::Error; use gosub_shared::document::DocumentHandle; use gosub_shared::location::Location; use gosub_shared::node_id::NodeId; @@ -5,23 +7,37 @@ use gosub_shared::traits::document::{Document, HasDocument}; use gosub_shared::traits::node::{ElementData, Node, NodeBuilder as _}; use crate::node::builder::NodeBuilder; -/// The Document Task Queue is used to queue up tasks that need to be executed on the document. This -/// can be used to defer tasks to a later stage. + +/// Identifier for each task found in the queue. Can be recycled after a flush +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct TaskId(usize); + +/// Destination of the task. It could be a node, the root, or another task that is currently in the task queue +#[derive(Clone, Debug)] +pub enum TaskDestination { + /// Destination is an existing node + #[allow(dead_code)] + Node(NodeId, Option), + /// Destination is a task that is currently in the task queue (does not have a node-id yet) + Task(TaskId, Option), + /// Destination is the root node + DocumentRoot(Option), +} /// Actual task that needs to be completed on flush #[derive(Clone, Debug)] pub struct Task { - /// Node ID that this task is related to. It's either the node-id reserved when creating a node - /// or an existing node-id when inserting an attribute. - pub node_id: NodeId, + pub task_id: TaskId, pub task: DocumentTask, + pub destination: TaskDestination, } impl Task { - pub fn new(node_id: NodeId, task: DocumentTask) -> Self { + pub fn new(task_id: TaskId, task: DocumentTask, destination: TaskDestination) -> Self { Self { - node_id, + task_id, task, + destination, } } } @@ -32,31 +48,24 @@ pub enum DocumentTask { CreateElement { name: String, namespace: String, - location: Location, - parent_id: NodeId, - position: Option, + location: Location }, /// Create a new text node CreateText { content: String, - location: Location, - parent_id: NodeId, - position: Option, + location: Location }, /// Create a new comment node #[allow(dead_code)] CreateComment { content: String, - location: Location, - parent_id: NodeId, - position: Option, + location: Location }, /// Insert an attribute into an element node InsertAttribute { - node_id: NodeId, key: String, value: String, - location: Location, + location: Location } } @@ -65,6 +74,10 @@ pub struct DocumentTaskQueue { doc_handle: DocumentHandle, /// Pending tasks that are needed to be flushed pending_tasks: Vec, + /// Next task id + next_task_id: usize, + /// Mapping of task id to node id in case we reference a task id in a task's destination + task_nodes: HashMap, } impl DocumentTaskQueue { @@ -72,6 +85,8 @@ impl DocumentTaskQueue { Self { doc_handle, pending_tasks: Vec::new(), + next_task_id: 0, + task_nodes: HashMap::new(), } } @@ -81,22 +96,14 @@ impl DocumentTaskQueue { !self.pending_tasks.is_empty() } - /// Add a new task to the queue on the given destination. Returns a reserved node_id (that isn't - /// used yet, but will be once flushed) of the new node. - pub fn add_task(&mut self, task: DocumentTask) -> NodeId { - let node_id = match task { - DocumentTask::InsertAttribute { node_id, .. } => { - // For insertions of attributes, we don't need to reserve a new node_id - node_id - } - _ => { - self.doc_handle.get_mut().get_next_node_id() - } - }; + /// Add a new task to the queue on the given destination + pub fn add_task(&mut self, task: DocumentTask, destination: TaskDestination) -> TaskId { + self.next_task_id += 1; + let task_id = TaskId(self.next_task_id); - self.pending_tasks.push(Task::new(node_id, task)); + self.pending_tasks.push(Task::new(task_id, task, destination)); - node_id + task_id } /// Executes all pending tasks into the document @@ -105,19 +112,41 @@ impl DocumentTaskQueue { for current_task in self.pending_tasks.clone() { match current_task.task { - DocumentTask::CreateElement { name, namespace, location: _location, parent_id, position } => { + DocumentTask::CreateElement { name, namespace, location: _location } => { let new_node = NodeBuilder::new_element_node(name.as_str(), namespace.as_str()); - self.insert_node(current_task.node_id, new_node, parent_id, position); + match self.insert_node(new_node, current_task.destination, current_task.task_id) { + Ok(_) => {}, + Err(e) => errors.push(e.to_string()), + } } - DocumentTask::CreateText { content, location: _location, parent_id, position } => { + DocumentTask::CreateText { content, location: _location } => { let new_node = NodeBuilder::new_text_node(content.as_str()); - self.insert_node(current_task.node_id, new_node, parent_id, position); + match self.insert_node(new_node, current_task.destination, current_task.task_id) { + Ok(_) => {}, + Err(e) => errors.push(e.to_string()), + } } - DocumentTask::CreateComment { content, location: _location, parent_id, position } => { + DocumentTask::CreateComment { content, location: _location } => { let new_node = NodeBuilder::new_comment_node(content.as_str()); - self.insert_node(current_task.node_id, new_node, parent_id, position); + match self.insert_node(new_node, current_task.destination, current_task.task_id) { + Ok(_) => {}, + Err(e) => errors.push(e.to_string()), + } } - DocumentTask::InsertAttribute { key, value, location: _location, node_id } => { + DocumentTask::InsertAttribute { key, value, location: _location } => { + // Find node_id based on destination + let node_id = match current_task.destination { + TaskDestination::Node(node_id, _) => node_id, + TaskDestination::Task(task_id, _) => { + if !self.task_nodes.contains_key(&task_id) { + errors.push(format!("Task id {:?} not found", task_id)); + continue; + } + self.task_nodes[&task_id] + }, + TaskDestination::DocumentRoot(_) => NodeId::root(), + }; + let mut binding = self.doc_handle.get_mut(); if let Some(mut node) = binding.detach_node(node_id) { if let Some(element) = node.get_element_data_mut() { @@ -134,15 +163,40 @@ impl DocumentTaskQueue { } self.pending_tasks.clear(); + self.task_nodes.clear(); errors } /// Insert a node into the document - fn insert_node(&mut self, node_id: NodeId, mut node: C::Node, parent_id: NodeId, position: Option) { + fn insert_node(&mut self, node: C::Node, destination: TaskDestination, task_id: TaskId) -> Result { let mut binding = self.doc_handle.get_mut(); - node.register(node_id); - binding.register_node_at(node, parent_id, position); + match destination { + TaskDestination::Node(parent_id, position) => { + let node_id = binding.register_node_at(node, parent_id, position); + + self.task_nodes.insert(task_id, node_id); + + Ok(node_id) + } + TaskDestination::Task(dest_task_id, position) => { + if ! self.task_nodes.contains_key(&dest_task_id) { + return Err(anyhow::anyhow!("Task id {:?} not found", dest_task_id)); + } + let parent_node_id = self.task_nodes[&dest_task_id]; + let node_id = binding.register_node_at(node, parent_node_id, position); + + self.task_nodes.insert(task_id, node_id); + + Ok(node_id) + } + TaskDestination::DocumentRoot(position) => { + let node_id = NodeId::root(); + binding.register_node_at(node, node_id, position); + self.task_nodes.insert(task_id, node_id); + Ok(node_id) + } + } } } \ No newline at end of file diff --git a/crates/gosub_html5/src/html5parser.rs b/crates/gosub_html5/src/html5parser.rs index 2ce407b..63bb56e 100644 --- a/crates/gosub_html5/src/html5parser.rs +++ b/crates/gosub_html5/src/html5parser.rs @@ -5,7 +5,7 @@ use gosub_shared::traits::css_system::{CssParser, HasCssParser}; use gosub_shared::traits::document::Document; use gosub_shared::traits::document::HasDocument; use gosub_shared::traits::html5_parser::HtmlParser; -use crate::document::task_queue::{DocumentTask, DocumentTaskQueue}; +use gosub_shared::document::task_queue::{DocumentTask, DocumentTaskQueue}; /// The HTML parser implementation will parse an input string (or stream reader) and generate a /// DOM tree based on the input. Instead of generating the tree directly, it will use the Document @@ -132,6 +132,7 @@ impl HtmlParser for MyHtmlParser { println!("Parse Error: {}", error); } } + */ // We also mimic some CSS style parsing here. let mut parser = C::CssParser::new(); diff --git a/crates/gosub_shared/Cargo.toml b/crates/gosub_shared/Cargo.toml index 7f091d2..5d01669 100644 --- a/crates/gosub_shared/Cargo.toml +++ b/crates/gosub_shared/Cargo.toml @@ -18,10 +18,12 @@ encoding_rs = "0.8.34" derive_more = "0.99" + [target.'cfg(target_arch = "wasm32")'.dependencies] js-sys = "0.3.70" getrandom = { version = "0.2.15", features = ["js"] } web-sys = { version = "0.3.70", features = ["Performance", "Window"] } [dev-dependencies] -wasm-bindgen-test = "0.3.43" \ No newline at end of file +wasm-bindgen-test = "0.3.43" +mockall = "0.13.0" \ No newline at end of file diff --git a/crates/gosub_shared/src/document.rs b/crates/gosub_shared/src/document.rs index 1aa70dc..25e6aa9 100644 --- a/crates/gosub_shared/src/document.rs +++ b/crates/gosub_shared/src/document.rs @@ -1,11 +1,23 @@ use crate::traits::document::Document; use crate::traits::document::HasDocument; use std::cell::{Ref, RefCell, RefMut}; +use std::fmt::{Debug, Formatter}; use std::rc::Rc; -#[derive(Debug)] pub struct DocumentHandle(pub Rc>); +impl Debug for DocumentHandle +where + C::Document: Debug +{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("DocumentHandle") + .field(&self.0) + .finish() + } +} + + impl DocumentHandle { /// Creates a new document handle based on the given document. The document itself can be /// referenced hereafter with the `get` or `get_mut()` methods. Note that the document handle @@ -42,4 +54,35 @@ impl PartialEq for DocumentHandle { fn eq(&self, other: &Self) -> bool { Rc::ptr_eq(&self.0, &other.0) } +} + +#[cfg(test)] +mod tests { + use crate::mock::{MockConfig, MockDocument}; + use super::*; + + #[test] + fn test_dochandle() { + let mock_doc = MockDocument { + content: "Hello".into(), + handle: None, + }; + let mut doc_handle: DocumentHandle = DocumentHandle::new(mock_doc); + assert_eq!(doc_handle.get().content, "Hello"); + + doc_handle.get_mut().content = "World".into(); + assert_eq!(doc_handle.get().content, "World"); + + let cloned_handle = doc_handle.clone(); + assert_eq!(cloned_handle.get().content, "World"); + assert_eq!(doc_handle, cloned_handle); + + let mock_doc = MockDocument { + content: "World".into(), + handle: None, + }; + let doc2_handle = DocumentHandle::new(mock_doc); + + assert_ne!(doc_handle, doc2_handle); + } } \ No newline at end of file diff --git a/crates/gosub_shared/src/lib.rs b/crates/gosub_shared/src/lib.rs index 2b42a17..af07c22 100644 --- a/crates/gosub_shared/src/lib.rs +++ b/crates/gosub_shared/src/lib.rs @@ -1,4 +1,5 @@ pub mod document; +pub mod location; pub mod node_id; pub mod traits; -pub mod location; +mod mock; diff --git a/crates/gosub_shared/src/location.rs b/crates/gosub_shared/src/location.rs index 5531078..212accf 100644 --- a/crates/gosub_shared/src/location.rs +++ b/crates/gosub_shared/src/location.rs @@ -1,5 +1,3 @@ -/// A location defines a particular offset in a source code document by line-number and offset on -/// that line. It's primary use is to provide a location for errors and warnings. #[derive(Clone, Debug, PartialEq)] pub struct Location { line: usize, diff --git a/crates/gosub_shared/src/mock.rs b/crates/gosub_shared/src/mock.rs new file mode 100644 index 0000000..bcd136d --- /dev/null +++ b/crates/gosub_shared/src/mock.rs @@ -0,0 +1,534 @@ +use std::collections::HashMap; +use std::fmt::{Debug, Formatter}; +use anyhow::Error; +use crate::document::DocumentHandle; +use crate::node_id::NodeId; +use crate::traits::css_system::{CssDeclaration, CssRule, CssValue, HasCssSystem}; +use crate::traits::document::{Document, HasDocument}; +use crate::traits::document::query::{Condition, Query, SearchType}; +use crate::traits::node::{CommentData, DocTypeData, DocumentData, ElementData, Node, NodeData, TextData}; + +pub struct MockDocument { + pub(crate) content: String, + pub(crate) handle: Option>, +} + + +impl> Debug for MockDocument +where +{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MockDocument") + .field("content", &self.content) + .field("handle", &self.handle.is_some()) + .finish() + } +} + +pub struct Mock; + +pub struct MockNode; + +pub struct MockNodeData; + +pub struct MockQuery; + +#[derive(PartialEq)] +pub enum MockSearchType { +} + +impl SearchType for MockSearchType { + fn uninitialized() -> Self { + todo!() + } + + fn find_first() -> Self { + todo!() + } + + fn find_all() -> Self { + todo!() + } +} + +pub enum MockCondition { +} + +impl Condition for MockCondition { + fn equals_tag(_tag_name: &str) -> Self { + todo!() + } + + fn equals_id(_id: &str) -> Self { + todo!() + } + + fn contains_class(_class: &str) -> Self { + todo!() + } + + fn contains_attribute(_attribute: &str) -> Self { + todo!() + } + + fn contains_child_tag(_child_tag: &str) -> Self { + todo!() + } + + fn has_parent_tag(_parent_tag: &str) -> Self { + todo!() + } +} + +impl Query for MockQuery { + type SearchType = MockSearchType; + type Condition = MockCondition; + + fn new(_search_type: Self::SearchType, _conditions: Vec) -> Self { + todo!() + } + + fn search_type(&self) -> Self::SearchType { + todo!() + } + + fn conditions(&self) -> Vec { + todo!() + } +} + +impl NodeData for MockNodeData { + // type Node = MockNode; + // type Element = MockElementData; + // type Text = MockTextData; + // type Comment = MockCommentData; + // type DocType = MockDocTypeData; + // type Document = MockDocumentData; +} + +impl From for MockNodeData { + fn from(_data: MockDocumentData) -> Self { + todo!() + } +} +impl From for MockNodeData { + fn from(_data: MockElementData) -> Self { + todo!() + } +} +impl From for MockNodeData { + fn from(_data: MockTextData) -> Self { + todo!() + } +} +impl From for MockNodeData { + fn from(_data: MockCommentData) -> Self { + todo!() + } +} +impl From for MockNodeData { + fn from(_data: MockDocTypeData) -> Self { + todo!() + } +} + +pub struct MockElementData; + +impl ElementData for MockElementData { + fn new(_name: &str, _namespace: &str) -> Self { + todo!() + } + + fn name(&self) -> &str { + todo!() + } + + fn namespace(&self) -> &str { + todo!() + } + + fn attributes(&self) -> &HashMap { + todo!() + } + + fn add_attribute(&mut self, _name: &str, _value: &str) { + todo!() + } + + fn remove_attribute(&mut self, _name: &str) { + todo!() + } + + fn classes(&self) -> &HashMap { + todo!() + } + + fn active_classes(&self) -> Vec { + todo!() + } + + fn add_class(&mut self, _name: &str, _active: bool) { + todo!() + } + + fn remove_class(&mut self, _name: &str) { + todo!() + } + + fn set_class_state(&mut self, _name: &str, _active: bool) { + todo!() + } +} + +pub struct MockTextData; + +impl TextData for MockTextData { + fn new(_content: &str) -> Self { + todo!() + } + + fn content(&self) -> &str { + todo!() + } +} + +pub struct MockCommentData; + +impl CommentData for MockCommentData { + fn new(_content: &str) -> Self { + todo!() + } + + fn content(&self) -> &str { + todo!() + } +} + +pub struct MockDocTypeData; + +impl DocTypeData for MockDocTypeData { + fn new(_name: &str, _public_id: &str, _system_id: &str) -> Self { + todo!() + } + + fn name(&self) -> &str { + todo!() + } + + fn public_id(&self) -> &str { + todo!() + } + + fn system_id(&self) -> &str { + todo!() + } +} + +pub struct MockDocumentData; + +impl DocumentData for MockDocumentData { + fn new() -> Self { + todo!() + } +} + +impl Node for MockNode { + type NodeData = MockNodeData; + type ElementData = MockElementData; + type TextData = MockTextData; + type CommentData = MockCommentData; + type DocTypeData = MockDocTypeData; + type DocumentData = MockDocumentData; + + fn new(_data: Self::NodeData) -> Self { + todo!() + } + + fn id(&self) -> Option { + todo!() + } + + fn is_registered(&self) -> bool { + todo!() + } + + fn register(&mut self, _node_id: NodeId) { + todo!() + } + + fn children(&self) -> &Vec { + todo!() + } + + fn is_renderable(&self) -> bool { + todo!() + } + + fn add_child_at_position(&mut self, _node_id: NodeId, _position: Option) { + todo!() + } + + fn get_element_data_mut(&mut self) -> Option<&mut Self::ElementData> { + todo!() + } + + fn get_element_data(&self) -> Option<&Self::ElementData> { + todo!() + } + + fn get_text_data(&self) -> Option<&Self::TextData> { + todo!() + } + + fn get_comment_data(&self) -> Option<&Self::CommentData> { + todo!() + } + + fn get_doctype_data(&self) -> Option<&Self::DocTypeData> { + todo!() + } + + fn get_document_data(&self) -> Option<&Self::DocumentData> { + todo!() + } +} + +pub struct MockCssStylesheet; + +pub struct MockCssRule; + +impl HasCssSystem for MockCssRule { + type CssStylesheet = MockCssStylesheet; + type CssRule = MockCssRule; + type CssDeclaration = MockCssDeclaration; + type CssValue = MockCssValue; +} + +impl Debug for MockCssRule { + fn fmt(&self, _f: &mut Formatter<'_>) -> std::fmt::Result { + todo!() + } +} + +impl CssRule for MockCssRule { + fn new() -> Self { + todo!() + } + + fn add_selector(&mut self, _selector: &str) { + todo!() + } + + fn add_declaration(&mut self, _declaration: Self::CssDeclaration) { + todo!() + } + + fn selectors(&self) -> &Vec { + todo!() + } + + fn declarations(&self) -> &Vec { + todo!() + } +} + +pub struct MockCssDeclaration; + +impl HasCssSystem for MockCssDeclaration { + type CssStylesheet = MockCssStylesheet; + type CssRule = MockCssRule; + type CssDeclaration = MockCssDeclaration; + type CssValue = MockCssValue; +} + +impl Debug for MockCssDeclaration { + fn fmt(&self, _f: &mut Formatter<'_>) -> std::fmt::Result { + todo!() + } +} + +impl CssDeclaration for MockCssDeclaration { + fn new(_property: &str, _value: Self::CssValue, _important: bool) -> Self { + todo!() + } + + fn name(&self) -> &str { + todo!() + } + + fn value(&self) -> MockCssValue { + todo!() + } + + fn important(&self) -> bool { + todo!() + } +} + +pub struct MockCssValue; + +impl Debug for MockCssValue { + fn fmt(&self, _f: &mut Formatter<'_>) -> std::fmt::Result { + todo!() + } +} + +impl CssValue for MockCssValue { + fn unit(_value: f32, _unit: &str) -> Self { + todo!() + } + + fn keyword(_value: &str) -> Self { + todo!() + } + + fn colorvalue(_value: &str) -> Self { + todo!() + } + + fn list(_args: Vec) -> Self { + todo!() + } + + fn is_unit(&self) -> bool { + todo!() + } + + fn is_keyword(&self) -> bool { + todo!() + } + + fn is_color(&self) -> bool { + todo!() + } + + fn is_list(&self) -> bool { + todo!() + } +} + +impl HasCssSystem for MockCssStylesheet { + type CssStylesheet = MockCssStylesheet; + type CssRule = MockCssRule; + type CssDeclaration = MockCssDeclaration; + type CssValue = MockCssValue; +} + +impl Debug for MockCssStylesheet { + fn fmt(&self, _f: &mut Formatter<'_>) -> std::fmt::Result { + todo!() + } +} + +impl crate::traits::css_system::CssStylesheet for MockCssStylesheet { + fn new() -> Self { + MockCssStylesheet + } + + fn add_rule(&mut self, _rule: MockCssRule) { + todo!() + } + + fn rules(&self) -> &Vec { + todo!() + } +} + +impl HasCssSystem for Mock { + type CssStylesheet = MockCssStylesheet; + type CssRule = MockCssRule; + type CssDeclaration = MockCssDeclaration; + type CssValue = MockCssValue; +} + +// impl HasDocument for Mock { +// } + +impl HasCssSystem for MockDocument { + type CssStylesheet = MockCssStylesheet; + type CssRule = MockCssRule; + type CssDeclaration = MockCssDeclaration; + type CssValue = MockCssValue; +} + + +impl Document for MockDocument { + type Node = MockNode; + type Query = MockQuery; + type Document = Self; + + fn new(_url: &str) -> Self { + todo!() + } + + fn register_node_at(&mut self, _node: Self::Node, _parent_id: NodeId, _position: Option) -> NodeId { + todo!() + } + + fn get_handle(&self) -> DocumentHandle { + self.handle.clone().unwrap() + } + + fn set_handle(&mut self, handle: DocumentHandle) { + self.handle = Some(handle); + } + + fn get_root_node(&self) -> Option<&Self::Node> { + todo!() + } + + fn get_node(&self, _id: NodeId) -> Option<&Self::Node> { + todo!() + } + + fn stylesheets(&self) -> &Vec { + todo!() + } + + fn add_stylesheet(&mut self, _stylesheet: C::CssStylesheet) { + todo!() + } + + fn detach_node(&mut self, _id: NodeId) -> Option { + todo!() + } + + fn update_node(&mut self, _id: NodeId, _node: Self::Node) { + todo!() + } + + fn get_url(&self) -> &str { + todo!() + } + + fn get_node_mut(&mut self, _id: NodeId) -> Option<&mut Self::Node> { + todo!() + } + + fn get_node_clone(&self, _id: NodeId) -> Option { + todo!() + } + + fn get_node_by_element_id(&self, _name: &str) -> Option { + todo!() + } + + fn query(&self, _query: &Self::Query) -> Result, Error> { + todo!() + } +} + + +pub struct MockConfig; + +impl HasCssSystem for MockConfig { + type CssStylesheet = MockCssStylesheet; + type CssRule = MockCssRule; + type CssDeclaration = MockCssDeclaration; + type CssValue = MockCssValue; +} + +impl HasDocument for MockConfig { + type Document = MockDocument; + type Node = MockNode; +} diff --git a/crates/gosub_shared/src/node_id.rs b/crates/gosub_shared/src/node_id.rs index d0fdd16..e401679 100644 --- a/crates/gosub_shared/src/node_id.rs +++ b/crates/gosub_shared/src/node_id.rs @@ -1,8 +1,6 @@ use std::fmt; use std::fmt::{Display, Formatter}; -/// A node ID is an identifier for nodes found inside a Document. Note that by convention, node ID 0 -/// references the root of a document / tree structure. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct NodeId { id: u32, diff --git a/docs/diag.md b/docs/diag.md index 60ba63d..60620b7 100644 --- a/docs/diag.md +++ b/docs/diag.md @@ -1,12 +1,3 @@ -# Trait diagrams - -This document contains the trait diagrams for the engine. The traits are grouped by their functionality and how they -are linked to each other. These traits form the boundaries of the engine and are used to separate the different -components. - -In other words, it should be possible to implement a different system for Nodes without changing anything else and it -would still function correctly. The same goes for the CSS system, the query system, and the rendering system. - ```mermaid --- title: Gosub Engine Traits From 8bf342a2e7f464d7ad9416c76e5894de220d10e9 Mon Sep 17 00:00:00 2001 From: Joshua Thijssen Date: Tue, 19 Nov 2024 20:49:03 +0100 Subject: [PATCH 2/2] trying to merge stuff --- crates/gosub_html5/src/html5parser.rs | 100 ++++++++++-------- crates/gosub_html5/src/node/arena.rs | 4 +- .../gosub_html5/src/node/node_data/element.rs | 2 +- crates/gosub_shared/src/mock.rs | 4 + 4 files changed, 62 insertions(+), 48 deletions(-) diff --git a/crates/gosub_html5/src/html5parser.rs b/crates/gosub_html5/src/html5parser.rs index 63bb56e..99998a4 100644 --- a/crates/gosub_html5/src/html5parser.rs +++ b/crates/gosub_html5/src/html5parser.rs @@ -1,16 +1,10 @@ use gosub_shared::document::DocumentHandle; use gosub_shared::location::Location; -use gosub_shared::node_id::NodeId; use gosub_shared::traits::css_system::{CssParser, HasCssParser}; use gosub_shared::traits::document::Document; use gosub_shared::traits::document::HasDocument; use gosub_shared::traits::html5_parser::HtmlParser; -use gosub_shared::document::task_queue::{DocumentTask, DocumentTaskQueue}; - -/// The HTML parser implementation will parse an input string (or stream reader) and generate a -/// DOM tree based on the input. Instead of generating the tree directly, it will use the Document -/// Task Queue to store tasks that will be executed during a flush of the queue. This will basically -/// defer the actual creation until a later time. +use crate::document::task_queue::{DocumentTask, DocumentTaskQueue, TaskDestination}; pub struct MyHtmlParser { doc_handle: DocumentHandle, @@ -43,88 +37,66 @@ impl HtmlParser for MyHtmlParser { let mut task_queue = DocumentTaskQueue::new(self.doc_handle.clone()); - let node_id_1 = task_queue.add_task(DocumentTask::CreateElement { + let tid_1 = task_queue.add_task(DocumentTask::CreateElement { name: "html".to_string(), namespace: "html".to_string(), location: Location::default(), - parent_id: NodeId::root(), - position: None - }); + }, TaskDestination::DocumentRoot(None)); let _ = task_queue.add_task(DocumentTask::CreateElement { name: "head".to_string(), namespace: "html".to_string(), location: Default::default(), - parent_id: node_id_1, - position: None - }); + }, TaskDestination::Task(tid_1, None)); - let node_id_3 = task_queue.add_task(DocumentTask::CreateElement { + let tid_3 = task_queue.add_task(DocumentTask::CreateElement { name: "body".to_string(), namespace: "html".to_string(), location: Default::default(), - parent_id: node_id_1, - position: None - }); + }, TaskDestination::Task(tid_1, None)); - let node_id_4_1 = task_queue.add_task(DocumentTask::CreateElement { + let tid_41 = task_queue.add_task(DocumentTask::CreateElement { name: "h1".to_string(), namespace: "html".to_string(), location: Default::default(), - parent_id: node_id_3, - position: None - }); + }, TaskDestination::Task(tid_3, None)); let _ = task_queue.add_task(DocumentTask::CreateText { content: "This is a header".to_string(), location: Default::default(), - parent_id: node_id_4_1, - position: None - }); + }, TaskDestination::Task(tid_41, None)); - let node_id_4 = task_queue.add_task(DocumentTask::CreateElement { + let tid_4 = task_queue.add_task(DocumentTask::CreateElement { name: "p".to_string(), namespace: "html".to_string(), location: Default::default(), - parent_id: node_id_3, - position: None - }); + }, TaskDestination::Task(tid_3, None)); let _ = task_queue.add_task(DocumentTask::CreateText { content: "hello world!".to_string(), location: Default::default(), - parent_id: node_id_4, - position: None - }); + }, TaskDestination::Task(tid_4, None)); let _ = task_queue.add_task(DocumentTask::CreateText { content: "prefix".to_string(), location: Default::default(), - parent_id: node_id_1, - position: Some(0) - }); + }, TaskDestination::Task(tid_4, Some(0))); let _ = task_queue.add_task(DocumentTask::InsertAttribute { key: "class".to_string(), value: "a b c".to_string(), location: Default::default(), - node_id: node_id_3, - }); - + }, TaskDestination::Task(tid_3, None)); let _ = task_queue.add_task(DocumentTask::InsertAttribute { key: "id".to_string(), value: "myid".to_string(), location: Default::default(), - node_id: node_id_3, - }); - + }, TaskDestination::Task(tid_3, None)); let _ = task_queue.add_task(DocumentTask::InsertAttribute { key: "foo".to_string(), value: "bar".to_string(), location: Default::default(), - node_id: node_id_3, - }); - + }, TaskDestination::Task(tid_3, None)); let errors = task_queue.flush(); if !errors.is_empty() { @@ -132,7 +104,45 @@ impl HtmlParser for MyHtmlParser { println!("Parse Error: {}", error); } } - */ + + /* + #[allow(type_alias_bounds)] + type BuilderType = NodeBuilder; + + let node1 = BuilderType::::new_element_node("html", "html"); + let node1_id = binding.register_node_at(node1, NodeId::root(), None); + + let node2 = BuilderType::::new_element_node("head", "html"); + let _node2_id = binding.register_node_at(node2, node1_id, None); + + let node3 = BuilderType::::new_element_node("body", "html"); + let node3_id = binding.register_node_at(node3, node1_id, None); + + let node41 = BuilderType::::new_element_node("h1", "html"); + let node41_id = binding.register_node_at(node41, node3_id, None); + + let node42 = BuilderType::::new_text_node("This is a header"); + let _node42_id = binding.register_node_at(node42, node41_id, None); + + let node4 = BuilderType::::new_element_node("p", "html"); + let node4_id = binding.register_node_at(node4, node3_id, None); + + let node5 = BuilderType::::new_text_node("hello world!"); + let _node5_id = binding.register_node_at(node5, node4_id, None); + + // Add some attributes to the P element + if let Some(mut node) = binding.detach_node(node4_id) { + // Get the mutable data and add some attributes + if let Some(data) = node.get_element_data_mut() { + data.add_attribute("class", "a b c"); + data.add_attribute("id", "myid"); + data.add_attribute("foo", "bar"); + } + + // Finally, reattach the node back into the document/arena + binding.update_node(node4_id, node); + } + */ // We also mimic some CSS style parsing here. let mut parser = C::CssParser::new(); diff --git a/crates/gosub_html5/src/node/arena.rs b/crates/gosub_html5/src/node/arena.rs index 05de71d..6ec3481 100644 --- a/crates/gosub_html5/src/node/arena.rs +++ b/crates/gosub_html5/src/node/arena.rs @@ -25,8 +25,8 @@ impl NodeArena { } pub fn add_node(&mut self, mut node: N) -> NodeId { - /// If a node is already registered, we should use that node-id instead of generating a new - /// one. + // If a node is already registered, we should use that node-id instead of generating a new + // one. let node_id = if node.id().is_some() { node.id().unwrap() } else { diff --git a/crates/gosub_html5/src/node/node_data/element.rs b/crates/gosub_html5/src/node/node_data/element.rs index 0c3085f..305ba90 100644 --- a/crates/gosub_html5/src/node/node_data/element.rs +++ b/crates/gosub_html5/src/node/node_data/element.rs @@ -65,7 +65,7 @@ impl gosub_shared::traits::node::ElementData for ElementData { fn add_attribute(&mut self, name: &str, value: &str) { self.attributes.insert(name.into(), value.into()); - /// When we add a "class" attribute, we actually store it in the classes map as well. + // When we add a "class" attribute, we actually store it in the classes map as well. if name == "class" { for class in value.split_whitespace() { self.classes.insert(class.to_string(), true); diff --git a/crates/gosub_shared/src/mock.rs b/crates/gosub_shared/src/mock.rs index bcd136d..3c6c35c 100644 --- a/crates/gosub_shared/src/mock.rs +++ b/crates/gosub_shared/src/mock.rs @@ -465,6 +465,10 @@ impl Document for MockDocument { todo!() } + fn get_next_node_id(&mut self) -> NodeId { + todo!() + } + fn get_handle(&self) -> DocumentHandle { self.handle.clone().unwrap() }