From d3f0c5158184df5804371f8929105f90e00d4981 Mon Sep 17 00:00:00 2001 From: Joshua Thijssen Date: Sun, 29 Sep 2024 16:40:27 +0200 Subject: [PATCH 1/2] Refactored crates around and added generics This is a rather large commit that was needed in order to let the gosub_html5 and gosub_css3 crates to be dependend on eachother. The problem was that they both need eachother and resulted in a cyclic dependency. We solve this usually by moving stuff to the gosub_shared crate, but in this case it would mean we practically move EVERYTHING to that crate. So instead we created a lot of traits in the gosub_shared that can be used by the other crates. This way, both html5 and css3 crates do not need to be depenended on eachother, but only on the gosub_shared crate. However, it crates a lot of extra efford in getting this up and running, the traits and generics means that A LOT of code must be refactored into this system, during which we found out that a lot of code is not on the correct places. We moved around much of the code so it kinda makes sense again. This resulted in the removal of the gosub_styling crate, which was a sort of bridge between the css3-system and other things, which now lives in the css3 crate (which we now properly could manage). There are still problems we're facing, but overal the refactoring looks good. Note that creating this trait setup resulted in redesigning a new engine architecture (unimaginably named "engine_v2"), in which we are redesigning the way we deal with all components. It should resolve all the trait bottlenecks we currently have and hopefully we will be able to refactor the current engine into that one. For now, we keep on working on this system. Co-authored-by: Shark --- .gitignore | 2 - CONTRIBUTING.md | 4 - Cargo.lock | 808 ++---- Cargo.toml | 1 - benches/tree_iterator.rs | 21 +- crates/gosub_bindings/.gitignore | 3 - crates/gosub_bindings/Cargo.toml | 14 - crates/gosub_bindings/Makefile | 54 - crates/gosub_bindings/README.md | 18 - crates/gosub_bindings/include/gosub_api.h | 37 - crates/gosub_bindings/include/nodes.h | 42 - crates/gosub_bindings/include/nodes/text.h | 25 - crates/gosub_bindings/include/properties.h | 16 - crates/gosub_bindings/src/gosub_api.c | 50 - crates/gosub_bindings/src/lib.rs | 118 - crates/gosub_bindings/src/nodes.c | 67 - crates/gosub_bindings/src/nodes/text.c | 38 - crates/gosub_bindings/src/wrapper.rs | 9 - crates/gosub_bindings/src/wrapper/node.rs | 47 - crates/gosub_bindings/src/wrapper/text.rs | 28 - crates/gosub_bindings/tests/rendertree_test.c | 121 - crates/gosub_config/Cargo.toml | 3 + crates/gosub_config/src/lib.rs | 17 +- crates/gosub_config/src/settings.rs | 8 +- crates/gosub_config/src/storage/json.rs | 6 +- crates/gosub_config/src/storage/sqlite.rs | 4 +- crates/gosub_css3/Cargo.toml | 12 +- .../resources/definitions/definitions.json | 0 .../definitions/definitions_at-rules.json | 0 .../definitions/definitions_properties.json | 0 .../definitions/definitions_selectors.json | 0 .../definitions/definitions_values.json | 0 .../resources/useragent.css | 0 .../src/{convert/ast_converter.rs => ast.rs} | 89 +- crates/gosub_css3/src/colors.rs | 21 +- crates/gosub_css3/src/convert.rs | 1 - crates/gosub_css3/src/errors.rs | 35 + crates/gosub_css3/src/functions.rs | 7 + crates/gosub_css3/src/functions/attr.rs | 33 + .../src/functions/calc.rs | 3 +- .../src/functions/var.rs | 13 +- crates/gosub_css3/src/lib.rs | 146 +- crates/gosub_css3/src/logging.rs | 528 ++++ crates/gosub_css3/src/matcher.rs | 6 + .../src/matcher}/property_definitions.rs | 94 +- .../src/matcher}/shorthands.rs | 69 +- .../src => gosub_css3/src/matcher}/styling.rs | 225 +- .../src => gosub_css3/src/matcher}/syntax.rs | 230 +- .../src/matcher}/syntax_matcher.rs | 207 +- crates/gosub_css3/src/matcher/walker.rs | 48 + crates/gosub_css3/src/node.rs | 16 +- crates/gosub_css3/src/parser.rs | 65 +- crates/gosub_css3/src/parser/anplusb.rs | 64 +- crates/gosub_css3/src/parser/at_rule.rs | 34 +- .../src/parser/at_rule/container.rs | 12 +- .../src/parser/at_rule/font_face.rs | 5 +- .../gosub_css3/src/parser/at_rule/import.rs | 19 +- crates/gosub_css3/src/parser/at_rule/layer.rs | 9 +- crates/gosub_css3/src/parser/at_rule/media.rs | 52 +- crates/gosub_css3/src/parser/at_rule/nest.rs | 5 +- crates/gosub_css3/src/parser/at_rule/page.rs | 5 +- crates/gosub_css3/src/parser/at_rule/scope.rs | 5 +- .../src/parser/at_rule/starting_style.rs | 5 +- .../gosub_css3/src/parser/at_rule/supports.rs | 8 +- crates/gosub_css3/src/parser/block.rs | 19 +- crates/gosub_css3/src/parser/calc.rs | 7 +- crates/gosub_css3/src/parser/combinator.rs | 18 +- crates/gosub_css3/src/parser/condition.rs | 8 +- crates/gosub_css3/src/parser/declaration.rs | 18 +- .../gosub_css3/src/parser/feature_function.rs | 10 +- crates/gosub_css3/src/parser/function.rs | 7 +- crates/gosub_css3/src/parser/operator.rs | 10 +- crates/gosub_css3/src/parser/pseudo.rs | 30 +- crates/gosub_css3/src/parser/rule.rs | 10 +- crates/gosub_css3/src/parser/selector.rs | 72 +- crates/gosub_css3/src/parser/selector_list.rs | 5 +- crates/gosub_css3/src/parser/stylesheet.rs | 14 +- crates/gosub_css3/src/parser/url.rs | 21 +- crates/gosub_css3/src/parser/value.rs | 34 +- crates/gosub_css3/src/stylesheet.rs | 195 +- crates/gosub_css3/src/system.rs | 244 ++ crates/gosub_css3/src/tokenizer.rs | 168 +- crates/gosub_css3/src/walker.rs | 11 +- .../tools/generate_definitions/go.mod | 0 .../tools/generate_definitions/main.go | 2 +- .../tools/generate_definitions/mdn/mdn.go | 0 .../patch/patch_darwin.go | 0 .../generate_definitions/patch/patch_linux.go | 0 .../patch/patch_windows.go | 0 .../generate_definitions/patch/patcher.go | 0 .../tools/generate_definitions/specs/specs.go | 0 .../utils/stringmaybearray.go | 0 .../tools/generate_definitions/utils/types.go | 0 .../tools/generate_definitions/utils/utils.go | 4 +- .../generate_definitions/webref/webref.go | 0 crates/gosub_html5/Cargo.toml | 2 - .../gosub_html5/benches/tree_construction.rs | 11 +- crates/gosub_html5/docs/parsing.md | 6 +- crates/gosub_html5/src/document.rs | 5 + crates/gosub_html5/src/document/builder.rs | 55 + .../gosub_html5/src/document/document_impl.rs | 2277 ++++++++++++++++ crates/gosub_html5/src/document/fragment.rs | 63 + crates/gosub_html5/src/document/query.rs | 125 + crates/gosub_html5/src/document/task_queue.rs | 235 ++ crates/gosub_html5/src/element_class.rs | 159 -- crates/gosub_html5/src/lib.rs | 22 +- crates/gosub_html5/src/node.rs | 732 +----- crates/gosub_html5/src/node/arena.rs | 208 +- crates/gosub_html5/src/node/data/comment.rs | 12 +- crates/gosub_html5/src/node/data/doctype.rs | 17 +- crates/gosub_html5/src/node/data/document.rs | 23 +- crates/gosub_html5/src/node/data/element.rs | 418 ++- crates/gosub_html5/src/node/data/text.rs | 23 +- crates/gosub_html5/src/node/elements.rs | 97 + crates/gosub_html5/src/node/node_impl.rs | 520 ++++ crates/gosub_html5/src/node/visitor.rs | 18 + crates/gosub_html5/src/parser.rs | 1135 ++++---- crates/gosub_html5/src/parser/document.rs | 2280 ----------------- .../src/{error_logger.rs => parser/errors.rs} | 42 +- crates/gosub_html5/src/parser/helper.rs | 377 ++- crates/gosub_html5/src/parser/query.rs | 35 +- crates/gosub_html5/src/parser/quirks.rs | 36 +- crates/gosub_html5/src/parser/tree_builder.rs | 41 +- crates/gosub_html5/src/tokenizer.rs | 158 +- .../src/tokenizer/character_reference.rs | 99 +- .../src/tokenizer/replacement_tables.rs | 2 +- .../gosub_html5/src/tokenizer/test_cases.rs | 5 +- crates/gosub_html5/src/tokenizer/token.rs | 26 +- crates/gosub_html5/src/util.rs | 5 - crates/gosub_html5/src/visit.rs | 22 - crates/gosub_html5/src/writer.rs | 178 +- .../tree-construction/tests19.dat | 1126 -------- crates/gosub_jsapi/src/console.rs | 6 +- .../src/console/writable_printer.rs | 21 +- crates/gosub_net/Cargo.toml | 2 +- crates/gosub_net/src/dns.rs | 6 +- crates/gosub_net/src/dns/cache.rs | 20 +- crates/gosub_net/src/dns/local.rs | 36 +- crates/gosub_net/src/dns/remote.rs | 8 +- crates/gosub_net/src/http/response.rs | 4 +- crates/gosub_render_backend/Cargo.toml | 11 +- crates/gosub_render_backend/src/layout.rs | 51 +- crates/gosub_render_backend/src/lib.rs | 17 +- crates/gosub_render_backend/src/svg.rs | 17 +- crates/gosub_render_utils/Cargo.toml | 3 +- crates/gosub_render_utils/src/lib.rs | 1 + crates/gosub_render_utils/src/position.rs | 38 +- crates/gosub_render_utils/src/render_tree.rs | 843 ++++-- .../src/render_tree/desc.rs | 37 +- .../src/render_tree/properties.rs | 91 - .../src/render_tree/text.rs | 50 - .../src/render_tree/util.rs | 14 - crates/gosub_render_utils/src/style/parse.rs | 3 +- crates/gosub_renderer/Cargo.toml | 5 - crates/gosub_renderer/src/debug/scale.rs | 4 +- crates/gosub_renderer/src/draw.rs | 317 +-- crates/gosub_renderer/src/position.rs | 174 ++ crates/gosub_renderer/src/render_tree.rs | 64 +- crates/gosub_shared/Cargo.toml | 1 + crates/gosub_shared/src/byte_stream.rs | 60 +- crates/gosub_shared/src/document.rs | 59 + crates/gosub_shared/src/element_data.rs | 80 + crates/gosub_shared/src/errors.rs | 39 + crates/gosub_shared/src/lib.rs | 7 + crates/gosub_shared/src/node.rs | 80 + crates/gosub_shared/src/timing.rs | 50 +- .../src/traits.rs} | 16 +- crates/gosub_shared/src/traits/css3.rs | 101 + crates/gosub_shared/src/traits/document.rs | 129 + crates/gosub_shared/src/traits/html5.rs | 31 + crates/gosub_shared/src/traits/node.rs | 196 ++ crates/gosub_shared/src/traits/render_tree.rs | 21 + crates/gosub_shared/src/types.rs | 19 +- crates/gosub_styling/Cargo.toml | 32 - crates/gosub_styling/README.md | 55 - crates/gosub_styling/src/errors.rs | 8 - crates/gosub_styling/src/functions.rs | 7 - crates/gosub_styling/src/functions/attr.rs | 26 - crates/gosub_styling/src/lib.rs | 34 - crates/gosub_styling/src/render_tree.rs | 1043 -------- crates/gosub_svg/Cargo.toml | 6 +- crates/gosub_svg/src/lib.rs | 16 +- crates/gosub_svg/src/resvg.rs | 34 +- crates/gosub_taffy/Cargo.toml | 3 +- crates/gosub_taffy/src/compute/inline.rs | 84 +- crates/gosub_taffy/src/lib.rs | 14 +- crates/gosub_taffy/src/style/parse.rs | 26 +- .../gosub_taffy/src/style/parse_properties.rs | 30 +- crates/gosub_testing/src/testing/tokenizer.rs | 49 +- .../src/testing/tree_construction.rs | 109 +- .../testing/tree_construction/generator.rs | 133 +- .../src/testing/tree_construction/parser.rs | 40 +- .../src/testing/tree_construction/result.rs | 9 +- crates/gosub_typeface/Cargo.toml | 2 + crates/gosub_useragent/Cargo.toml | 2 + crates/gosub_useragent/src/application.rs | 73 +- crates/gosub_useragent/src/event_loop.rs | 25 +- crates/gosub_useragent/src/tabs.rs | 59 +- crates/gosub_useragent/src/window.rs | 30 +- crates/gosub_v8/Cargo.toml | 2 - crates/gosub_v8/src/v8.rs | 10 +- crates/gosub_v8/src/v8/array.rs | 97 +- crates/gosub_v8/src/v8/context.rs | 31 +- crates/gosub_v8/src/v8/function.rs | 64 +- crates/gosub_v8/src/v8/object.rs | 63 +- crates/gosub_v8/src/v8/value.rs | 32 +- crates/gosub_vello/Cargo.toml | 2 + crates/gosub_vello/src/border.rs | 44 +- crates/gosub_vello/src/gradient.rs | 19 +- crates/gosub_vello/src/image.rs | 7 +- crates/gosub_vello/src/lib.rs | 22 +- crates/gosub_vello/src/rect.rs | 8 +- crates/gosub_vello/src/render.rs | 7 +- crates/gosub_vello/src/scene.rs | 12 +- crates/gosub_vello/src/text.rs | 6 +- crates/gosub_vello/src/vello_svg.rs | 12 +- crates/gosub_webexecutor/src/js/array.rs | 9 +- crates/gosub_webexecutor/src/js/function.rs | 27 +- crates/gosub_webexecutor/src/js/object.rs | 6 +- crates/gosub_webexecutor/src/js/runtime.rs | 5 +- crates/gosub_webexecutor/src/js/value.rs | 17 +- crates/gosub_webexecutor/tests/interop.rs | 49 +- crates/gosub_webinterop/Cargo.toml | 4 +- crates/gosub_webinterop/src/function.rs | 5 +- crates/gosub_webinterop/src/lib.rs | 15 +- crates/gosub_webinterop/src/property.rs | 5 +- crates/gosub_webinterop/src/types/args.rs | 22 +- crates/gosub_webinterop/src/types/generics.rs | 5 +- .../gosub_webinterop/src/types/primitive.rs | 4 +- crates/gosub_webinterop/src/types/ty.rs | 5 +- examples/html5-parser.rs | 13 +- rustfmt.toml | 1 + src/bin/config-store.rs | 7 +- src/bin/css3-parser.rs | 88 +- src/bin/display-text-tree.rs | 42 +- src/bin/gosub-parser.rs | 17 +- src/bin/html5-parser-test.rs | 18 +- src/bin/parser-test.rs | 21 +- src/bin/renderer.rs | 21 +- src/engine.rs | 32 +- src/wasm/css.rs | 10 +- src/wasm/renderer.rs | 7 +- src/wasm/styles.rs | 2 +- tests/example1.js | 2 +- 244 files changed, 9578 insertions(+), 11134 deletions(-) delete mode 100644 crates/gosub_bindings/.gitignore delete mode 100644 crates/gosub_bindings/Cargo.toml delete mode 100644 crates/gosub_bindings/Makefile delete mode 100644 crates/gosub_bindings/README.md delete mode 100644 crates/gosub_bindings/include/gosub_api.h delete mode 100644 crates/gosub_bindings/include/nodes.h delete mode 100644 crates/gosub_bindings/include/nodes/text.h delete mode 100644 crates/gosub_bindings/include/properties.h delete mode 100644 crates/gosub_bindings/src/gosub_api.c delete mode 100644 crates/gosub_bindings/src/lib.rs delete mode 100644 crates/gosub_bindings/src/nodes.c delete mode 100644 crates/gosub_bindings/src/nodes/text.c delete mode 100644 crates/gosub_bindings/src/wrapper.rs delete mode 100644 crates/gosub_bindings/src/wrapper/node.rs delete mode 100644 crates/gosub_bindings/src/wrapper/text.rs delete mode 100644 crates/gosub_bindings/tests/rendertree_test.c rename crates/{gosub_styling => gosub_css3}/resources/definitions/definitions.json (100%) rename crates/{gosub_styling => gosub_css3}/resources/definitions/definitions_at-rules.json (100%) rename crates/{gosub_styling => gosub_css3}/resources/definitions/definitions_properties.json (100%) rename crates/{gosub_styling => gosub_css3}/resources/definitions/definitions_selectors.json (100%) rename crates/{gosub_styling => gosub_css3}/resources/definitions/definitions_values.json (100%) rename crates/{gosub_styling => gosub_css3}/resources/useragent.css (100%) rename crates/gosub_css3/src/{convert/ast_converter.rs => ast.rs} (79%) delete mode 100644 crates/gosub_css3/src/convert.rs create mode 100644 crates/gosub_css3/src/errors.rs create mode 100644 crates/gosub_css3/src/functions.rs create mode 100644 crates/gosub_css3/src/functions/attr.rs rename crates/{gosub_styling => gosub_css3}/src/functions/calc.rs (58%) rename crates/{gosub_styling => gosub_css3}/src/functions/var.rs (74%) create mode 100644 crates/gosub_css3/src/logging.rs create mode 100644 crates/gosub_css3/src/matcher.rs rename crates/{gosub_styling/src => gosub_css3/src/matcher}/property_definitions.rs (92%) rename crates/{gosub_styling/src => gosub_css3/src/matcher}/shorthands.rs (92%) rename crates/{gosub_styling/src => gosub_css3/src/matcher}/styling.rs (78%) rename crates/{gosub_styling/src => gosub_css3/src/matcher}/syntax.rs (94%) rename crates/{gosub_styling/src => gosub_css3/src/matcher}/syntax_matcher.rs (90%) create mode 100644 crates/gosub_css3/src/matcher/walker.rs create mode 100644 crates/gosub_css3/src/system.rs rename crates/{gosub_styling => gosub_css3}/tools/generate_definitions/go.mod (100%) rename crates/{gosub_styling => gosub_css3}/tools/generate_definitions/main.go (98%) rename crates/{gosub_styling => gosub_css3}/tools/generate_definitions/mdn/mdn.go (100%) rename crates/{gosub_styling => gosub_css3}/tools/generate_definitions/patch/patch_darwin.go (100%) rename crates/{gosub_styling => gosub_css3}/tools/generate_definitions/patch/patch_linux.go (100%) rename crates/{gosub_styling => gosub_css3}/tools/generate_definitions/patch/patch_windows.go (100%) rename crates/{gosub_styling => gosub_css3}/tools/generate_definitions/patch/patcher.go (100%) rename crates/{gosub_styling => gosub_css3}/tools/generate_definitions/specs/specs.go (100%) rename crates/{gosub_styling => gosub_css3}/tools/generate_definitions/utils/stringmaybearray.go (100%) rename crates/{gosub_styling => gosub_css3}/tools/generate_definitions/utils/types.go (100%) rename crates/{gosub_styling => gosub_css3}/tools/generate_definitions/utils/utils.go (89%) rename crates/{gosub_styling => gosub_css3}/tools/generate_definitions/webref/webref.go (100%) create mode 100644 crates/gosub_html5/src/document.rs create mode 100644 crates/gosub_html5/src/document/builder.rs create mode 100755 crates/gosub_html5/src/document/document_impl.rs create mode 100644 crates/gosub_html5/src/document/fragment.rs create mode 100644 crates/gosub_html5/src/document/query.rs create mode 100644 crates/gosub_html5/src/document/task_queue.rs delete mode 100644 crates/gosub_html5/src/element_class.rs create mode 100644 crates/gosub_html5/src/node/elements.rs create mode 100644 crates/gosub_html5/src/node/node_impl.rs create mode 100644 crates/gosub_html5/src/node/visitor.rs delete mode 100755 crates/gosub_html5/src/parser/document.rs rename crates/gosub_html5/src/{error_logger.rs => parser/errors.rs} (90%) delete mode 100644 crates/gosub_html5/src/util.rs delete mode 100644 crates/gosub_html5/src/visit.rs rename crates/{gosub_styling => gosub_render_utils}/src/render_tree/desc.rs (53%) delete mode 100644 crates/gosub_render_utils/src/render_tree/properties.rs delete mode 100644 crates/gosub_render_utils/src/render_tree/text.rs delete mode 100644 crates/gosub_render_utils/src/render_tree/util.rs create mode 100644 crates/gosub_renderer/src/position.rs create mode 100644 crates/gosub_shared/src/document.rs create mode 100644 crates/gosub_shared/src/element_data.rs create mode 100644 crates/gosub_shared/src/errors.rs create mode 100644 crates/gosub_shared/src/node.rs rename crates/{gosub_css3/src/parser_config.rs => gosub_shared/src/traits.rs} (65%) create mode 100644 crates/gosub_shared/src/traits/css3.rs create mode 100644 crates/gosub_shared/src/traits/document.rs create mode 100644 crates/gosub_shared/src/traits/html5.rs create mode 100644 crates/gosub_shared/src/traits/node.rs create mode 100644 crates/gosub_shared/src/traits/render_tree.rs delete mode 100644 crates/gosub_styling/Cargo.toml delete mode 100644 crates/gosub_styling/README.md delete mode 100644 crates/gosub_styling/src/errors.rs delete mode 100644 crates/gosub_styling/src/functions.rs delete mode 100644 crates/gosub_styling/src/functions/attr.rs delete mode 100644 crates/gosub_styling/src/lib.rs delete mode 100644 crates/gosub_styling/src/render_tree.rs create mode 100644 rustfmt.toml diff --git a/.gitignore b/.gitignore index c7a968a96..d8f1c953c 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,4 @@ local/ settings.db # C API tests (ignore everything but source files) -*/gosub_bindings/tests/* -!*/gosub_bindings/tests/*.c *.dSYM diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f32aa34b2..5f11722df 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -38,10 +38,6 @@ In the root directory, run `make format` to have clippy automatically fix some o ### Running Benchmarks In the root directory, run `make bench` to run the benchmarks. -### Building C API Bindings -In the `crates/gosub_bindings` directory, run `make bindings` to build the C static libraries. For more information on this, see the [README](crates/gosub-bindings/README.md). - -Can also run `make test` in the same directory to build and run the tests for the C bindings. ## Code Style We use cargo's built-in formatter. When running `make test`, the formatter will also run (after all the tests) and display any issues in a `git diff`-like output in the terminal. Please resolve these formatting issues prior to pushing any code! This is validated in our CI when creating a pull request. diff --git a/Cargo.lock b/Cargo.lock index ef084b42f..0d565f97b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,17 +74,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "ahash" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" -dependencies = [ - "getrandom", - "once_cell", - "version_check", -] - [[package]] name = "ahash" version = "0.8.11" @@ -95,7 +84,7 @@ dependencies = [ "getrandom", "once_cell", "version_check", - "zerocopy 0.7.35", + "zerocopy", ] [[package]] @@ -107,66 +96,18 @@ dependencies = [ "memchr", ] -[[package]] -name = "aliasable" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" - [[package]] name = "aligned-vec" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" -[[package]] -name = "alloc-no-stdlib" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" - -[[package]] -name = "alloc-stdlib" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" -dependencies = [ - "alloc-no-stdlib", -] - [[package]] name = "allocator-api2" version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" -[[package]] -name = "allsorts" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afb67debdbc7e8b0716e7b10ce247ff6cd2811b0b478969158b16ac58995d16b" -dependencies = [ - "bitflags 1.3.2", - "bitreader", - "brotli-decompressor", - "byteorder", - "encoding_rs", - "flate2", - "glyph-names", - "itertools 0.10.5", - "lazy_static", - "libc", - "log", - "num-traits", - "ouroboros", - "rustc-hash", - "tinyvec", - "ucd-trie", - "unicode-canonical-combining-class", - "unicode-general-category", - "unicode-joining-type", -] - [[package]] name = "android-activity" version = "0.6.0" @@ -278,14 +219,14 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] name = "arrayref" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" @@ -295,9 +236,9 @@ checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] name = "arrayvec" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "as-raw-xcb-connection" @@ -316,13 +257,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.81" +version = "0.1.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -333,9 +274,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "av1-grain" @@ -344,7 +285,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf" dependencies = [ "anyhow", - "arrayvec 0.7.4", + "arrayvec 0.7.6", "log", "nom 7.1.3", "num-rational", @@ -357,7 +298,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876c75a42f6364451a033496a14c44bffe41f5f4a8236f697391f11024e596d2" dependencies = [ - "arrayvec 0.7.4", + "arrayvec 0.7.6", ] [[package]] @@ -400,7 +341,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.77", + "syn 2.0.79", "which 4.4.2", ] @@ -452,20 +393,11 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" -[[package]] -name = "bitreader" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdd859c9d97f7c468252795b35aeccc412bdbb1e90ee6969c4fa6328272eaeff" -dependencies = [ - "cfg-if", -] - [[package]] name = "bitstream-io" -version = "2.5.0" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dcde5f311c85b8ca30c2e4198d4326bc342c76541590106f5fa4a50946ea499" +checksum = "b81e1519b0d82120d2fd469d5bfb2919a9361c48b02d82d04befc1cdd2002452" [[package]] name = "block" @@ -491,16 +423,6 @@ dependencies = [ "objc2", ] -[[package]] -name = "brotli-decompressor" -version = "2.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", -] - [[package]] name = "built" version = "0.7.4" @@ -521,22 +443,22 @@ checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" [[package]] name = "bytemuck" -version = "1.16.3" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "102087e286b4677862ea56cf8fc58bb2cdfa8725c40ffb80fe3a008eb7f2fc83" +checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee891b04274a59bd38b412188e24b849617b2e45a0fd8d057deb63e7403761b" +checksum = "0cc8b54b395f2fcfbb3d90c47b01c7f444d94d05bdeb775811dec868ac3bbc26" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -553,9 +475,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.7.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "calloop" @@ -591,12 +513,13 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.1.7" +version = "1.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" +checksum = "9540e661f81799159abee814118cc139a2004b3a3aa3ea37724a1b66530b90e0" dependencies = [ "jobserver", "libc", + "shlex", ] [[package]] @@ -735,10 +658,10 @@ version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -855,6 +778,12 @@ dependencies = [ "web-sys", ] +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + [[package]] name = "cookie" version = "0.18.1" @@ -884,9 +813,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "core-graphics" @@ -935,9 +864,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] @@ -1076,6 +1005,19 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "derive_more" +version = "0.99.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.79", +] + [[package]] name = "derive_more" version = "1.0.0" @@ -1093,7 +1035,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "unicode-xid", ] @@ -1122,7 +1064,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1163,9 +1105,9 @@ checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" [[package]] name = "dwrote" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" +checksum = "2da3498378ed373237bdef1eddcc64e7be2d3ba4841f4c22a998e81cadeea83c" dependencies = [ "lazy_static", "libc", @@ -1192,14 +1134,14 @@ dependencies = [ [[package]] name = "enum-as-inner" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a" +checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" dependencies = [ - "heck 0.4.1", + "heck", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1220,9 +1162,9 @@ dependencies = [ [[package]] name = "euclid" -version = "0.22.10" +version = "0.22.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0f0eb73b934648cd7a4a61f1b15391cd95dab0b4da6e2e66c2a072c144b4a20" +checksum = "ad9cdb4b747e485a12abb0e6566612956c7a1bafa3bdb8d682c5b6d403589e48" dependencies = [ "num-traits", ] @@ -1245,21 +1187,21 @@ dependencies = [ [[package]] name = "fdeflate" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" +checksum = "d8090f921a24b04994d9929e204f50b498a33ea6ba559ffaa05e04f7ee7fb5ab" dependencies = [ "simd-adler32", ] [[package]] name = "flate2" -version = "1.0.30" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" dependencies = [ "crc32fast", - "miniz_oxide 0.7.4", + "miniz_oxide 0.8.0", ] [[package]] @@ -1358,7 +1300,7 @@ dependencies = [ "core-text", "dwrote", "fontconfig-cache-parser", - "hashbrown 0.14.5", + "hashbrown", "icu_locid", "icu_properties", "memmap2", @@ -1379,7 +1321,7 @@ dependencies = [ "core-text", "dwrote", "fontconfig-cache-parser", - "hashbrown 0.14.5", + "hashbrown", "icu_locid", "memmap2", "objc2", @@ -1410,7 +1352,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1505,7 +1447,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1635,21 +1577,6 @@ dependencies = [ "gl_generator", ] -[[package]] -name = "glyph-names" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3531d702d6c1a3ba92a5fb55a404c7b8c476c8e7ca249951077afcbe4bc807f" - -[[package]] -name = "gosub_bindings" -version = "0.1.0" -dependencies = [ - "gosub_html5", - "gosub_rendering", - "gosub_shared", -] - [[package]] name = "gosub_config" version = "0.1.0" @@ -1676,9 +1603,15 @@ dependencies = [ "anyhow", "colors-transform", "gosub_shared", + "itertools 0.13.0", "lazy_static", "log", + "nom 7.1.3", + "rand 0.8.5", + "serde", + "serde_json", "simple_logger", + "thiserror", ] [[package]] @@ -1691,7 +1624,7 @@ dependencies = [ "console_log", "cookie", "criterion", - "derive_more", + "derive_more 1.0.0", "futures", "getrandom", "gosub_config", @@ -1703,7 +1636,6 @@ dependencies = [ "gosub_renderer", "gosub_rendering", "gosub_shared", - "gosub_styling", "gosub_taffy", "gosub_testing", "gosub_useragent", @@ -1732,7 +1664,7 @@ name = "gosub_html5" version = "0.1.0" dependencies = [ "criterion", - "derive_more", + "derive_more 1.0.0", "gosub_css3", "gosub_shared", "gosub_testing", @@ -1760,7 +1692,7 @@ version = "0.1.0" dependencies = [ "anyhow", "cookie", - "derive_more", + "derive_more 1.0.0", "domain-lookup-tree", "gosub_config", "gosub_shared", @@ -1777,10 +1709,13 @@ dependencies = [ name = "gosub_render_backend" version = "0.1.0" dependencies = [ + "anyhow", + "gosub_css3", "gosub_html5", "gosub_shared", "gosub_typeface", "image", + "log", "raw-window-handle", "smallvec", ] @@ -1796,7 +1731,6 @@ dependencies = [ "gosub_render_backend", "gosub_rendering", "gosub_shared", - "gosub_styling", "image", "log", "url", @@ -1812,7 +1746,8 @@ dependencies = [ "gosub_css3", "gosub_html5", "gosub_render_backend", - "gosub_styling", + "gosub_shared", + "log", "regex", "rstar", ] @@ -1824,6 +1759,7 @@ dependencies = [ "anyhow", "chardet", "chardetng", + "derive_more 0.99.18", "encoding_rs", "getrandom", "js-sys", @@ -1836,31 +1772,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "gosub_styling" -version = "0.1.0" -dependencies = [ - "anyhow", - "backtrace", - "colors-transform", - "gosub_css3", - "gosub_html5", - "gosub_render_backend", - "gosub_shared", - "gosub_typeface", - "itertools 0.13.0", - "lazy_static", - "log", - "memoize", - "nom 7.1.3", - "rand 0.9.0-alpha.2", - "regex", - "rust-fontconfig", - "serde", - "serde_json", - "thiserror", -] - [[package]] name = "gosub_svg" version = "0.1.0" @@ -1880,7 +1791,6 @@ dependencies = [ "anyhow", "gosub_render_backend", "gosub_shared", - "gosub_styling", "gosub_typeface", "log", "parley", @@ -1963,7 +1873,7 @@ version = "0.1.0" dependencies = [ "anyhow", "colored", - "derive_more", + "derive_more 1.0.0", "gosub_shared", "gosub_v8", "gosub_webinterop", @@ -1981,7 +1891,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -2024,7 +1934,7 @@ checksum = "9c08c1f623a8d0b722b8b99f821eb0ba672a1618f0d3b16ddbee1cedd2dd8557" dependencies = [ "bitflags 2.6.0", "gpu-descriptor-types", - "hashbrown 0.14.5", + "hashbrown", ] [[package]] @@ -2080,22 +1990,13 @@ dependencies = [ "byteorder", ] -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.8", -] - [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ - "ahash 0.8.11", + "ahash", "allocator-api2", ] @@ -2124,12 +2025,6 @@ dependencies = [ "stable_deref_trait", ] -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - [[package]] name = "heck" version = "0.5.0" @@ -2339,7 +2234,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -2415,12 +2310,12 @@ checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126" [[package]] name = "indexmap" -version = "2.2.6" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown", ] [[package]] @@ -2440,7 +2335,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -2457,17 +2352,17 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" [[package]] name = "is-terminal" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" dependencies = [ - "hermit-abi 0.3.9", + "hermit-abi 0.4.0", "libc", "windows-sys 0.52.0", ] @@ -2576,11 +2471,11 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" [[package]] name = "kurbo" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e5aa9f0f96a938266bdb12928a67169e8d22c6a786fda8ed984b85e6ba93c3c" +checksum = "89234b2cc610a7dd927ebde6b41dd1a5d4214cffaef4cf1fb2195d592f92518f" dependencies = [ - "arrayvec 0.7.4", + "arrayvec 0.7.6", "smallvec", ] @@ -2617,9 +2512,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.158" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libfuzzer-sys" @@ -2708,15 +2603,6 @@ dependencies = [ "imgref", ] -[[package]] -name = "lru" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a" -dependencies = [ - "hashbrown 0.12.3", -] - [[package]] name = "lru-cache" version = "0.1.2" @@ -2758,36 +2644,13 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" +checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" dependencies = [ "libc", ] -[[package]] -name = "memoize" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5df4051db13d0816cf23196d3baa216385ae099339f5d0645a8d9ff2305e82b8" -dependencies = [ - "lazy_static", - "lru", - "memoize-inner", -] - -[[package]] -name = "memoize-inner" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27bdece7e91f0d1e33df7b46ec187a93ea0d4e642113a1039ac8bfdd4a3273ac" -dependencies = [ - "lazy_static", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "metal" version = "0.29.0" @@ -2826,7 +2689,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", - "simd-adler32", ] [[package]] @@ -2836,13 +2698,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ "adler2", + "simd-adler32", ] [[package]] name = "mio" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ "hermit-abi 0.3.9", "libc", @@ -2850,23 +2713,13 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "mmapio" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0204e2cac68f5b2e35b7ec8cb5d906f6e58e78dad8066a30b6ee54da99bb03dd" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "naga" version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e536ae46fcab0876853bd4a632ede5df4b1c2527a58f6c5a4150fe86be858231" dependencies = [ - "arrayvec 0.7.4", + "arrayvec 0.7.6", "bit-set 0.5.3", "bitflags 2.6.0", "codespan-reporting", @@ -2886,7 +2739,7 @@ version = "22.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bd5a652b6faf21496f2cfd88fc49989c8db0825d1f6746b1a71a6ede24a63ad" dependencies = [ - "arrayvec 0.7.4", + "arrayvec 0.7.6", "bit-set 0.6.0", "bitflags 2.6.0", "cfg_aliases 0.1.1", @@ -3017,7 +2870,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -3068,7 +2921,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -3294,18 +3147,21 @@ dependencies = [ [[package]] name = "object" -version = "0.36.2" +version = "0.36.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" +checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" +dependencies = [ + "portable-atomic", +] [[package]] name = "oorandom" @@ -3328,30 +3184,6 @@ dependencies = [ "libredox", ] -[[package]] -name = "ouroboros" -version = "0.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2ba07320d39dfea882faa70554b4bd342a5f273ed59ba7c1c6b4c840492c954" -dependencies = [ - "aliasable", - "ouroboros_macro", - "static_assertions", -] - -[[package]] -name = "ouroboros_macro" -version = "0.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec4c6225c69b4ca778c0aea097321a64c421cf4577b331c61b229267edabb6f8" -dependencies = [ - "heck 0.4.1", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.77", -] - [[package]] name = "owned_ttf_parser" version = "0.24.0" @@ -3379,7 +3211,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.3", + "redox_syscall 0.5.6", "smallvec", "windows-targets 0.52.6", ] @@ -3447,7 +3279,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -3482,7 +3314,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -3499,15 +3331,15 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "plotters" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" dependencies = [ "num-traits", "plotters-backend", @@ -3518,37 +3350,37 @@ dependencies = [ [[package]] name = "plotters-backend" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" [[package]] name = "plotters-svg" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" dependencies = [ "plotters-backend", ] [[package]] name = "png" -version = "0.17.13" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" +checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0" dependencies = [ "bitflags 1.3.2", "crc32fast", "fdeflate", "flate2", - "miniz_oxide 0.7.4", + "miniz_oxide 0.8.0", ] [[package]] name = "polling" -version = "3.7.2" +version = "3.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3ed00ed3fbf728b5816498ecd316d1716eecaced9c0c8d2c5a6740ca214985b" +checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" dependencies = [ "cfg-if", "concurrent-queue", @@ -3556,7 +3388,7 @@ dependencies = [ "pin-project-lite", "rustix", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3571,6 +3403,12 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "portable-atomic" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" + [[package]] name = "powerfmt" version = "0.2.0" @@ -3579,11 +3417,11 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.18" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee4364d9f3b902ef14fab8a1ddffb783a1cb6b4bba3bfc1fa3922732c7de97f" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ - "zerocopy 0.6.6", + "zerocopy", ] [[package]] @@ -3594,45 +3432,21 @@ checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" [[package]] name = "prettyplease" -version = "0.2.20" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" dependencies = [ "proc-macro2", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] name = "proc-macro-crate" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ - "toml_edit 0.21.1", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", + "toml_edit", ] [[package]] @@ -3660,7 +3474,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd" dependencies = [ "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -3686,9 +3500,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quick-xml" -version = "0.34.0" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f24d770aeca0eacb81ac29dfbc55ebcc09312fdd1f8bbecdc7e4a84e000e3b4" +checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" dependencies = [ "memchr", ] @@ -3721,7 +3535,7 @@ checksum = "c3e256ff62cee3e03def855c4d4260106d2bb1696fdc01af03e9935b993720a5" dependencies = [ "rand_chacha 0.9.0-alpha.2", "rand_core 0.9.0-alpha.2", - "zerocopy 0.7.35", + "zerocopy", ] [[package]] @@ -3760,7 +3574,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4e93f5a5e3c528cda9acb0928c31b2ba868c551cc46e67b778075e34aab9906" dependencies = [ "getrandom", - "zerocopy 0.7.35", + "zerocopy", ] [[package]] @@ -3777,7 +3591,7 @@ checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" dependencies = [ "arbitrary", "arg_enum_proc_macro", - "arrayvec 0.7.4", + "arrayvec 0.7.6", "av1-grain", "bitstream-io", "built", @@ -3806,9 +3620,9 @@ dependencies = [ [[package]] name = "ravif" -version = "0.11.9" +version = "0.11.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5797d09f9bd33604689e87e8380df4951d4912f01b63f71205e2abd4ae25e6b6" +checksum = "a8f0bfd976333248de2078d350bfdf182ff96e168a24d23d2436cef320dd4bdd" dependencies = [ "avif-serialize", "imgref", @@ -3875,18 +3689,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.3" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +checksum = "355ae415ccd3a04315d3f8246e86d67689ea74d88d915576e1589a351062a13b" dependencies = [ "bitflags 2.6.0", ] [[package]] name = "regex" -version = "1.10.6" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", @@ -3896,9 +3710,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", @@ -3907,9 +3721,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "renderdoc-sys" @@ -3946,9 +3760,9 @@ dependencies = [ [[package]] name = "rgb" -version = "0.8.45" +version = "0.8.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade4539f42266ded9e755c605bdddf546242b2c961b03b06a7375260788a0523" +checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" dependencies = [ "bytemuck", ] @@ -3991,18 +3805,6 @@ dependencies = [ "smallvec", ] -[[package]] -name = "rust-fontconfig" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea6002602a06c22f5f4e5ea2ed68854bfce071b48e1092c03874e34d0b59eb4b" -dependencies = [ - "allsorts", - "mmapio", - "rayon", - "xmlparser", -] - [[package]] name = "rustc-demangle" version = "0.1.24" @@ -4015,11 +3817,20 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + [[package]] name = "rustix" -version = "0.38.34" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ "bitflags 2.6.0", "errno", @@ -4030,9 +3841,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.12" +version = "0.23.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" +checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" dependencies = [ "log", "once_cell", @@ -4045,15 +3856,15 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.7.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" +checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55" [[package]] name = "rustls-webpki" -version = "0.102.6" +version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ "ring", "rustls-pki-types", @@ -4134,6 +3945,12 @@ dependencies = [ "tiny-skia", ] +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + [[package]] name = "serde" version = "1.0.210" @@ -4151,7 +3968,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -4169,9 +3986,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] @@ -4416,9 +4233,9 @@ checksum = "20e16a0f46cf5fd675563ef54f26e83e20f2366bcf027bcb3cc3ed2b98aaf2ca" [[package]] name = "svgtypes" -version = "0.15.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fae3064df9b89391c9a76a0425a69d124aee9c5c28455204709e72c39868a43c" +checksum = "794de53cc48eaabeed0ab6a3404a65f40b3e38c067e4435883a65d2aa4ca000e" dependencies = [ "kurbo", "siphasher 1.0.1", @@ -4448,9 +4265,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -4465,7 +4282,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -4475,7 +4292,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" dependencies = [ "cfg-expr", - "heck 0.5.0", + "heck", "pkg-config", "toml", "version-compare", @@ -4487,7 +4304,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cb893bff0f80ae17d3a57e030622a967b8dbc90e38284d9b4b1442e23873c94" dependencies = [ - "arrayvec 0.7.4", + "arrayvec 0.7.6", "grid", "num-traits", "serde", @@ -4527,7 +4344,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -4538,7 +4355,7 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "test-case-core", ] @@ -4568,7 +4385,7 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -4622,7 +4439,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" dependencies = [ "arrayref", - "arrayvec 0.7.4", + "arrayvec 0.7.6", "bytemuck", "cfg-if", "log", @@ -4678,9 +4495,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.39.2" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" dependencies = [ "backtrace", "bytes", @@ -4693,14 +4510,14 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.17" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a44eede9b727419af8095cb2d72fab15487a541f54647ad4414b34096ee4631" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.18", + "toml_edit", ] [[package]] @@ -4714,26 +4531,15 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.21.1" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" -dependencies = [ - "indexmap", - "toml_datetime", - "winnow 0.5.40", -] - -[[package]] -name = "toml_edit" -version = "0.22.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1490595c74d930da779e944f5ba2ecdf538af67df1a9848cbd156af43c1b7cf0" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.16", + "winnow", ] [[package]] @@ -4755,7 +4561,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -4788,12 +4594,6 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" -[[package]] -name = "ucd-trie" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" - [[package]] name = "unicode-bidi" version = "0.3.15" @@ -4812,12 +4612,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64af057ad7466495ca113126be61838d8af947f41d93a949980b2389a118082f" -[[package]] -name = "unicode-canonical-combining-class" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6925586af9268182c711e47c0853ed84131049efaca41776d0ca97f983865c32" - [[package]] name = "unicode-ccc" version = "0.2.0" @@ -4830,50 +4624,38 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "260bc6647b3893a9a90668360803a15f96b85a5257b1c3a0c3daf6ae2496de42" -[[package]] -name = "unicode-general-category" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2281c8c1d221438e373249e065ca4989c4c36952c211ff21a0ee91c44a3869e7" - [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "unicode-joining-type" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22f8cb47ccb8bc750808755af3071da4a10dcd147b68fc874b7ae4b12543f6f5" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] [[package]] name = "unicode-properties" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4259d9d4425d9f0661581b804cb85fe66a4c631cadd8f490d1c13a35d5d9291" +checksum = "52ea75f83c0137a9b98608359a5f1af8144876eb67bcb1ce837368e906a9f524" [[package]] name = "unicode-script" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8d71f5726e5f285a935e9fe8edfd53f0491eb6e9a5774097fdabee7cd8c9cd" +checksum = "9fb421b350c9aff471779e262955939f565ec18b86c15364e6bdf0d662ca7c1f" [[package]] name = "unicode-segmentation" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-vo" @@ -4883,15 +4665,15 @@ checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" [[package]] name = "unicode-width" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-xid" -version = "0.2.4" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "universal-hash" @@ -5019,7 +4801,7 @@ dependencies = [ "miniz_oxide 0.7.4", "once_cell", "paste", - "which 6.0.2", + "which 6.0.3", ] [[package]] @@ -5180,7 +4962,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "wasm-bindgen-shared", ] @@ -5214,7 +4996,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5248,14 +5030,14 @@ checksum = "4b8220be1fa9e4c889b30fd207d4906657e7e90b12e0e6b0c8b8d8709f5de021" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] name = "wayland-backend" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90e11ce2ca99c97b940ee83edbae9da2d56a08f9ea8158550fd77fa31722993" +checksum = "056535ced7a150d45159d3a8dc30f91a2e2d588ca0b23f70e56033622b8016f6" dependencies = [ "cc", "downcast-rs", @@ -5267,9 +5049,9 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.5" +version = "0.31.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e321577a0a165911bdcfb39cf029302479d7527b517ee58ab0f6ad09edf0943" +checksum = "e3f45d1222915ef1fd2057220c1d9d9624b7654443ea35c3877f7a52bd0a5a2d" dependencies = [ "bitflags 2.6.0", "rustix", @@ -5290,9 +5072,9 @@ dependencies = [ [[package]] name = "wayland-cursor" -version = "0.31.5" +version = "0.31.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ef9489a8df197ebf3a8ce8a7a7f0a2320035c3743f3c1bd0bdbccf07ce64f95" +checksum = "3a94697e66e76c85923b0d28a0c251e8f0666f58fc47d316c0f4da6da75d37cb" dependencies = [ "rustix", "wayland-client", @@ -5301,9 +5083,9 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.32.3" +version = "0.32.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62989625a776e827cc0f15d41444a3cea5205b963c3a25be48ae1b52d6b4daaa" +checksum = "2b5755d77ae9040bb872a25026555ce4cb0ae75fd923e90d25fba07d81057de0" dependencies = [ "bitflags 2.6.0", "wayland-backend", @@ -5313,9 +5095,9 @@ dependencies = [ [[package]] name = "wayland-protocols-plasma" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f79f2d57c7fcc6ab4d602adba364bf59a5c24de57bd194486bf9b8360e06bfc4" +checksum = "8a0a41a6875e585172495f7a96dfa42ca7e0213868f4f15c313f7c33221a7eff" dependencies = [ "bitflags 2.6.0", "wayland-backend", @@ -5326,9 +5108,9 @@ dependencies = [ [[package]] name = "wayland-protocols-wlr" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd993de54a40a40fbe5601d9f1fbcaef0aebcc5fda447d7dc8f6dcbaae4f8953" +checksum = "dad87b5fd1b1d3ca2f792df8f686a2a11e3fe1077b71096f7a175ab699f89109" dependencies = [ "bitflags 2.6.0", "wayland-backend", @@ -5339,9 +5121,9 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.31.4" +version = "0.31.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7b56f89937f1cf2ee1f1259cf2936a17a1f45d8f0aa1019fae6d470d304cfa6" +checksum = "597f2001b2e5fc1121e3d5b9791d3e78f05ba6bfa4641053846248e3a13661c3" dependencies = [ "proc-macro2", "quick-xml", @@ -5350,9 +5132,9 @@ dependencies = [ [[package]] name = "wayland-sys" -version = "0.31.4" +version = "0.31.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43676fe2daf68754ecf1d72026e4e6c15483198b5d24e888b74d3f22f887a148" +checksum = "efa8ac0d8e8ed3e3b5c9fc92c7881406a268e11555abe36493efabe649a29e09" dependencies = [ "dlib", "log", @@ -5382,9 +5164,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.26.3" +version = "0.26.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd" +checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" dependencies = [ "rustls-pki-types", ] @@ -5401,7 +5183,7 @@ version = "22.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d1c4ba43f80542cf63a0a6ed3134629ae73e8ab51e4b765a67f3aa062eb433" dependencies = [ - "arrayvec 0.7.4", + "arrayvec 0.7.6", "cfg_aliases 0.1.1", "document-features", "js-sys", @@ -5426,7 +5208,7 @@ version = "22.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0348c840d1051b8e86c3bcd31206080c5e71e5933dabd79be1ce732b0b2f089a" dependencies = [ - "arrayvec 0.7.4", + "arrayvec 0.7.6", "bit-vec 0.7.0", "bitflags 2.6.0", "cfg_aliases 0.1.1", @@ -5452,7 +5234,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6bbf4b4de8b2a83c0401d9e5ae0080a2792055f25859a02bf9be97952bbed4f" dependencies = [ "android_system_properties", - "arrayvec 0.7.4", + "arrayvec 0.7.6", "ash", "bit-set 0.6.0", "bitflags 2.6.0", @@ -5515,9 +5297,9 @@ dependencies = [ [[package]] name = "which" -version = "6.0.2" +version = "6.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d9c5ed668ee1f17edb3b627225343d210006a90bb1e3745ce1f30b1fb115075" +checksum = "b4ee928febd44d98f2f459a4a79bd4d928591333a494a10a868418ac1b39cf1f" dependencies = [ "either", "home", @@ -5555,11 +5337,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -5614,6 +5396,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -5798,7 +5589,7 @@ version = "0.30.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0be9e76a1f1077e04a411f0b989cbd3c93339e1771cb41e71ac4aee95bfd2c67" dependencies = [ - "ahash 0.8.11", + "ahash", "android-activity", "atomic-waker", "bitflags 2.6.0", @@ -5846,18 +5637,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.5.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] - -[[package]] -name = "winnow" -version = "0.6.16" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b480ae9340fc261e6be3e95a1ba86d54ae3f9171132a73ce8d4bbaf68339507c" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] @@ -5927,9 +5709,9 @@ checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" [[package]] name = "xcursor" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d491ee231a51ae64a5b762114c3ac2104b967aadba1de45c86ca42cf051513b7" +checksum = "0ef33da6b1660b4ddbfb3aef0ade110c8b8a781a3b6382fa5f2b5b040fd55f61" [[package]] name = "xkbcommon-dl" @@ -5952,15 +5734,9 @@ checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" [[package]] name = "xml-rs" -version = "0.8.20" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193" - -[[package]] -name = "xmlparser" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" +checksum = "af4e2e2f7cba5a093896c1e150fbfe177d1883e7448200efb81d40b9d339ef26" [[package]] name = "xmlwriter" @@ -5994,7 +5770,7 @@ checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "synstructure", ] @@ -6004,34 +5780,14 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd15f8e0dbb966fd9245e7498c7e9e5055d9e5c8b676b95bd67091cd11a1e697" -[[package]] -name = "zerocopy" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854e949ac82d619ee9a14c66a1b674ac730422372ccb759ce0c39cabcf2bf8e6" -dependencies = [ - "byteorder", - "zerocopy-derive 0.6.6", -] - [[package]] name = "zerocopy" version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "zerocopy-derive 0.7.35", -] - -[[package]] -name = "zerocopy-derive" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "125139de3f6b9d625c39e2efdd73d41bdac468ccd556556440e322be0e1bbd91" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", + "byteorder", + "zerocopy-derive", ] [[package]] @@ -6042,7 +5798,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -6062,7 +5818,7 @@ checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "synstructure", ] @@ -6091,7 +5847,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 17fe717c1..fa96aad2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,6 @@ gosub_shared = { path = "./crates/gosub_shared", features = [] } gosub_config = { path = "./crates/gosub_config", features = [] } gosub_html5 = { path = "./crates/gosub_html5", features = [] } gosub_css3 = { path = "./crates/gosub_css3", features = [] } -gosub_styling = { path = "./crates/gosub_styling", features = [] } gosub_jsapi = { path = "./crates/gosub_jsapi", features = [] } gosub_testing = { path = "./crates/gosub_testing", features = [] } gosub_rendering = { path = "crates/gosub_render_utils", features = [] } diff --git a/benches/tree_iterator.rs b/benches/tree_iterator.rs index 0532ab999..639246007 100644 --- a/benches/tree_iterator.rs +++ b/benches/tree_iterator.rs @@ -1,10 +1,13 @@ use std::fs::File; use criterion::{criterion_group, criterion_main, Criterion}; -use gosub_html5::node::NodeId; -use gosub_html5::parser::document::{Document, DocumentBuilder, TreeIterator}; +use gosub_css3::system::Css3System; +use gosub_html5::document::builder::DocumentBuilderImpl; +use gosub_html5::document::document_impl::TreeIterator; use gosub_html5::parser::Html5Parser; use gosub_shared::byte_stream::{ByteStream, Encoding}; +use gosub_shared::node::NodeId; +use gosub_shared::traits::document::DocumentBuilder; fn wikipedia_main_page(c: &mut Criterion) { // Criterion can report inconsistent results from run to run in some cases. We attempt to @@ -17,13 +20,12 @@ fn wikipedia_main_page(c: &mut Criterion) { let mut stream = ByteStream::new(Encoding::UTF8, None); let _ = stream.read_from_file(html_file); - let main_document = DocumentBuilder::new_document(None); - let document = Document::clone(&main_document); - let _ = Html5Parser::parse_document(&mut stream, document, None); + let doc_handle = >::new_document(None); + let _ = Html5Parser::parse_document(&mut stream, doc_handle.clone(), None); group.bench_function("wikipedia main page", |b| { b.iter(|| { - let tree_iterator = TreeIterator::new(&main_document); + let tree_iterator = TreeIterator::new(doc_handle.clone()); let _ = tree_iterator.collect::>(); }) }); @@ -43,13 +45,12 @@ fn stackoverflow_home(c: &mut Criterion) { let mut bytestream = ByteStream::new(Encoding::UTF8, None); let _ = bytestream.read_from_file(html_file); - let main_document = DocumentBuilder::new_document(None); - let document = Document::clone(&main_document); - let _ = Html5Parser::parse_document(&mut bytestream, document, None); + let doc_handle = >::new_document(None); + let _ = Html5Parser::parse_document(&mut bytestream, doc_handle.clone(), None); group.bench_function("stackoverflow home", |b| { b.iter(|| { - let tree_iterator = TreeIterator::new(&main_document); + let tree_iterator = TreeIterator::new(doc_handle.clone()); let _ = tree_iterator.collect::>(); }) }); diff --git a/crates/gosub_bindings/.gitignore b/crates/gosub_bindings/.gitignore deleted file mode 100644 index e9b24178c..000000000 --- a/crates/gosub_bindings/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -libgosub.a -lib/ -*.dSYM diff --git a/crates/gosub_bindings/Cargo.toml b/crates/gosub_bindings/Cargo.toml deleted file mode 100644 index e6d390d72..000000000 --- a/crates/gosub_bindings/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "gosub_bindings" -version = "0.1.0" -edition = "2021" -authors = ["Gosub Community "] -license = "MIT" - -[dependencies] -gosub_shared = { path = "../gosub_shared" } -gosub_rendering = { path = "../gosub_render_utils" } -gosub_html5 = { path = "../gosub_html5" } - -[lib] -crate-type = ["staticlib"] diff --git a/crates/gosub_bindings/Makefile b/crates/gosub_bindings/Makefile deleted file mode 100644 index 796c1fe1b..000000000 --- a/crates/gosub_bindings/Makefile +++ /dev/null @@ -1,54 +0,0 @@ -# "global" compile settings - -# compilation mode Release or Debug -MODE?=Release - -CFLAGS_DEBUG := -std=c99 -g -Wall -Wextra -O0 -CFLAGS_RELEASE := -std=c99 -Wall -Wextra -O2 - -TARGET_DIR_DEBUG := ../../target/debug -TARGET_DIR_RELEASE := ../../target/release - -ifeq ($(MODE), Release) -$(info ***** COMPILING IN RELEASE MODE *****) -CFLAGS = $(CFLAGS_RELEASE) -TARGET_DIR = $(TARGET_DIR_RELEASE) -else ifeq ($(MODE), Debug) -$(info ***** COMPILING IN DEBUG MODE *****) -CFLAGS = $(CFLAGS_DEBUG) -TARGET_DIR = $(TARGET_DIR_DEBUG) -else -$(warning ***** UNKNOWN MODE. DEFAULTING TO RELEASE MODE *****) -CFLAGS = $(CFLAGS_RELEASE) -TARGET_DIR = $(TARGET_DIR_RELEASE) -endif - -CC := gcc - -INCLUDE_DIR := include -SRC_DIR := src -NODE_SRC_DIR := $(SRC_DIR)/nodes - -CPPFLAGS := -I$(INCLUDE_DIR) -I$(INCLUDE_DIR)/nodes -LDFLAGS := -L$(TARGET_DIR) - -bindings: # build gosub static library to be used externally -ifeq ($(MODE), Debug) - cargo build -else - cargo build --release -endif - $(CC) $(CFLAGS) $(CPPFLAGS) -c $(SRC_DIR)/gosub_api.c $(SRC_DIR)/nodes.c $(NODE_SRC_DIR)/text.c - ar rcs libgosub.a gosub_api.o nodes.o text.o - rm *.o - test -d lib || mkdir lib - cp $(TARGET_DIR)/libgosub_bindings.a lib/ - cp ./libgosub.a lib/ - -TEST_SRC_DIR := tests -test: # build and run tests for bindings - $(CC) $(TEST_SRC_DIR)/rendertree_test.c -L./ -lgosub $(LDFLAGS) -lgosub_bindings -lsqlite3 -lm -o $(TEST_SRC_DIR)/rendertree_test $(CPPFLAGS) $(CFLAGS) - ./$(TEST_SRC_DIR)/rendertree_test - -format: - cargo clippy --fix --allow-dirty --allow-staged diff --git a/crates/gosub_bindings/README.md b/crates/gosub_bindings/README.md deleted file mode 100644 index 2323dbf43..000000000 --- a/crates/gosub_bindings/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# Gosub Bindings -These are bindings that expose some of Gosub's engine to the world via a C API. Typically these bindings will be used by user agents. - -## Building -By default, the bindings will be built in release mode. You can modify this by specifying a `MODE` variable: -```text -export MODE=Debug # or MODE=Release (default) -make bindings -make test -``` - -or alternatively specify it manually (not recommended) -```text -make bindings MODE=Debug -make test MODE=Debug -``` - -This approach is not recommended because if you forget to specify it, it will default to release mode and you may be using the wrong version. diff --git a/crates/gosub_bindings/include/gosub_api.h b/crates/gosub_bindings/include/gosub_api.h deleted file mode 100644 index c0a7389ab..000000000 --- a/crates/gosub_bindings/include/gosub_api.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef GOSUB_API_H -#define GOSUB_API_H - -#include "nodes.h" - -extern void *gosub_rendertree_init(const char *html); -extern void *gosub_rendertree_iterator_init(void *rendertree); -extern const void *gosub_rendertree_next_node(void *tree_iterator); -extern void gosub_rendertree_get_node_data(const void *current_node, - struct node_t *node); -extern void gosub_rendertree_iterator_free(void *tree_iterator); -extern void gosub_rendertree_free(void *render_free); - -struct rendertree_t { - void *tree; - void *iterator; - const void *current_node; - struct node_t *data; -}; - -/// Initialize a render tree by passing a stack-allocated -/// struct by address. -/// Returns 0 on success or -1 if a failure occurred. -int8_t rendertree_init(struct rendertree_t *rendertree, const char *html); - -/// Get the next node in the render tree as a read-only pointer. -/// Returns NULL when reaching end of tree. -const struct node_t *rendertree_next(struct rendertree_t *rendertree); - -/// Get the type of the current node the render tree is pointing to. -enum node_type_e -rendertree_get_current_node_type(const struct rendertree_t *rendertree); - -/// Free all memory tied to the render tree -void rendertree_free(struct rendertree_t *rendertree); - -#endif diff --git a/crates/gosub_bindings/include/nodes.h b/crates/gosub_bindings/include/nodes.h deleted file mode 100644 index d4c55c369..000000000 --- a/crates/gosub_bindings/include/nodes.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef GOSUB_API_NODES_H -#define GOSUB_API_NODES_H - -#include -#include // for NULL (which is basically just 0... but more clear) -#include -#include - -#include "nodes/text.h" -#include "properties.h" - -struct node_t *rendertree_node_init(); -void rendertree_node_free(struct node_t **node); - -enum node_type_e { NODE_TYPE_ROOT = 0u, NODE_TYPE_TEXT }; - -struct node_t { - enum node_type_e type; - struct position_t position; - struct rectangle_t margin; - struct rectangle_t padding; - union data { - bool root; // NODE_TYPE_ROOT - struct node_text_t text; // NODE_TYPE_TEXT - } data; -}; - -struct node_t *rendertree_node_init(); -double rendertree_node_get_x(const struct node_t *node); -double rendertree_node_get_y(const struct node_t *node); -double rendertree_node_get_margin_top(const struct node_t *node); -double rendertree_node_get_margin_left(const struct node_t *node); -double rendertree_node_get_margin_right(const struct node_t *node); -double rendertree_node_get_margin_bottom(const struct node_t *node); -double rendertree_node_get_padding_top(const struct node_t *node); -double rendertree_node_get_padding_left(const struct node_t *node); -double rendertree_node_get_padding_right(const struct node_t *node); -double rendertree_node_get_padding_bottom(const struct node_t *node); -void rendertree_node_free_data(struct node_t *node); -void rendertree_node_free(struct node_t **node); - -#endif diff --git a/crates/gosub_bindings/include/nodes/text.h b/crates/gosub_bindings/include/nodes/text.h deleted file mode 100644 index 9873a0193..000000000 --- a/crates/gosub_bindings/include/nodes/text.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef GOSUB_API_NODES_TEXT_H -#define GOSUB_API_NODES_TEXT_H - -#include -#include - -struct node_t; - -struct node_text_t { - // this tag is not used but is required to map properly from Rust - uint32_t tag; - char *value; - char *font; - double font_size; - bool is_bold; -}; - -void rendertree_node_text_free_data(struct node_text_t *text); - -const char *rendertree_node_text_get_value(const struct node_t *node); -const char *rendertree_node_text_get_font(const struct node_t *node); -double rendertree_node_text_get_font_size(const struct node_t *node); -bool rendertree_node_text_get_bold(const struct node_t *node); - -#endif diff --git a/crates/gosub_bindings/include/properties.h b/crates/gosub_bindings/include/properties.h deleted file mode 100644 index d9502bf8d..000000000 --- a/crates/gosub_bindings/include/properties.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef GOSUB_API_PROPERTIES_H -#define GOSUB_API_PROPERTIES_H - -struct position_t { - double x; - double y; -}; - -struct rectangle_t { - double top; - double left; - double right; - double bottom; -}; - -#endif diff --git a/crates/gosub_bindings/src/gosub_api.c b/crates/gosub_bindings/src/gosub_api.c deleted file mode 100644 index 005a9c1e5..000000000 --- a/crates/gosub_bindings/src/gosub_api.c +++ /dev/null @@ -1,50 +0,0 @@ -#include "gosub_api.h" - -int8_t rendertree_init(struct rendertree_t *rendertree, const char *html) { - - rendertree->tree = gosub_rendertree_init(html); - if (!rendertree->tree) { - return -1; - } - - rendertree->iterator = gosub_rendertree_iterator_init(rendertree->tree); - if (!rendertree->iterator) { - gosub_rendertree_free(rendertree->tree); - return -1; - } - - rendertree->current_node = NULL; - - rendertree->data = rendertree_node_init(); - if (!rendertree->data) { - gosub_rendertree_iterator_free(rendertree->iterator); - gosub_rendertree_free(rendertree->tree); - return -1; - } - - return 0; -} - -const struct node_t *rendertree_next(struct rendertree_t *rendertree) { - rendertree_node_free_data(rendertree->data); - rendertree->current_node = - gosub_rendertree_next_node(rendertree->iterator); - if (!rendertree->current_node) - return NULL; - gosub_rendertree_get_node_data(rendertree->current_node, rendertree->data); - return (const struct node_t *)rendertree->data; -} - -enum node_type_e -rendertree_get_current_node_type(const struct rendertree_t *rendertree) { - return rendertree->data->type; -} - -void rendertree_free(struct rendertree_t *rendertree) { - gosub_rendertree_iterator_free(rendertree->iterator); - rendertree->iterator = NULL; - gosub_rendertree_free(rendertree->tree); - rendertree->tree = NULL; - rendertree_node_free(&rendertree->data); - rendertree->data = NULL; -} diff --git a/crates/gosub_bindings/src/lib.rs b/crates/gosub_bindings/src/lib.rs deleted file mode 100644 index 0a61eb7c3..000000000 --- a/crates/gosub_bindings/src/lib.rs +++ /dev/null @@ -1,118 +0,0 @@ -use std::ffi::c_char; -use std::ffi::CStr; -use std::ptr; - -pub mod wrapper; - -use gosub_html5::parser::document::{Document, DocumentBuilder}; -use gosub_html5::parser::Html5Parser; -use gosub_rendering::render_tree::{Node, NodeType, RenderTree, TreeIterator}; -use gosub_shared::byte_stream::{ByteStream, Encoding}; -use wrapper::node::CNode; - -/// Initialize a render tree and return an owning pointer to it. -/// If the HTML fails to parse or the html string fails to be converted to Rust, -/// returns a NULL pointer. -/// -/// # Safety -/// Takes a read-only pointer owned from the C API representing the HTML source -/// to build a render tree. DO NOT take ownership of this pointer in Rust or the -/// universe might collapse. -/// -/// Moves an owning pointer to the rendertree using Box::into_raw() to the C API. -/// This pointer MUST be passed to gosub_rendertree_free() after usage for proper cleanup. -#[no_mangle] -pub unsafe extern "C" fn gosub_rendertree_init(html: *const c_char) -> *mut RenderTree { - let html_str = unsafe { - if let Ok(html_str) = CStr::from_ptr(html).to_str() { - html_str - } else { - return ptr::null_mut(); - } - }; - let mut stream = ByteStream::new(Encoding::UTF8, None); - stream.read_from_str(html_str, Some(Encoding::UTF8)); - stream.close(); - - let doc = DocumentBuilder::new_document(None); - let parse_result = Html5Parser::parse_document(&mut stream, Document::clone(&doc), None); - - if parse_result.is_ok() { - let mut rendertree = Box::new(RenderTree::new(&doc)); - rendertree.build(); - - Box::into_raw(rendertree) - } else { - ptr::null_mut() - } -} - -/// Construct a tree iterator for a render tree and return an owning pointer to it. -/// -/// # Safety -/// Moves an owning pointer to the tree iterator using Box::into_raw() to the C API. -/// This pointer MUST be passed to gosub_rendertree_iterator_free() after usage for proper cleanup. -#[no_mangle] -pub unsafe extern "C" fn gosub_rendertree_iterator_init( - rendertree: *const RenderTree, -) -> *mut TreeIterator { - let tree_iterator = Box::new(TreeIterator::new(&(*rendertree))); - Box::into_raw(tree_iterator) -} - -/// Takes a tree_iterator and returns a non-owning pointer to the next node -/// -/// # Safety -/// Takes a tree_iterator pointer (owned by the C API generated by gosub_rendertree_iterator_init()) -/// and modifies it to point to the next tree-order node in the tree. Any heap-allocated data -/// on the current node is free'd before pointing to the next node. Returns a ready-only pointer -/// to the next node. -#[no_mangle] -pub unsafe extern "C" fn gosub_rendertree_next_node( - tree_iterator: *mut TreeIterator, -) -> *const Node { - let next = (*tree_iterator).next(); - if let Some(next) = next { - next.as_ptr() as *const Node - } else { - ptr::null() - } -} - -/// Fetch the node data according to the NodeType of the current node. -/// -/// # Safety -/// Uses a read-only pointer obtained from gosub_rendertree_next_node() -/// and a mutable pointer owned by the C API to write (copy) the contents -/// of the read-only pointer into the mutable pointer. -#[no_mangle] -pub unsafe extern "C" fn gosub_rendertree_get_node_data(node: *const Node, c_node: *mut CNode) { - // Change this to a match when we have more types - if let NodeType::Text(text_node) = &(*node).node_type { - *c_node = CNode::new_text(&*node, text_node); - } -} - -/// Free the iterator pointer obtained from gosub_rendertree_iterator_init() -/// -/// # Safety -/// This takes ownership of the pointer from the C API and transfers it to Rust so it can -/// be deallocated. -#[no_mangle] -pub unsafe extern "C" fn gosub_rendertree_iterator_free(tree_iterator: *mut TreeIterator) { - let _ = Box::from_raw(tree_iterator); -} - -/// Free the rendertree pointer obtained from gosub_rendertree_init() -/// -/// # Safety -/// This takes ownership of the pointer from the C API and transfers it to Rust so it can -/// be deallocated. -/// -// INTERNAL NOTE: It seems there's a leak happening with the document handle (if you -// check with valgrind) although I cannot figure out how to resolve this memory leak... -// needs more investigation; I've tried various methods. -#[no_mangle] -pub unsafe extern "C" fn gosub_rendertree_free(rendertree: *mut RenderTree) { - let _ = Box::from_raw(rendertree); -} diff --git a/crates/gosub_bindings/src/nodes.c b/crates/gosub_bindings/src/nodes.c deleted file mode 100644 index 4e88c52dd..000000000 --- a/crates/gosub_bindings/src/nodes.c +++ /dev/null @@ -1,67 +0,0 @@ -#include "nodes.h" - -struct node_t *rendertree_node_init() { - struct node_t *node = malloc(sizeof(*node)); - if (!node) - return NULL; - - node->type = NODE_TYPE_ROOT; - node->data.root = true; // dummy value - - return node; -} - -void rendertree_node_free_data(struct node_t *node) { - switch (node->type) { - case NODE_TYPE_ROOT: - break; - case NODE_TYPE_TEXT: - rendertree_node_text_free_data(&node->data.text); - break; - } -} - -double rendertree_node_get_x(const struct node_t *node) { - return node->position.x; -} - -double rendertree_node_get_y(const struct node_t *node) { - return node->position.y; -} - -double rendertree_node_get_margin_top(const struct node_t *node) { - return node->margin.top; -} - -double rendertree_node_get_margin_left(const struct node_t *node) { - return node->margin.left; -} - -double rendertree_node_get_margin_right(const struct node_t *node) { - return node->margin.right; -} - -double rendertree_node_get_margin_bottom(const struct node_t *node) { - return node->margin.bottom; -} - -double rendertree_node_get_padding_top(const struct node_t *node) { - return node->padding.top; -} - -double rendertree_node_get_padding_left(const struct node_t *node) { - return node->padding.left; -} - -double rendertree_node_get_padding_right(const struct node_t *node) { - return node->padding.right; -} - -double rendertree_node_get_padding_bottom(const struct node_t *node) { - return node->padding.bottom; -} - -void rendertree_node_free(struct node_t **node) { - free(*node); - *node = NULL; -} diff --git a/crates/gosub_bindings/src/nodes/text.c b/crates/gosub_bindings/src/nodes/text.c deleted file mode 100644 index 76780ce25..000000000 --- a/crates/gosub_bindings/src/nodes/text.c +++ /dev/null @@ -1,38 +0,0 @@ -#include "text.h" -#include "nodes.h" - -void rendertree_node_text_free_data(struct node_text_t *text) { - free(text->value); - text->value = NULL; - - free(text->font); - text->font = NULL; -} - -const char *rendertree_node_text_get_value(const struct node_t *node) { - if (!node) - return NULL; - - return (const char *)node->data.text.value; -} - -const char *rendertree_node_text_get_font(const struct node_t *node) { - if (!node) - return NULL; - - return (const char *)node->data.text.font; -} - -double rendertree_node_text_get_font_size(const struct node_t *node) { - if (!node) - return 0.0; - - return node->data.text.font_size; -} - -bool rendertree_node_text_get_bold(const struct node_t *node) { - if (!node) - return false; - - return node->data.text.is_bold; -} diff --git a/crates/gosub_bindings/src/wrapper.rs b/crates/gosub_bindings/src/wrapper.rs deleted file mode 100644 index 705f04f73..000000000 --- a/crates/gosub_bindings/src/wrapper.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub mod node; -pub mod text; - -/// Numerical values that map rendertree::NodeType to C -#[repr(C)] -pub enum CNodeType { - Root = 0, - Text = 1, -} diff --git a/crates/gosub_bindings/src/wrapper/node.rs b/crates/gosub_bindings/src/wrapper/node.rs deleted file mode 100644 index 10e35027f..000000000 --- a/crates/gosub_bindings/src/wrapper/node.rs +++ /dev/null @@ -1,47 +0,0 @@ -use gosub_rendering::render_tree::properties::{Position, Rectangle}; -use gosub_rendering::render_tree::{text::TextNode, Node}; - -use crate::wrapper::{text::CTextNode, CNodeType}; - -#[repr(C, u32)] -pub enum CNodeData { - Root(bool), - Text(CTextNode), -} - -#[repr(C)] -pub struct CNode { - pub tag: CNodeType, - pub position: Position, - pub margin: Rectangle, - pub padding: Rectangle, - pub data: CNodeData, -} - -impl Default for CNode { - fn default() -> Self { - Self { - tag: CNodeType::Root, - position: Position::new(), - margin: Rectangle::new(), - padding: Rectangle::new(), - data: CNodeData::Root(true), - } - } -} - -impl CNode { - pub fn new_root() -> Self { - Self::default() - } - - pub fn new_text(node: &Node, text_node: &TextNode) -> Self { - Self { - tag: CNodeType::Text, - position: node.position.clone(), - margin: node.margin.clone(), - padding: node.padding.clone(), - data: CNodeData::Text(CTextNode::from(text_node)), - } - } -} diff --git a/crates/gosub_bindings/src/wrapper/text.rs b/crates/gosub_bindings/src/wrapper/text.rs deleted file mode 100644 index 78c89312b..000000000 --- a/crates/gosub_bindings/src/wrapper/text.rs +++ /dev/null @@ -1,28 +0,0 @@ -use gosub_rendering::render_tree::text::TextNode; -use std::ffi::c_char; -use std::ffi::CString; - -/// This is a C-friendly wrapper around gosub_render_utils::rendertree::text::TextNode -/// that converts Rust Strings to owned pointers to pass to the C API. -#[repr(C)] -pub struct CTextNode { - pub value: *mut c_char, - pub font: *mut c_char, - pub font_size: f64, - pub is_bold: bool, -} - -impl From<&TextNode> for CTextNode { - fn from(text_node: &TextNode) -> Self { - Self { - value: CString::new(text_node.value.clone().into_bytes()) - .expect("Failed to allocate memory for text node value in CTextNode") - .into_raw(), - font: CString::new(text_node.font.clone().into_bytes()) - .expect("Failed to allocate memory for text node font in CTextNode") - .into_raw(), - font_size: text_node.font_size, - is_bold: text_node.is_bold, - } - } -} diff --git a/crates/gosub_bindings/tests/rendertree_test.c b/crates/gosub_bindings/tests/rendertree_test.c deleted file mode 100644 index dd8104214..000000000 --- a/crates/gosub_bindings/tests/rendertree_test.c +++ /dev/null @@ -1,121 +0,0 @@ -#include "gosub_api.h" -#include -#include -#include -#include - -int main() { - const char *html = "" - "

this is heading 1

" - "

this is heading 2

" - "

this is heading 3

" - "

this is heading 4

" - "
this is heading 5
" - "
this is heading 6
" - "

this is a paragraph

" - ""; - struct rendertree_t rendertree; - assert(rendertree_init(&rendertree, html) == 0); - - const struct node_t *node = NULL; - - // - node = rendertree_next(&rendertree); - assert(node->type == NODE_TYPE_ROOT); - - const double tol = 0.00001; - - double y = 0.00; - - //

- node = rendertree_next(&rendertree); - y += rendertree_node_get_margin_top(node); - assert(node->type == NODE_TYPE_TEXT); - assert(strcmp(rendertree_node_text_get_value(node), "this is heading 1") == 0); - assert(strcmp(rendertree_node_text_get_font(node), "Times New Roman") == 0); - assert(fabs(rendertree_node_text_get_font_size(node) - 37.0) < tol); - assert(rendertree_node_text_get_bold(node) == true); - assert(fabs(rendertree_node_get_x(node) - 0.00) < tol); - assert(fabs(rendertree_node_get_y(node) - y) < tol); - y += (rendertree_node_text_get_font_size(node) + rendertree_node_get_margin_bottom(node)); - - //

- node = rendertree_next(&rendertree); - y += rendertree_node_get_margin_top(node); - assert(node->type == NODE_TYPE_TEXT); - assert(strcmp(rendertree_node_text_get_value(node), "this is heading 2") == 0); - assert(strcmp(rendertree_node_text_get_font(node), "Times New Roman") == 0); - assert(fabs(rendertree_node_text_get_font_size(node) - 27.5) < tol); - assert(rendertree_node_text_get_bold(node) == true); - assert(fabs(rendertree_node_get_x(node) - 0.00) < tol); - assert(fabs(rendertree_node_get_y(node) - y) < tol); - y += (rendertree_node_text_get_font_size(node) + rendertree_node_get_margin_bottom(node)); - - //

- node = rendertree_next(&rendertree); - y += rendertree_node_get_margin_top(node); - assert(node->type == NODE_TYPE_TEXT); - assert(strcmp(rendertree_node_text_get_value(node), "this is heading 3") == 0); - assert(strcmp(rendertree_node_text_get_font(node), "Times New Roman") == 0); - assert(fabs(rendertree_node_text_get_font_size(node) - 21.5) < tol); - assert(rendertree_node_text_get_bold(node) == true); - assert(fabs(rendertree_node_get_x(node) - 0.00) < tol); - assert(fabs(rendertree_node_get_y(node) - y) < tol); - y += (rendertree_node_text_get_font_size(node) + rendertree_node_get_margin_bottom(node)); - - //

- node = rendertree_next(&rendertree); - y += rendertree_node_get_margin_top(node); - assert(node->type == NODE_TYPE_TEXT); - assert(strcmp(rendertree_node_text_get_value(node), "this is heading 4") == 0); - assert(strcmp(rendertree_node_text_get_font(node), "Times New Roman") == 0); - assert(fabs(rendertree_node_text_get_font_size(node) - 18.5) < tol); - assert(rendertree_node_text_get_bold(node) == true); - assert(fabs(rendertree_node_get_x(node) - 0.00) < tol); - assert(fabs(rendertree_node_get_y(node) - y) < tol); - y += (rendertree_node_text_get_font_size(node) + rendertree_node_get_margin_bottom(node)); - - //

- node = rendertree_next(&rendertree); - y += rendertree_node_get_margin_top(node); - assert(node->type == NODE_TYPE_TEXT); - assert(strcmp(rendertree_node_text_get_value(node), "this is heading 5") == 0); - assert(strcmp(rendertree_node_text_get_font(node), "Times New Roman") == 0); - assert(fabs(rendertree_node_text_get_font_size(node) - 15.5) < tol); - assert(rendertree_node_text_get_bold(node) == true); - assert(fabs(rendertree_node_get_x(node) - 0.00) < tol); - assert(fabs(rendertree_node_get_y(node) - y) < tol); - y += (rendertree_node_text_get_font_size(node) + rendertree_node_get_margin_bottom(node)); - - //
- node = rendertree_next(&rendertree); - y += rendertree_node_get_margin_top(node); - assert(node->type == NODE_TYPE_TEXT); - assert(strcmp(rendertree_node_text_get_value(node), "this is heading 6") == 0); - assert(strcmp(rendertree_node_text_get_font(node), "Times New Roman") == 0); - assert(fabs(rendertree_node_text_get_font_size(node) - 12.0) < tol); - assert(rendertree_node_text_get_bold(node) == true); - assert(fabs(rendertree_node_get_x(node) - 0.00) < tol); - assert(fabs(rendertree_node_get_y(node) - y) < tol); - y += (rendertree_node_text_get_font_size(node) + rendertree_node_get_margin_bottom(node)); - - //

- node = rendertree_next(&rendertree); - y += rendertree_node_get_margin_top(node); - assert(node->type == NODE_TYPE_TEXT); - assert(strcmp(rendertree_node_text_get_value(node), "this is a paragraph") == 0); - assert(strcmp(rendertree_node_text_get_font(node), "Times New Roman") == 0); - assert(fabs(rendertree_node_text_get_font_size(node) - 18.5) < tol); - assert(rendertree_node_text_get_bold(node) == false); - assert(fabs(rendertree_node_get_x(node) - 0.00) < tol); - assert(fabs(rendertree_node_get_y(node) - y) < tol); - - // end of iterator, last node is free'd - node = rendertree_next(&rendertree); - assert(node == NULL); - - rendertree_free(&rendertree); - - printf("\033[0;32mrendertree_test.c: All assertions passed\n\033[0;30m"); - return 0; -} diff --git a/crates/gosub_config/Cargo.toml b/crates/gosub_config/Cargo.toml index 4cb6a1cdb..3d8ba12dd 100644 --- a/crates/gosub_config/Cargo.toml +++ b/crates/gosub_config/Cargo.toml @@ -2,6 +2,9 @@ name = "gosub_config" version = "0.1.0" edition = "2021" +authors = ["Gosub Community "] +license = "MIT" + [dependencies] gosub_shared = { path = "../gosub_shared", features = [] } diff --git a/crates/gosub_config/src/lib.rs b/crates/gosub_config/src/lib.rs index 40e5f9472..4129e4918 100644 --- a/crates/gosub_config/src/lib.rs +++ b/crates/gosub_config/src/lib.rs @@ -166,11 +166,7 @@ impl ConfigStore { // Find all keys, and add them to the configuration store if let Ok(all_settings) = self.storage.all() { for (key, value) in all_settings { - self.settings - .lock() - .unwrap() - .borrow_mut() - .insert(key, value); + self.settings.lock().unwrap().borrow_mut().insert(key, value); } } } @@ -258,14 +254,12 @@ impl ConfigStore { /// Populates the settings in the storage from the settings.json file fn populate_default_settings(&mut self) -> Result<()> { - let json_data: Value = - serde_json::from_str(SETTINGS_JSON).expect("Failed to parse settings.json"); + let json_data: Value = serde_json::from_str(SETTINGS_JSON).expect("Failed to parse settings.json"); if let Value::Object(data) = json_data { for (section_prefix, section_entries) in &data { let section_entries: Vec = - serde_json::from_value(section_entries.clone()) - .expect("Failed to parse settings.json"); + serde_json::from_value(section_entries.clone()).expect("Failed to parse settings.json"); for entry in section_entries { let key = format!("{}.{}", section_prefix, entry.key); @@ -317,10 +311,7 @@ mod test { assert_eq!(captured_logs.len(), 0); }); - config_store_write().set( - "dns.local.enabled", - Setting::String("wont accept strings".into()), - ); + config_store_write().set("dns.local.enabled", Setting::String("wont accept strings".into())); testing_logger::validate(|captured_logs| { assert_eq!(captured_logs.len(), 1); diff --git a/crates/gosub_config/src/settings.rs b/crates/gosub_config/src/settings.rs index 56be2af6e..ec00529dd 100644 --- a/crates/gosub_config/src/settings.rs +++ b/crates/gosub_config/src/settings.rs @@ -118,8 +118,7 @@ impl<'de> Deserialize<'de> for Setting { D: Deserializer<'de>, { let value = String::deserialize(deserializer)?; - Setting::from_str(&value) - .map_err(|err| serde::de::Error::custom(format!("cannot deserialize: {err}"))) + Setting::from_str(&value).map_err(|err| serde::de::Error::custom(format!("cannot deserialize: {err}"))) } } @@ -241,10 +240,7 @@ mod test { assert_eq!(vec!("hello world"), s.to_map()); let s = Setting::from_str("m:foo,bar,baz").unwrap(); - assert_eq!( - s, - Setting::Map(vec!["foo".into(), "bar".into(), "baz".into()]) - ); + assert_eq!(s, Setting::Map(vec!["foo".into(), "bar".into(), "baz".into()])); assert!(s.to_bool()); assert_eq!(3, s.to_sint()); assert_eq!(3, s.to_uint()); diff --git a/crates/gosub_config/src/storage/json.rs b/crates/gosub_config/src/storage/json.rs index be43b5e62..967e81f99 100644 --- a/crates/gosub_config/src/storage/json.rs +++ b/crates/gosub_config/src/storage/json.rs @@ -105,9 +105,7 @@ impl JsonStorageAdapter { let json = serde_json::to_string_pretty(&self.elements).expect("failed to serialize"); file.set_len(0).expect("failed to truncate file"); - file.seek(std::io::SeekFrom::Start(0)) - .expect("failed to seek"); - file.write_all(json.as_bytes()) - .expect("failed to write file"); + file.seek(std::io::SeekFrom::Start(0)).expect("failed to seek"); + file.write_all(json.as_bytes()).expect("failed to write file"); } } diff --git a/crates/gosub_config/src/storage/sqlite.rs b/crates/gosub_config/src/storage/sqlite.rs index 8790f6283..16b23fc3a 100644 --- a/crates/gosub_config/src/storage/sqlite.rs +++ b/crates/gosub_config/src/storage/sqlite.rs @@ -52,9 +52,7 @@ impl StorageAdapter for SqliteStorageAdapter { let query = "INSERT OR REPLACE INTO settings (key, value) VALUES (:key, :value)"; let mut statement = db_lock.prepare(query).unwrap(); statement.bind((":key", key)).unwrap(); - statement - .bind((":value", value.to_string().as_str())) - .unwrap(); + statement.bind((":value", value.to_string().as_str())).unwrap(); statement.next().unwrap(); } diff --git a/crates/gosub_css3/Cargo.toml b/crates/gosub_css3/Cargo.toml index 4245b8ab1..a674f7ed7 100644 --- a/crates/gosub_css3/Cargo.toml +++ b/crates/gosub_css3/Cargo.toml @@ -7,8 +7,16 @@ license = "MIT" [dependencies] gosub_shared = { path = "../gosub_shared", features = [] } -lazy_static = "1.5" +lazy_static = "1.5.0" log = "0.4.22" simple_logger = "5.0.0" anyhow = { version = "1.0.89", features = [] } -colors-transform = "0.2.11" \ No newline at end of file +colors-transform = "0.2.11" +rand = "0.8.5" +itertools = "0.13.0" + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +serde = { version = "1.0.210", features = ["derive"] } +serde_json = "1.0.128" +thiserror = "1.0.63" +nom = "7.1.3" diff --git a/crates/gosub_styling/resources/definitions/definitions.json b/crates/gosub_css3/resources/definitions/definitions.json similarity index 100% rename from crates/gosub_styling/resources/definitions/definitions.json rename to crates/gosub_css3/resources/definitions/definitions.json diff --git a/crates/gosub_styling/resources/definitions/definitions_at-rules.json b/crates/gosub_css3/resources/definitions/definitions_at-rules.json similarity index 100% rename from crates/gosub_styling/resources/definitions/definitions_at-rules.json rename to crates/gosub_css3/resources/definitions/definitions_at-rules.json diff --git a/crates/gosub_styling/resources/definitions/definitions_properties.json b/crates/gosub_css3/resources/definitions/definitions_properties.json similarity index 100% rename from crates/gosub_styling/resources/definitions/definitions_properties.json rename to crates/gosub_css3/resources/definitions/definitions_properties.json diff --git a/crates/gosub_styling/resources/definitions/definitions_selectors.json b/crates/gosub_css3/resources/definitions/definitions_selectors.json similarity index 100% rename from crates/gosub_styling/resources/definitions/definitions_selectors.json rename to crates/gosub_css3/resources/definitions/definitions_selectors.json diff --git a/crates/gosub_styling/resources/definitions/definitions_values.json b/crates/gosub_css3/resources/definitions/definitions_values.json similarity index 100% rename from crates/gosub_styling/resources/definitions/definitions_values.json rename to crates/gosub_css3/resources/definitions/definitions_values.json diff --git a/crates/gosub_styling/resources/useragent.css b/crates/gosub_css3/resources/useragent.css similarity index 100% rename from crates/gosub_styling/resources/useragent.css rename to crates/gosub_css3/resources/useragent.css diff --git a/crates/gosub_css3/src/convert/ast_converter.rs b/crates/gosub_css3/src/ast.rs similarity index 79% rename from crates/gosub_css3/src/convert/ast_converter.rs rename to crates/gosub_css3/src/ast.rs index ba05aa2ca..c2fefe1f1 100644 --- a/crates/gosub_css3/src/convert/ast_converter.rs +++ b/crates/gosub_css3/src/ast.rs @@ -1,10 +1,10 @@ use crate::node::{Node as CssNode, NodeType}; use crate::stylesheet::{ - AttributeSelector, Combinator, CssDeclaration, CssOrigin, CssRule, CssSelector, - CssSelectorPart, CssStylesheet, CssValue, MatcherType, + AttributeSelector, Combinator, CssDeclaration, CssRule, CssSelector, CssSelectorPart, CssStylesheet, CssValue, + MatcherType, }; -use anyhow::anyhow; -use gosub_shared::types::Result; +use gosub_shared::errors::{CssError, CssResult}; +use gosub_shared::traits::css3::CssOrigin; use log::warn; /* @@ -60,19 +60,16 @@ vs */ /// Converts a CSS AST to a CSS stylesheet structure -pub fn convert_ast_to_stylesheet( - css_ast: &CssNode, - origin: CssOrigin, - location: &str, -) -> Result { +pub fn convert_ast_to_stylesheet(css_ast: &CssNode, origin: CssOrigin, url: &str) -> CssResult { if !css_ast.is_stylesheet() { - return Err(anyhow!("CSS AST must start with a stylesheet node")); + return Err(CssError::new("CSS AST must start with a stylesheet node")); } let mut sheet = CssStylesheet { rules: vec![], origin, - location: location.to_string(), + url: url.to_string(), + parse_log: vec![], }; for node in css_ast.as_stylesheet() { @@ -91,9 +88,7 @@ pub fn convert_ast_to_stylesheet( continue; } - let mut selector = CssSelector { - parts: vec![vec![]], - }; + let mut selector = CssSelector { parts: vec![vec![]] }; for node in node.as_selector_list().iter() { if !node.is_selector() { continue; @@ -111,24 +106,18 @@ pub fn convert_ast_to_stylesheet( " " => Combinator::Descendant, "||" => Combinator::Column, "|" => Combinator::Namespace, - _ => return Err(anyhow!("Unknown combinator: {}", value)), + _ => return Err(CssError::new(format!("Unknown combinator: {}", value).as_str())), }; CssSelectorPart::Combinator(combinator) } NodeType::IdSelector { value } => CssSelectorPart::Id(value.clone()), - NodeType::TypeSelector { value, .. } if value == "*" => { - CssSelectorPart::Universal - } - NodeType::PseudoClassSelector { value, .. } => { - CssSelectorPart::PseudoClass(value.to_string()) - } + NodeType::TypeSelector { value, .. } if value == "*" => CssSelectorPart::Universal, + NodeType::PseudoClassSelector { value, .. } => CssSelectorPart::PseudoClass(value.to_string()), NodeType::PseudoElementSelector { value, .. } => { CssSelectorPart::PseudoElement(value.to_string()) } - NodeType::TypeSelector { value, .. } => { - CssSelectorPart::Type(value.clone()) - } + NodeType::TypeSelector { value, .. } => CssSelectorPart::Type(value.clone()), NodeType::AttributeSelector { name, value, @@ -170,7 +159,9 @@ pub fn convert_ast_to_stylesheet( continue; } _ => { - return Err(anyhow!("Unsupported selector part: {:?}", node.node_type)) + return Err(CssError::new( + format!("Unsupported selector part: {:?}", node.node_type).as_str(), + )); } }; if let Some(x) = selector.parts.last_mut() { @@ -224,12 +215,12 @@ pub fn convert_ast_to_stylesheet( #[cfg(test)] mod tests { use super::*; - use crate::parser_config::ParserConfig; use crate::Css3; + use gosub_shared::traits::ParserConfig; #[test] fn convert_font_family() { - let ast = Css3::parse( + let stylesheet = Css3::parse_str( r#" body { border: 1px solid black; @@ -241,66 +232,42 @@ mod tests { } "#, ParserConfig::default(), + CssOrigin::User, + "test.css", ) .unwrap(); - let tree = convert_ast_to_stylesheet(&ast, CssOrigin::UserAgent, "test.css").unwrap(); - - dbg!(&tree); + dbg!(&stylesheet); } #[test] fn convert_test() { - let ast = Css3::parse( + let stylesheet = Css3::parse_str( r#" h1 { color: red; } h3, h4 { border: 1px solid black; } "#, ParserConfig::default(), + CssOrigin::User, + "test.css", ) .unwrap(); - let tree = convert_ast_to_stylesheet(&ast, CssOrigin::UserAgent, "test.css").unwrap(); - assert_eq!( - tree.rules - .first() - .unwrap() - .declarations - .first() - .unwrap() - .property, + stylesheet.rules.first().unwrap().declarations.first().unwrap().property, "color" ); assert_eq!( - tree.rules - .first() - .unwrap() - .declarations - .first() - .unwrap() - .value, + stylesheet.rules.first().unwrap().declarations.first().unwrap().value, vec![CssValue::String("red".into())] ); assert_eq!( - tree.rules - .get(1) - .unwrap() - .declarations - .first() - .unwrap() - .property, + stylesheet.rules.get(1).unwrap().declarations.first().unwrap().property, "border" ); assert_eq!( - tree.rules - .get(1) - .unwrap() - .declarations - .first() - .unwrap() - .value, + stylesheet.rules.get(1).unwrap().declarations.first().unwrap().value, vec![ CssValue::Unit(1.0, "px".into()), CssValue::String("solid".into()), diff --git a/crates/gosub_css3/src/colors.rs b/crates/gosub_css3/src/colors.rs index 7575cb3bc..75c576d75 100644 --- a/crates/gosub_css3/src/colors.rs +++ b/crates/gosub_css3/src/colors.rs @@ -76,12 +76,7 @@ impl From<&str> for RgbColor { return RgbColor::default(); } let rgb = rgb.unwrap(); - return RgbColor::new( - rgb.get_red(), - rgb.get_green(), - rgb.get_blue(), - rgb.get_alpha(), - ); + return RgbColor::new(rgb.get_red(), rgb.get_green(), rgb.get_blue(), rgb.get_alpha()); } if value.starts_with("hsl(") { let hsl = Hsl::from_str(value); @@ -99,12 +94,7 @@ impl From<&str> for RgbColor { return RgbColor::default(); } let rgb: Rgb = hsl.unwrap().to_rgb(); - return RgbColor::new( - rgb.get_red(), - rgb.get_green(), - rgb.get_blue(), - rgb.get_alpha(), - ); + return RgbColor::new(rgb.get_red(), rgb.get_green(), rgb.get_blue(), rgb.get_alpha()); } return get_hex_color_from_name(value).map_or(RgbColor::default(), parse_hex); @@ -146,12 +136,7 @@ fn parse_hex(value: &str) -> RgbColor { let r = i32::from_str_radix(&value[1..2], 16).unwrap(); let g = i32::from_str_radix(&value[2..3], 16).unwrap(); let b = i32::from_str_radix(&value[3..4], 16).unwrap(); - return RgbColor::new( - (r * 16 + r) as f32, - (g * 16 + g) as f32, - (b * 16 + b) as f32, - 255.0, - ); + return RgbColor::new((r * 16 + r) as f32, (g * 16 + g) as f32, (b * 16 + b) as f32, 255.0); } // 4 hex digits (RGBA) diff --git a/crates/gosub_css3/src/convert.rs b/crates/gosub_css3/src/convert.rs deleted file mode 100644 index 4637be32d..000000000 --- a/crates/gosub_css3/src/convert.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod ast_converter; diff --git a/crates/gosub_css3/src/errors.rs b/crates/gosub_css3/src/errors.rs new file mode 100644 index 000000000..e76800e83 --- /dev/null +++ b/crates/gosub_css3/src/errors.rs @@ -0,0 +1,35 @@ +//! Error results that can be returned from the css3 parser +// use gosub_shared::byte_stream::Location; + +// +// /// Parser error that defines an error (message) on the given position +// #[derive(Clone, Debug, PartialEq)] +// pub struct CssError { +// /// Error message +// pub message: String, +// /// Location of the error, if available (during parsing mostly) +// pub location: Option, +// } +// +// impl CssError { +// pub fn new(message: &str, location: Option) -> Self { +// Self { +// message: message.to_string(), +// location, +// } +// } +// } + +// /// Serious errors and errors from third-party libraries +// #[derive(Debug, Error)] +// pub enum Error { +// #[error("parse error: {0} at {1}")] +// Parse(String, Location), +// +// #[allow(dead_code)] +// #[error("incorrect value: {0} at {1}")] +// IncorrectValue(String, Location), +// +// #[error("css failure: {0}")] +// CssFailure(String), +// } diff --git a/crates/gosub_css3/src/functions.rs b/crates/gosub_css3/src/functions.rs new file mode 100644 index 000000000..b8e327dd1 --- /dev/null +++ b/crates/gosub_css3/src/functions.rs @@ -0,0 +1,7 @@ +// pub use attr::*; +// pub use calc::*; +// pub use var::*; + +pub mod attr; +pub mod calc; +pub mod var; diff --git a/crates/gosub_css3/src/functions/attr.rs b/crates/gosub_css3/src/functions/attr.rs new file mode 100644 index 000000000..8de5951f8 --- /dev/null +++ b/crates/gosub_css3/src/functions/attr.rs @@ -0,0 +1,33 @@ +use crate::stylesheet::CssValue; +use gosub_shared::traits::css3::CssSystem; +use gosub_shared::traits::node::{ElementDataType, Node}; + +// Probably this shouldn't quite be in gosub_css3 +#[allow(dead_code)] +pub fn resolve_attr, C: CssSystem>(values: &[CssValue], node: &N) -> Vec { + let Some(attr_name) = values.first().map(|v| v.to_string()) else { + return vec![]; + }; + + let ty = values.get(1).cloned(); + + let Some(data) = node.get_element_data() else { + return vec![]; + }; + + let Some(attr_value) = data.attribute(&attr_name) else { + let _default_value = values.get(2).cloned(); + + if let Some(ty) = ty { + return vec![ty]; + } + + return vec![]; + }; + + let Ok(value) = CssValue::parse_str(attr_value) else { + return vec![]; + }; + + vec![value] +} diff --git a/crates/gosub_styling/src/functions/calc.rs b/crates/gosub_css3/src/functions/calc.rs similarity index 58% rename from crates/gosub_styling/src/functions/calc.rs rename to crates/gosub_css3/src/functions/calc.rs index 5e24bd79f..c40bfcf56 100644 --- a/crates/gosub_styling/src/functions/calc.rs +++ b/crates/gosub_css3/src/functions/calc.rs @@ -1,5 +1,6 @@ -use gosub_css3::stylesheet::CssValue; +use crate::stylesheet::CssValue; +#[allow(dead_code)] pub fn resolve_calc(_values: &[CssValue]) -> Vec { todo!() } diff --git a/crates/gosub_styling/src/functions/var.rs b/crates/gosub_css3/src/functions/var.rs similarity index 74% rename from crates/gosub_styling/src/functions/var.rs rename to crates/gosub_css3/src/functions/var.rs index 1180456ca..1de85d88e 100644 --- a/crates/gosub_styling/src/functions/var.rs +++ b/crates/gosub_css3/src/functions/var.rs @@ -1,16 +1,18 @@ use std::collections::HashMap; -use gosub_css3::stylesheet::CssValue; -use gosub_html5::node::Node; -use gosub_html5::parser::document::Document; +use crate::stylesheet::CssValue; +use gosub_shared::traits::css3::CssSystem; +use gosub_shared::traits::document::Document; +#[allow(dead_code)] #[derive(Clone, Debug, Default)] pub struct VariableEnvironment { pub values: HashMap, } +#[allow(dead_code)] impl VariableEnvironment { - pub fn get(&self, name: &str, _doc: &Document, _node: &Node) -> Option { + pub fn get, C: CssSystem>(&self, name: &str, _doc: &D, _node: &D::Node) -> Option { let mut current = Some(self); while let Some(env) = current { @@ -29,7 +31,8 @@ impl VariableEnvironment { } } -pub fn resolve_var(values: &[CssValue], doc: &Document, node: &Node) -> Vec { +#[allow(dead_code)] +pub fn resolve_var, C: CssSystem>(values: &[CssValue], doc: &D, node: &D::Node) -> Vec { let Some(name) = values.first().map(|v| { let mut str = v.to_string(); diff --git a/crates/gosub_css3/src/lib.rs b/crates/gosub_css3/src/lib.rs index 8c0a966f6..ab70d8cbf 100644 --- a/crates/gosub_css3/src/lib.rs +++ b/crates/gosub_css3/src/lib.rs @@ -1,98 +1,127 @@ -use crate::node::{Node, NodeType}; -use crate::parser_config::{Context, ParserConfig}; +use crate::ast::convert_ast_to_stylesheet; +use crate::stylesheet::CssStylesheet; use crate::tokenizer::Tokenizer; + use gosub_shared::byte_stream::{ByteStream, Encoding, Location}; +use gosub_shared::errors::{CssError, CssResult}; +use gosub_shared::traits::css3::CssOrigin; +use gosub_shared::traits::Context; +use gosub_shared::traits::ParserConfig; use gosub_shared::{timing_start, timing_stop}; +pub mod ast; +/// This CSS3 parser is heavily based on the MIT licensed CssTree parser written by +/// Roman Dvornov (https://github.com/lahmatiy). +/// The original version can be found at https://github.com/csstree/csstree pub mod colors; -pub mod convert; -mod node; +pub mod errors; +mod functions; +#[allow(dead_code)] +pub mod matcher; +pub mod node; pub mod parser; -pub mod parser_config; pub mod stylesheet; +pub mod system; pub mod tokenizer; mod unicode; pub mod walker; -/// This CSS3 parser is heavily based on the MIT licensed CssTree parser written by -/// Roman Dvornov (https://github.com/lahmatiy). -/// The original version can be found at https://github.com/csstree/csstree - pub struct Css3<'stream> { /// The tokenizer is responsible for reading the input stream and pub tokenizer: Tokenizer<'stream>, - /// When the last item is true, we allow values in argument lists. + /// When true, we allow values in argument lists. allow_values_in_argument_list: Vec, /// The parser configuration as given config: ParserConfig, -} - -#[derive(Debug)] -pub struct Error { - /// The error message - pub message: String, - /// The location of the error - pub location: Location, -} - -impl Error { - pub(crate) fn new(message: String, location: Location) -> Error { - Error { message, location } - } + /// Origin of the stream (useragent, inline etc.) + origin: CssOrigin, + /// Source of the stream (filename, url, etc.) + source: String, } impl<'stream> Css3<'stream> { - /// Parse a CSS string, which depends on the context. - pub fn parse(data: &str, config: ParserConfig) -> Result { - let t_id = timing_start!("css3.parse", config.source.as_deref().unwrap_or("")); + /// Creates a new parser with the given byte stream so only parse() needs to be called. + fn new(stream: &'stream mut ByteStream, config: ParserConfig, origin: CssOrigin, source: &str) -> Self { + Self { + tokenizer: Tokenizer::new(stream, Location::default()), + allow_values_in_argument_list: Vec::new(), + config, + origin, + source: source.to_string(), + } + } + /// Parses a direct string to a CssStyleSheet + pub fn parse_str( + data: &str, + config: ParserConfig, + origin: CssOrigin, + source_url: &str, + ) -> CssResult { let mut stream = ByteStream::new(Encoding::UTF8, None); stream.read_from_str(data, Some(Encoding::UTF8)); stream.close(); - let mut parser = Css3::new(&mut stream); - let result = parser.parse_internal(config); - - timing_stop!(t_id); + Css3::parse_stream(&mut stream, config, origin, source_url) + } - match result { - Ok(Some(node)) => Ok(node), - Ok(None) => Ok(Node::new( - NodeType::StyleSheet { - children: Vec::new(), - }, - Location::default(), - )), - Err(e) => Err(e), - } + /// Parses a direct stream to a CssStyleSheet + pub fn parse_stream( + stream: &mut ByteStream, + config: ParserConfig, + origin: CssOrigin, + source_url: &str, + ) -> CssResult { + Css3::new(stream, config, origin, source_url).parse() } - /// Create a new parser with the given bytestream - fn new(it: &'stream mut ByteStream) -> Self { - Self { - tokenizer: Tokenizer::new(it, Location::default()), - allow_values_in_argument_list: Vec::new(), - config: Default::default(), + fn parse(&mut self) -> CssResult { + if self.config.context != Context::Stylesheet { + return Err(CssError::new("Expected a stylesheet context")); } - } - /// Actual parser implementation - fn parse_internal(&mut self, config: ParserConfig) -> Result, Error> { - self.config = config; + let t_id = timing_start!("css3.parse", self.config.source.as_deref().unwrap_or("")); - match self.config.context { - Context::Stylesheet => self.parse_stylesheet(), + // let mut stream = ByteStream::new(Encoding::UTF8, None); + // stream.read_from_str(data, Some(Encoding::UTF8)); + // stream.close(); + + let node_tree = match self.config.context { + Context::Stylesheet => self.parse_stylesheet_internal(), Context::Rule => self.parse_rule(), Context::AtRule => self.parse_at_rule(true), Context::Declaration => self.parse_declaration(), + }; + + timing_stop!(t_id); + + match node_tree { + Ok(None) => Err(CssError::new("No node tree found")), + Ok(Some(node)) => convert_ast_to_stylesheet(&node, self.origin, self.source.clone().as_str()), + Err(e) => Err(e), } } } +/// Loads the default user agent stylesheet +pub fn load_default_useragent_stylesheet() -> CssStylesheet { + // @todo: we should be able to browse to gosub:useragent.css and see the actual useragent css file + let url = "gosub:useragent.css"; + + let config = ParserConfig { + ignore_errors: true, + match_values: true, + ..Default::default() + }; + + let css_data = include_str!("../resources/useragent.css"); + Css3::parse_str(css_data, config, CssOrigin::UserAgent, url).expect("Could not parse useragent stylesheet") +} + #[cfg(test)] mod tests { use super::*; - use crate::walker::Walker; + // use crate::walker::Walker; use simple_logger::SimpleLogger; #[test] @@ -109,14 +138,13 @@ mod tests { }; let css = std::fs::read_to_string(filename).unwrap(); - let res = Css3::parse(css.as_str(), config); + let res = Css3::parse_str(css.as_str(), config, CssOrigin::Author, filename); if res.is_err() { println!("{:?}", res.err().unwrap()); - return; } - let binding = res.unwrap(); - let w = Walker::new(&binding); - w.walk_stdout(); + // let binding = res.unwrap(); + // let w = Walker::new(&binding); + // w.walk_stdout(); } } diff --git a/crates/gosub_css3/src/logging.rs b/crates/gosub_css3/src/logging.rs new file mode 100644 index 000000000..a1d6032c6 --- /dev/null +++ b/crates/gosub_css3/src/logging.rs @@ -0,0 +1,528 @@ +use crate::colors::RgbColor; +use anyhow::anyhow; +use core::fmt::Debug; +use std::cmp::Ordering; +use std::fmt::Display; +use gosub_shared::byte_stream::Location; + +/// Defines a complete stylesheet with all its rules and the location where it was found +#[derive(Debug)] +pub struct CssStylesheet { + /// List of rules found in this stylesheet + pub rules: Vec, + /// Origin of the stylesheet (user agent, author, user) + pub origin: CssOrigin, + /// Url or file path where the stylesheet was found + pub location: String, + /// Any issues during parsing of the stylesheet + pub parse_log: Vec +} + +/// A CSS rule, which contains a list of selectors and a list of declarations +#[derive(Debug, PartialEq, Clone)] +pub struct CssRule { + /// Selectors that must match for the declarations to apply + pub selectors: Vec, + /// Actual declarations that will be applied if the selectors match + pub declarations: Vec, +} + +impl CssRule { + pub fn selectors(&self) -> &Vec { + &self.selectors + } + + pub fn declarations(&self) -> &Vec { + &self.declarations + } +} + +/// A CSS declaration, which contains a property, value and a flag for !important +#[derive(Debug, PartialEq, Clone)] +pub struct CssDeclaration { + // Css property color + pub property: String, + // Raw values of the declaration. It is not calculated or converted in any way (ie: "red", "50px" etc) + // There can be multiple values (ie: "1px solid black" are split into 3 values) + pub value: Vec, + // ie: !important + pub important: bool, +} + +#[derive(Debug, PartialEq, Clone)] +pub struct CssSelector { + // List of parts that make up this selector + pub parts: Vec, +} + +impl CssSelector { + /// Generate specificity for this selector + pub fn specificity(&self) -> Specificity { + let mut id_count = 0; + let mut class_count = 0; + let mut element_count = 0; + for part in &self.parts { + match part.type_ { + CssSelectorType::Id => { + id_count += 1; + } + CssSelectorType::Class => { + class_count += 1; + } + CssSelectorType::Type => { + element_count += 1; + } + _ => {} + } + } + Specificity::new(id_count, class_count, element_count) + } +} + +/// @todo: it would be nicer to have a struct for each type of selector part, but for now we'll keep it simple +/// Represents a CSS selector part, which has a type and value (e.g. type=Class, class="my-class") +#[derive(PartialEq, Clone, Default)] +pub struct CssSelectorPart { + pub type_: CssSelectorType, + pub value: String, + pub matcher: MatcherType, + pub name: String, + pub flags: String, +} + +impl Debug for CssSelectorPart { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self.type_ { + CssSelectorType::Universal => { + write!(f, "*") + } + CssSelectorType::Attribute => { + write!( + f, + "[{} {} {} {}]", + self.name, self.matcher, self.value, self.flags + ) + } + CssSelectorType::Class => { + write!(f, ".{}", self.value) + } + CssSelectorType::Id => { + write!(f, "#{}", self.value) + } + CssSelectorType::PseudoClass => { + write!(f, ":{}", self.value) + } + CssSelectorType::PseudoElement => { + write!(f, "::{}", self.value) + } + CssSelectorType::Combinator => { + write!(f, "'{}'", self.value) + } + CssSelectorType::Type => { + write!(f, "{}", self.value) + } + } + } +} + +/// Represents a CSS selector type for this part +#[derive(Debug, PartialEq, Clone, Default)] +pub enum CssSelectorType { + Universal, // '*' + #[default] + Type, // ul, a, h1, etc + Attribute, // [type ~= "text" i] (name, matcher, value, flags) + Class, // .myclass + Id, // #myid + PseudoClass, // :hover, :active + PseudoElement, // ::first-child + Combinator, +} + +/// Represents which type of matcher is used (in case of an attribute selector type) +#[derive(Default, PartialEq, Clone)] +pub enum MatcherType { + #[default] + None, // No matcher + Equals, // Equals + Includes, // Must include + DashMatch, // Must start with + PrefixMatch, // Must begin with + SuffixMatch, // Must ends with + SubstringMatch, // Must contain +} + +impl Display for MatcherType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + MatcherType::None => write!(f, ""), + MatcherType::Equals => write!(f, "="), + MatcherType::Includes => write!(f, "~="), + MatcherType::DashMatch => write!(f, "|="), + MatcherType::PrefixMatch => write!(f, "^="), + MatcherType::SuffixMatch => write!(f, "$="), + MatcherType::SubstringMatch => write!(f, "*="), + } + } +} + +/// Defines the specificity for a selector +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Specificity(u32, u32, u32); + +impl Specificity { + pub fn new(a: u32, b: u32, c: u32) -> Self { + Self(a, b, c) + } +} + +impl PartialOrd for Specificity { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Specificity { + fn cmp(&self, other: &Self) -> Ordering { + match self.0.cmp(&other.0) { + Ordering::Greater => Ordering::Greater, + Ordering::Less => Ordering::Less, + Ordering::Equal => match self.1.cmp(&other.1) { + Ordering::Greater => Ordering::Greater, + Ordering::Less => Ordering::Less, + Ordering::Equal => match self.2.cmp(&other.2) { + Ordering::Greater => Ordering::Greater, + Ordering::Less => Ordering::Less, + Ordering::Equal => Ordering::Equal, + }, + }, + } + } +} + +/// Actual CSS value, can be a color, length, percentage, string or unit. Some relative values will be computed +/// from other values (ie: Percent(50) will convert to Length(100) when the parent width is 200) +#[derive(Debug, Clone, PartialEq)] +pub enum CssValue { + None, + Color(RgbColor), + Zero, + Number(f32), + Percentage(f32), + String(String), + Unit(f32, String), + Function(String, Vec), + Initial, + Inherit, + Comma, + List(Vec), +} + +impl Display for CssValue { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + CssValue::None => write!(f, "none"), + CssValue::Color(col) => { + write!( + f, + "#{:02x}{:02x}{:02x}{:02x}", + col.r as u8, col.g as u8, col.b as u8, col.a as u8 + ) + } + CssValue::Zero => write!(f, "0"), + CssValue::Number(num) => write!(f, "{}", num), + CssValue::Percentage(p) => write!(f, "{}%", p), + CssValue::String(s) => write!(f, "{}", s), + CssValue::Unit(val, unit) => write!(f, "{}{}", val, unit), + CssValue::Function(name, args) => { + write!(f, "{}(", name)?; + for (i, arg) in args.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + write!(f, "{}", arg)?; + } + write!(f, ")") + } + CssValue::Initial => write!(f, "initial"), + CssValue::Inherit => write!(f, "inherit"), + CssValue::Comma => write!(f, ","), + CssValue::List(v) => { + write!(f, "List(")?; + for (i, value) in v.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + write!(f, "{}", value)?; + } + write!(f, ")") + } + } + } +} + +impl CssValue { + pub fn to_color(&self) -> Option { + match self { + CssValue::Color(col) => Some(*col), + CssValue::String(s) => Some(RgbColor::from(s.as_str())), + _ => None, + } + } + + pub fn unit_to_px(&self) -> f32 { + //TODO: Implement the rest of the units + match self { + CssValue::Unit(val, unit) => match unit.as_str() { + "px" => *val, + "em" => *val * 16.0, + "rem" => *val * 16.0, + _ => *val, + }, + CssValue::String(value) => { + if value.ends_with("px") { + value.trim_end_matches("px").parse::().unwrap() + } else if value.ends_with("rem") { + value.trim_end_matches("rem").parse::().unwrap() * 16.0 + } else if value.ends_with("em") { + value.trim_end_matches("em").parse::().unwrap() * 16.0 + } else { + 0.0 + } + } + _ => 0.0, + } + } + + /// Converts a CSS AST node to a CSS value + pub fn parse_ast_node(node: &crate::node::Node) -> CssResult { + match *node.node_type.clone() { + crate::node::NodeType::Ident { value } => Ok(CssValue::String(value)), + crate::node::NodeType::Number { value } => { + if value == 0.0 { + // Zero is a special case since we need to do some pattern matching once in a while, and + // this is not possible (anymore) with floating point 0.0 it seems + Ok(CssValue::Zero) + } else { + Ok(CssValue::Number(value)) + } + } + crate::node::NodeType::Percentage { value } => Ok(CssValue::Percentage(value)), + crate::node::NodeType::Dimension { value, unit } => Ok(CssValue::Unit(value, unit)), + crate::node::NodeType::String { value } => Ok(CssValue::String(value)), + crate::node::NodeType::Hash { value } => Ok(CssValue::String(value)), + crate::node::NodeType::Operator(_) => Ok(CssValue::None), + crate::node::NodeType::Calc { .. } => { + Ok(CssValue::Function("calc".to_string(), vec![])) + } + crate::node::NodeType::Url { url } => Ok(CssValue::Function( + "url".to_string(), + vec![CssValue::String(url)], + )), + crate::node::NodeType::Function { name, arguments } => { + let mut list = vec![]; + for node in arguments.iter() { + match CssValue::parse_ast_node(node) { + Ok(value) => list.push(value), + Err(e) => return Err(e), + } + } + Ok(CssValue::Function(name, list)) + } + _ => Err(anyhow!(format!( + "Cannot convert node to CssValue: {:?}", + node + ))), + } + } + + /// Parses a string into a CSS value or list of css values + pub fn parse_str(value: &str) -> CssResult { + match value { + "initial" => return Ok(CssValue::Initial), + "inherit" => return Ok(CssValue::Inherit), + "none" => return Ok(CssValue::None), + "" => return Ok(CssValue::String("".into())), + _ => {} + } + + if let Ok(num) = value.parse::() { + return Ok(CssValue::Number(num)); + } + + // Color values + if value.starts_with("color(") && value.ends_with(')') { + return Ok(CssValue::Color(RgbColor::from( + value[6..value.len() - 1].to_string().as_str(), + ))); + } + + // Percentages + if value.ends_with('%') { + if let Ok(num) = value[0..value.len() - 1].parse::() { + return Ok(CssValue::Percentage(num)); + } + } + + // units. If the value starts with a number and ends with some non-numerical + let mut split_index = None; + for (index, char) in value.chars().enumerate() { + if char.is_alphabetic() { + split_index = Some(index); + break; + } + } + if let Some(index) = split_index { + let (number_part, unit_part) = value.split_at(index); + if let Ok(number) = number_part.parse::() { + return Ok(CssValue::Unit(number, unit_part.to_string())); + } + } + + Ok(CssValue::String(value.to_string())) + } +} + +#[cfg(test)] +mod test { + use super::*; + + // #[test] + // fn test_css_value_to_color() { + // assert_eq!(CssValue::from_str("color(#ff0000)").unwrap().to_color().unwrap(), RgbColor::from("#ff0000")); + // assert_eq!(CssValue::from_str("'Hello'").unwrap().to_color().unwrap(), RgbColor::from("#000000")); + // } + // + // #[test] + // fn test_css_value_unit_to_px() { + // assert_eq!(CssValue::from_str("10px").unwrap().unit_to_px(), 10.0); + // assert_eq!(CssValue::from_str("10em").unwrap().unit_to_px(), 160.0); + // assert_eq!(CssValue::from_str("10rem").unwrap().unit_to_px(), 160.0); + // assert_eq!(CssValue::from_str("10").unwrap().unit_to_px(), 0.0); + // } + + #[test] + fn test_css_rule() { + let rule = CssRule { + selectors: vec![CssSelector { + parts: vec![CssSelectorPart { + type_: CssSelectorType::Type, + value: "h1".to_string(), + ..Default::default() + }], + }], + declarations: vec![CssDeclaration { + property: "color".to_string(), + value: vec![CssValue::String("red".to_string())], + important: false, + }], + }; + + assert_eq!(rule.selectors().len(), 1); + assert_eq!( + rule.selectors() + .first() + .unwrap() + .parts + .first() + .unwrap() + .value, + "h1" + ); + assert_eq!(rule.declarations().len(), 1); + assert_eq!(rule.declarations().first().unwrap().property, "color"); + } + + #[test] + fn test_specificity() { + let selector = CssSelector { + parts: vec![ + CssSelectorPart { + type_: CssSelectorType::Type, + value: "h1".to_string(), + ..Default::default() + }, + CssSelectorPart { + type_: CssSelectorType::Class, + value: "myclass".to_string(), + ..Default::default() + }, + CssSelectorPart { + type_: CssSelectorType::Id, + value: "myid".to_string(), + ..Default::default() + }, + ], + }; + + let specificity = selector.specificity(); + assert_eq!(specificity, Specificity::new(1, 1, 1)); + + let selector = CssSelector { + parts: vec![ + CssSelectorPart { + type_: CssSelectorType::Type, + value: "h1".to_string(), + ..Default::default() + }, + CssSelectorPart { + type_: CssSelectorType::Class, + value: "myclass".to_string(), + ..Default::default() + }, + ], + }; + + let specificity = selector.specificity(); + assert_eq!(specificity, Specificity::new(0, 1, 1)); + + let selector = CssSelector { + parts: vec![CssSelectorPart { + type_: CssSelectorType::Type, + value: "h1".to_string(), + ..Default::default() + }], + }; + + let specificity = selector.specificity(); + assert_eq!(specificity, Specificity::new(0, 0, 1)); + + let selector = CssSelector { + parts: vec![ + CssSelectorPart { + type_: CssSelectorType::Class, + value: "myclass".to_string(), + ..Default::default() + }, + CssSelectorPart { + type_: CssSelectorType::Class, + value: "otherclass".to_string(), + ..Default::default() + }, + ], + }; + + let specificity = selector.specificity(); + assert_eq!(specificity, Specificity::new(0, 2, 0)); + } + + #[test] + fn test_specificity_ordering() { + let specificity1 = Specificity::new(1, 1, 1); + let specificity2 = Specificity::new(0, 1, 1); + let specificity3 = Specificity::new(0, 0, 1); + let specificity4 = Specificity::new(0, 2, 0); + let specificity5 = Specificity::new(1, 0, 0); + let specificity6 = Specificity::new(1, 2, 1); + let specificity7 = Specificity::new(1, 1, 2); + let specificity8 = Specificity::new(2, 1, 1); + + assert!(specificity1 > specificity2); + assert!(specificity2 > specificity3); + assert!(specificity3 < specificity4); + assert!(specificity4 < specificity5); + assert!(specificity5 < specificity6); + assert!(specificity6 > specificity7); + assert!(specificity7 < specificity8); + } +} diff --git a/crates/gosub_css3/src/matcher.rs b/crates/gosub_css3/src/matcher.rs new file mode 100644 index 000000000..299733b55 --- /dev/null +++ b/crates/gosub_css3/src/matcher.rs @@ -0,0 +1,6 @@ +pub mod property_definitions; +pub mod shorthands; +pub mod styling; +mod syntax; +mod syntax_matcher; +mod walker; diff --git a/crates/gosub_styling/src/property_definitions.rs b/crates/gosub_css3/src/matcher/property_definitions.rs similarity index 92% rename from crates/gosub_styling/src/property_definitions.rs rename to crates/gosub_css3/src/matcher/property_definitions.rs index 2ed361366..41fde9c71 100644 --- a/crates/gosub_styling/src/property_definitions.rs +++ b/crates/gosub_css3/src/matcher/property_definitions.rs @@ -1,15 +1,13 @@ use std::collections::HashMap; +use std::sync::LazyLock; use log::warn; -use gosub_css3::stylesheet::CssValue; - -use crate::shorthands::{FixList, Shorthands}; -use std::sync::LazyLock; - -use crate::syntax::GroupCombinators::Juxtaposition; -use crate::syntax::{CssSyntax, SyntaxComponent}; -use crate::syntax_matcher::CssSyntaxTree; +use crate::matcher::shorthands::{FixList, Shorthands}; +use crate::matcher::syntax::GroupCombinators::Juxtaposition; +use crate::matcher::syntax::{CssSyntax, SyntaxComponent}; +use crate::matcher::syntax_matcher::CssSyntaxTree; +use crate::stylesheet::CssValue; /// List of elements that are built-in data types in the CSS specification. These will be handled /// by the syntax matcher as built-in types. @@ -240,23 +238,16 @@ impl CssDefinitions { } /// Resolve a syntax component - pub fn resolve_component( - &mut self, - component: &SyntaxComponent, - prop_name: &str, - ) -> SyntaxComponent { + pub fn resolve_component(&mut self, component: &SyntaxComponent, prop_name: &str) -> SyntaxComponent { match component { SyntaxComponent::Definition { - datatype, - multipliers, - .. + datatype, multipliers, .. } => { // First step: Resolve by looking the definition up in the syntax defintions. if let Some(syntax_element) = self.syntax.get(datatype) { let mut syntax_element = syntax_element.clone(); if !syntax_element.resolved { - syntax_element.syntax = - self.resolve_syntax(&syntax_element.syntax, prop_name); + syntax_element.syntax = self.resolve_syntax(&syntax_element.syntax, prop_name); syntax_element.resolved = true; self.syntax.insert(datatype.clone(), syntax_element.clone()); } @@ -348,21 +339,18 @@ impl CssDefinitions { } } -pub static CSS_DEFINITIONS: LazyLock CssDefinitions> = - LazyLock::new(pars_definition_files); +pub static CSS_DEFINITIONS: LazyLock CssDefinitions> = LazyLock::new(pars_definition_files); /// Parses the internal CSS definition file fn pars_definition_files() -> CssDefinitions { // parse all syntax, so we can use them in the properties - let contents = include_str!("../resources/definitions/definitions_values.json"); - let json: serde_json::Value = - serde_json::from_str(contents).expect("JSON was not well-formatted"); + let contents = include_str!("../../resources/definitions/definitions_values.json"); + let json: serde_json::Value = serde_json::from_str(contents).expect("JSON was not well-formatted"); let syntax = parse_syntax_file(json); // Parse property definitions - let contents = include_str!("../resources/definitions/definitions_properties.json"); - let json: serde_json::Value = - serde_json::from_str(contents).expect("JSON was not well-formatted"); + let contents = include_str!("../../resources/definitions/definitions_properties.json"); + let json: serde_json::Value = serde_json::from_str(contents).expect("JSON was not well-formatted"); let properties = parse_property_file(json); // Create definition structure, and resolve all definitions @@ -503,9 +491,8 @@ fn parse_property_file(json: serde_json::Value) -> HashMap { @@ -566,9 +553,7 @@ mod tests { .matches(&[str!("solid"), CssValue::Color(RgbColor::from("black")),])); assert_true!(prop.clone().matches(&[str!("solid")])); assert_false!(prop.clone().matches(&[str!("not-solid")])); - assert_false!(prop - .clone() - .matches(&[str!("solid"), str!("solid"), unit!(1.0, "px"),])); + assert_false!(prop.clone().matches(&[str!("solid"), str!("solid"), unit!(1.0, "px"),])); } #[test] @@ -579,9 +564,7 @@ mod tests { PropertyDefinition { name: "color".to_string(), computed: vec![], - syntax: CssSyntax::new("color()") - .compile() - .expect("Could not compile syntax"), + syntax: CssSyntax::new("color()").compile().expect("Could not compile syntax"), inherited: false, initial_value: None, resolved: false, @@ -604,9 +587,7 @@ mod tests { "border-bottom-style".to_string(), "border-left-style".to_string(), ], - syntax: CssSyntax::new("") - .compile() - .expect("Could not compile syntax"), + syntax: CssSyntax::new("").compile().expect("Could not compile syntax"), inherited: false, initial_value: Some(str!("thick".to_string())), resolved: false, @@ -669,9 +650,7 @@ mod tests { assert_true!(def.clone().matches(&[str!("Menu")])); assert_true!(def.clone().matches(&[str!("blue")])); - assert_true!(def - .clone() - .matches(&[CssValue::Color(RgbColor::from("#ff0000"))])); + assert_true!(def.clone().matches(&[CssValue::Color(RgbColor::from("#ff0000"))])); assert_true!(def.clone().matches(&[str!("rebeccapurple")])); assert_false!(def.clone().matches(&[str!("thiscolordoesnotexist")])); @@ -700,12 +679,9 @@ mod tests { assert_true!(def.clone().matches(&[str!("left"), unit!(10.0, "px"),])); // background-position: left 10px top 20px; - assert_true!(def.clone().matches(&[ - str!("left"), - unit!(10.0, "px"), - str!("top"), - unit!(20.0, "px"), - ])); + assert_true!(def + .clone() + .matches(&[str!("left"), unit!(10.0, "px"), str!("top"), unit!(20.0, "px"),])); // background-position: right 15% bottom 5%; assert_true!(def.clone().matches(&[ @@ -727,17 +703,12 @@ mod tests { assert_true!(def.clone().matches(&[CssValue::Percentage(75.0),])); // background-position: top 10px center; - assert_true!(def - .clone() - .matches(&[str!("top"), unit!(10.0, "px"), str!("center"),])); + assert_true!(def.clone().matches(&[str!("top"), unit!(10.0, "px"), str!("center"),])); // background-position: bottom 20px right 30px; - assert_true!(def.clone().matches(&[ - str!("bottom"), - unit!(20.0, "px"), - str!("right"), - unit!(30.0, "px"), - ])); + assert_true!(def + .clone() + .matches(&[str!("bottom"), unit!(20.0, "px"), str!("right"), unit!(30.0, "px"),])); // background-position: 20% 80%; assert_true!(def @@ -758,9 +729,7 @@ mod tests { ])); // background-position: center top 35px; - assert_true!(def - .clone() - .matches(&[str!("center"), str!("top"), unit!(35.0, "px"),])); + assert_true!(def.clone().matches(&[str!("center"), str!("top"), unit!(35.0, "px"),])); // background-position: left 45% bottom 25%; assert_true!(def.clone().matches(&[ @@ -885,11 +854,8 @@ mod tests { .clone() .matches(&[unit!(1.0, "px"), unit!(2.0, "px"), unit!(3.0, "px")])); - assert!(def.clone().matches(&[ - unit!(1.0, "px"), - unit!(2.0, "px"), - unit!(3.0, "px"), - unit!(4.0, "px"), - ])); + assert!(def + .clone() + .matches(&[unit!(1.0, "px"), unit!(2.0, "px"), unit!(3.0, "px"), unit!(4.0, "px"),])); } } diff --git a/crates/gosub_styling/src/shorthands.rs b/crates/gosub_css3/src/matcher/shorthands.rs similarity index 92% rename from crates/gosub_styling/src/shorthands.rs rename to crates/gosub_css3/src/matcher/shorthands.rs index 81386e8e3..38a678f9c 100644 --- a/crates/gosub_styling/src/shorthands.rs +++ b/crates/gosub_css3/src/matcher/shorthands.rs @@ -1,11 +1,11 @@ +use crate::stylesheet::{CssValue, Specificity}; +use gosub_shared::traits::css3::CssOrigin; use std::collections::hash_map::Entry; -use gosub_css3::stylesheet::{CssOrigin, CssValue, Specificity}; - -use crate::property_definitions::CssDefinitions; -use crate::styling::{CssProperties, CssProperty, DeclarationProperty}; -use crate::syntax::{SyntaxComponent, SyntaxComponentMultiplier}; -use crate::syntax_matcher::CssSyntaxTree; +use crate::matcher::property_definitions::CssDefinitions; +use crate::matcher::styling::{CssProperties, CssProperty, DeclarationProperty}; +use crate::matcher::syntax::{SyntaxComponent, SyntaxComponentMultiplier}; +use crate::matcher::syntax_matcher::CssSyntaxTree; impl CssSyntaxTree { pub fn has_property_syntax(&self, property: &str) -> Option { @@ -28,9 +28,7 @@ impl SyntaxComponent { pub fn has_property_syntax(&self, prop: &str, path: &mut Vec) -> bool { match self { SyntaxComponent::Property { property, .. } => prop == property, - SyntaxComponent::Definition { - datatype, quoted, .. - } if *quoted => prop == datatype, + SyntaxComponent::Definition { datatype, quoted, .. } if *quoted => prop == datatype, SyntaxComponent::Group { components, .. } => { for (i, component) in components.iter().enumerate() { path.push(i); @@ -218,11 +216,7 @@ impl<'a> ShorthandResolver<'a> { } if !complete.is_empty() { - let idx = self - .fix_list - .multipliers - .iter_mut() - .find(|m| m.0 == self.name); + let idx = self.fix_list.multipliers.iter_mut().find(|m| m.0 == self.name); if let Some(idx) = idx { let Some(items) = self.multiplier.get_names(complete, idx.1) else { @@ -431,12 +425,7 @@ impl CssDefinitions { } } - pub fn resolve_shorthands( - &self, - computed: &[String], - syntax: &CssSyntaxTree, - name: &str, - ) -> Option { + pub fn resolve_shorthands(&self, computed: &[String], syntax: &CssSyntaxTree, name: &str) -> Option { if computed.len() <= 1 || syntax.components.is_empty() { return None; } @@ -548,9 +537,7 @@ impl CssDefinitions { match component { SyntaxComponent::Definition { datatype, .. } => { if let Some(d) = self.syntax.get(datatype) { - if let Some(mut shorthands) = - self.resolve_shorthands(computed, &d.syntax, name) - { + if let Some(mut shorthands) = self.resolve_shorthands(computed, &d.syntax, name) { shorthands.multiplier = Multiplier::None; return Some(shorthands); @@ -559,9 +546,7 @@ impl CssDefinitions { if let Some(p) = self.properties.get(datatype) { //currently properties get parsed as definitions - if let Some(mut shorthands) = - self.resolve_shorthands(computed, &p.syntax, name) - { + if let Some(mut shorthands) = self.resolve_shorthands(computed, &p.syntax, name) { shorthands.multiplier = Multiplier::None; return Some(shorthands); @@ -571,9 +556,7 @@ impl CssDefinitions { SyntaxComponent::Property { property, .. } => { if let Some(d) = self.properties.get(property) { - if let Some(mut shorthands) = - self.resolve_shorthands(computed, &d.syntax, name) - { + if let Some(mut shorthands) = self.resolve_shorthands(computed, &d.syntax, name) { shorthands.multiplier = Multiplier::None; return Some(shorthands); @@ -603,11 +586,10 @@ impl CssDefinitions { #[cfg(test)] mod tests { - use gosub_css3::colors::RgbColor; - use gosub_css3::stylesheet::CssValue; - - use crate::property_definitions::get_css_definitions; - use crate::shorthands::FixList; + use crate::colors::RgbColor; + use crate::matcher::property_definitions::get_css_definitions; + use crate::matcher::shorthands::CssValue; + use crate::matcher::shorthands::FixList; macro_rules! str { ($s:expr) => { @@ -666,10 +648,9 @@ mod tests { ); fix_list = FixList::new(); - assert!(prop.clone().matches_and_shorthands( - &[unit!(1.0, "px"), unit!(2.0, "px"), unit!(3.0, "px"),], - &mut fix_list, - )); + assert!(prop + .clone() + .matches_and_shorthands(&[unit!(1.0, "px"), unit!(2.0, "px"), unit!(3.0, "px"),], &mut fix_list,)); assert_eq!( fix_list, @@ -686,12 +667,7 @@ mod tests { fix_list = FixList::new(); assert!(prop.clone().matches_and_shorthands( - &[ - unit!(1.0, "px"), - unit!(2.0, "px"), - unit!(3.0, "px"), - unit!(4.0, "px"), - ], + &[unit!(1.0, "px"), unit!(2.0, "px"), unit!(3.0, "px"), unit!(4.0, "px"),], &mut fix_list, )); @@ -737,10 +713,7 @@ mod tests { fix_list = FixList::new(); assert!(prop.clone().matches_and_shorthands( - &[ - str!("solid"), - CssValue::Color(RgbColor::new(0.0, 0.0, 0.0, 0.0)) - ], + &[str!("solid"), CssValue::Color(RgbColor::new(0.0, 0.0, 0.0, 0.0))], &mut fix_list, )); diff --git a/crates/gosub_styling/src/styling.rs b/crates/gosub_css3/src/matcher/styling.rs similarity index 78% rename from crates/gosub_styling/src/styling.rs rename to crates/gosub_css3/src/matcher/styling.rs index 631165256..0a1c46004 100644 --- a/crates/gosub_styling/src/styling.rs +++ b/crates/gosub_css3/src/matcher/styling.rs @@ -1,17 +1,21 @@ -use crate::property_definitions::{get_css_definitions, CSS_DEFINITIONS}; use core::fmt::Debug; -use gosub_css3::stylesheet::{ - Combinator, CssOrigin, CssSelector, CssSelectorPart, CssValue, MatcherType, Specificity, -}; -use gosub_html5::node::{Node, NodeId}; -use gosub_html5::parser::document::{Document, DocumentHandle}; +use gosub_shared::document::DocumentHandle; +use gosub_shared::node::NodeId; +use gosub_shared::traits::css3::{CssOrigin, CssPropertyMap, CssSystem}; +use gosub_shared::traits::document::Document; +use gosub_shared::traits::node::ClassList; +use gosub_shared::traits::node::ElementDataType; +use gosub_shared::traits::node::Node; use itertools::Itertools; use std::cmp::Ordering; use std::collections::HashMap; +use crate::matcher::property_definitions::get_css_definitions; +use crate::stylesheet::{Combinator, CssSelector, CssSelectorPart, CssValue, MatcherType, Specificity}; + // Matches a complete selector (all parts) against the given node(id) -pub(crate) fn match_selector( - document: DocumentHandle, +pub(crate) fn match_selector, C: CssSystem>( + document: DocumentHandle, node_id: NodeId, selector: &CssSelector, ) -> (bool, Specificity) { @@ -35,13 +39,13 @@ fn consume<'a, T>(this: &mut &'a [T]) -> Option<&'a T> { } /// Returns true when the given node matches the part(s) -fn match_selector_parts( - document: DocumentHandle, +fn match_selector_parts, C: CssSystem>( + handle: DocumentHandle, node_id: NodeId, mut parts: &[CssSelectorPart], ) -> bool { - let binding = document.get(); - let mut next_current_node = binding.get_node_by_id(node_id); + let binding = handle.get(); + let mut next_current_node = binding.node_by_id(node_id); if next_current_node.is_none() { return false; } @@ -55,13 +59,7 @@ fn match_selector_parts( return false; } - if !match_selector_part( - part, - current_node, - &binding, - &mut next_current_node, - &mut parts, - ) { + if !match_selector_part(part, current_node, &*binding, &mut next_current_node, &mut parts) { return false; } @@ -74,11 +72,11 @@ fn match_selector_parts( true } -fn match_selector_part<'a>( +fn match_selector_part<'a, D: Document, C: CssSystem>( part: &CssSelectorPart, - current_node: &Node, - doc: &'a Document, - next_node: &mut Option<&'a Node>, + current_node: &D::Node, + doc: &'a D, + next_node: &mut Option<&'a D::Node>, parts: &mut &[CssSelectorPart], ) -> bool { match part { @@ -87,24 +85,25 @@ fn match_selector_part<'a>( true } CssSelectorPart::Type(name) => { - if !current_node.is_element() { + if !current_node.is_element_node() { return false; } - *name == current_node.as_element().name + *name == current_node.get_element_data().unwrap().name() } CssSelectorPart::Class(name) => { - if !current_node.is_element() { + if !current_node.is_element_node() { return false; } - current_node.as_element().classes.contains(name) + current_node.get_element_data().unwrap().classlist().contains(name) } CssSelectorPart::Id(name) => { - if !current_node.is_element() { + if !current_node.is_element_node() { return false; } current_node - .as_element() - .attributes + .get_element_data() + .unwrap() + .attributes() .get("id") .unwrap_or(&"".to_string()) == name @@ -112,15 +111,17 @@ fn match_selector_part<'a>( CssSelectorPart::Attribute(attr) => { let wanted_attr_name = &attr.name; - if !current_node.has_attribute(wanted_attr_name) { + if current_node.get_element_data().is_none() { + return false; + } + + let element_data = current_node.get_element_data().unwrap(); + if !element_data.attributes().contains_key(wanted_attr_name) { return false; } let mut wanted_attr_value = &attr.value; - let mut got_attr_value = current_node - .get_attribute(wanted_attr_name) - .map(|v| v.as_str()) - .unwrap_or(""); + let mut got_attr_value = element_data.attributes().get(wanted_attr_name).unwrap(); let mut _wanted_buf = String::new(); //Two buffers, so we don't need to clone the value if we match case-sensitive let mut _got_buf = String::new(); @@ -144,9 +145,7 @@ fn match_selector_part<'a>( } MatcherType::Includes => { // Contains word - wanted_attr_value - .split_whitespace() - .any(|s| s == got_attr_value) + wanted_attr_value.split_whitespace().any(|s| s == got_attr_value) } MatcherType::DashMatch => { // Exact value or value followed by a hyphen @@ -179,18 +178,19 @@ fn match_selector_part<'a>( // We don't have the descendant combinator (space), as this is the default behaviour match combinator { Combinator::Descendant => { - let Some(mut parent_id) = current_node.parent else { + // let handle_clone = handle.clone(); + + let Some(mut parent_id) = current_node.parent_id() else { return false; }; let last = consume(parts); - let Some(last) = last else { return false; }; loop { - let Some(parent) = doc.get_node_by_id(parent_id) else { + let Some(parent) = doc.node_by_id(parent_id) else { return false; }; @@ -200,7 +200,7 @@ fn match_selector_part<'a>( return true; } - let Some(p) = parent.parent else { + let Some(p) = parent.parent_id() else { return false; }; @@ -209,7 +209,7 @@ fn match_selector_part<'a>( } Combinator::Child => { // Child combinator. Only matches the direct child - let Some(parent) = current_node.parent else { + let Some(parent) = current_node.parent_id() else { return false; }; @@ -219,7 +219,7 @@ fn match_selector_part<'a>( return false; }; - let Some(parent) = doc.get_node_by_id(parent) else { + let Some(parent) = doc.node_by_id(parent) else { return false; }; @@ -228,13 +228,14 @@ fn match_selector_part<'a>( match_selector_part(last, parent, doc, next_node, parts) } Combinator::NextSibling => { - let Some(children) = doc.parent_node(current_node).map(|p| &p.children) else { + let parent_node = doc.node_by_id(current_node.parent_id().unwrap()); + let Some(children) = parent_node.map(|p| p.children()) else { return false; }; let Some(my_index) = children .iter() - .find_position(|c| **c == current_node.id) + .find_position(|c| **c == current_node.id()) .map(|(i, _)| i) else { return false; @@ -252,7 +253,7 @@ fn match_selector_part<'a>( return false; }; - let Some(prev) = doc.get_node_by_id(prev_id) else { + let Some(prev) = doc.node_by_id(prev_id) else { return false; }; @@ -261,7 +262,8 @@ fn match_selector_part<'a>( match_selector_part(last, prev, doc, next_node, parts) } Combinator::SubsequentSibling => { - let Some(children) = doc.parent_node(current_node).map(|p| &p.children) else { + let parent_node = doc.node_by_id(current_node.parent_id().unwrap()); + let Some(children) = parent_node.map(|p| p.children()) else { return false; }; @@ -270,11 +272,11 @@ fn match_selector_part<'a>( }; for child in children { - if *child == current_node.id { + if *child == current_node.id() { break; } - let Some(child) = doc.get_node_by_id(*child) else { + let Some(child) = doc.node_by_id(*child) else { continue; }; @@ -289,10 +291,6 @@ fn match_selector_part<'a>( let namespace = consume(parts); let Some(namespace) = namespace else { - if current_node.namespace.is_none() { - return true; - } - return false; }; @@ -304,7 +302,7 @@ fn match_selector_part<'a>( return false; }; - current_node.is_namespace(namespace) + current_node.get_element_data().unwrap().is_namespace(namespace) } Combinator::Column => { //TODO @@ -326,6 +324,7 @@ pub struct DeclarationProperty { pub origin: CssOrigin, /// Whether the declaration is !important pub important: bool, + // @TODO: location should be a Location /// The location of the declaration in the stylesheet (name.css:123) or empty pub location: String, /// The specificity of the selector that declared this property @@ -492,7 +491,7 @@ impl CssProperty { } } - // /// Returns true if the given property is a shorthand property (ie: border, margin etc) + // /// Returns true if the given property is a shorthand property (ie: border, margin etc.) pub fn is_shorthand(&self) -> bool { let defs = get_css_definitions(); match defs.find_property(&self.name) { @@ -520,8 +519,74 @@ impl CssProperty { // // Returns the initial value for the property, if any fn get_initial_value(&self) -> Option { let defs = get_css_definitions(); - defs.find_property(&self.name) - .map(|def| def.initial_value()) + defs.find_property(&self.name).map(|def| def.initial_value()) + } +} + +impl gosub_shared::traits::css3::CssProperty for CssProperty { + type Value = CssValue; + + fn compute_value(&mut self) { + self.compute_value(); + } + fn unit_to_px(&self) -> f32 { + self.actual.unit_to_px() + } + + fn as_string(&self) -> Option<&str> { + if let CssValue::String(str) = &self.actual { + Some(str) + } else { + None + } + } + + fn as_percentage(&self) -> Option { + if let CssValue::Percentage(percent) = &self.actual { + Some(*percent) + } else { + None + } + } + + fn as_unit(&self) -> Option<(f32, &str)> { + if let CssValue::Unit(value, unit) = &self.actual { + Some((*value, unit)) + } else { + None + } + } + + fn as_color(&self) -> Option<(f32, f32, f32, f32)> { + if let CssValue::Color(color) = &self.actual { + Some((color.r, color.g, color.b, color.a)) + } else { + None + } + } + + fn parse_color(&self) -> Option<(f32, f32, f32, f32)> { + self.actual.to_color().map(|color| (color.r, color.g, color.b, color.a)) + } + + fn as_number(&self) -> Option { + if let CssValue::Number(num) = &self.actual { + Some(*num) + } else { + None + } + } + + fn as_list(&self) -> Option> { + if let CssValue::List(list) = &self.actual { + Some(list.to_vec()) + } else { + None + } + } + + fn is_none(&self) -> bool { + matches!(self.actual, CssValue::None) } } @@ -530,6 +595,7 @@ impl CssProperty { #[derive(Debug)] pub struct CssProperties { pub properties: HashMap, + pub dirty: bool, } impl Default for CssProperties { @@ -542,6 +608,7 @@ impl CssProperties { pub fn new() -> Self { Self { properties: HashMap::new(), + dirty: true, } } @@ -550,17 +617,47 @@ impl CssProperties { } } -pub fn prop_is_inherit(name: &str) -> bool { - CSS_DEFINITIONS - .find_property(name) - .map(|def| def.inherited) - .unwrap_or(false) +impl CssPropertyMap for CssProperties { + type Property = CssProperty; + + fn insert_inherited(&mut self, name: &str, value: Self::Property) { + self.properties.entry(name.to_string()).or_insert(value); + } + + fn get(&self, name: &str) -> Option<&Self::Property> { + self.properties.get(name) + } + + fn get_mut(&mut self, name: &str) -> Option<&mut Self::Property> { + self.properties.get_mut(name) + } + + fn make_dirty(&mut self) { + self.dirty = true; + } + + fn iter(&self) -> impl Iterator + '_ { + self.properties.iter().map(|(k, v)| (k.as_str(), v)) + } + + fn iter_mut(&mut self) -> impl Iterator + '_ { + self.properties.iter_mut().map(|(k, v)| (k.as_str(), v)) + } + + fn make_clean(&mut self) { + self.dirty = false; + } + + fn is_dirty(&self) -> bool { + self.dirty + } } #[cfg(test)] mod tests { use super::*; - use gosub_css3::colors::RgbColor; + use crate::colors::RgbColor; + use crate::system::prop_is_inherit; #[test] fn css_props() { diff --git a/crates/gosub_styling/src/syntax.rs b/crates/gosub_css3/src/matcher/syntax.rs similarity index 94% rename from crates/gosub_styling/src/syntax.rs rename to crates/gosub_css3/src/matcher/syntax.rs index 1402f1b02..f9e732dfd 100644 --- a/crates/gosub_styling/src/syntax.rs +++ b/crates/gosub_css3/src/matcher/syntax.rs @@ -1,10 +1,9 @@ use std::fmt::{Debug, Display, Formatter}; +use gosub_shared::errors::{CssError, CssResult}; use nom::branch::alt; use nom::bytes::complete::{tag, tag_no_case, take_while}; -use nom::character::complete::{ - alpha1, alphanumeric1, char, digit0, digit1, multispace0, one_of, space0, -}; +use nom::character::complete::{alpha1, alphanumeric1, char, digit0, digit1, multispace0, one_of, space0}; use nom::combinator::{map, map_res, opt, recognize}; use nom::multi::{fold_many1, many0, many1, separated_list0, separated_list1}; use nom::number::complete::float; @@ -12,11 +11,8 @@ use nom::sequence::{delimited, pair, preceded, separated_pair, tuple}; use nom::Err; use nom::IResult; -use gosub_css3::stylesheet::CssValue; -use gosub_shared::types::Result; - -use crate::errors::Error; -use crate::syntax_matcher::CssSyntaxTree; +use crate::matcher::syntax_matcher::CssSyntaxTree; +use crate::stylesheet::CssValue; // When debugging the parser, it's nice to have some additional information ready. This should maybe // be inside a cfg setting, but for now (un)commenting the appropriate line is good enough. @@ -33,6 +29,7 @@ pub struct Group { pub components: Vec, } +#[allow(dead_code)] #[derive(PartialEq, Debug, Clone)] pub enum GroupCombinators { /// All elements must be matched in order (space delimited) @@ -259,7 +256,7 @@ impl CssSyntax { } /// Compiles the current syntax into a list of components or Err on compilation error - pub fn compile(self) -> Result { + pub fn compile(self) -> CssResult { if self.source.is_empty() { return Ok(CssSyntaxTree::new(vec![])); } @@ -268,15 +265,13 @@ impl CssSyntax { match p { Ok((input, components)) => { if !input.trim().is_empty() { - return Err(Error::CssCompile(format!( - "Failed to parse all input (left: '{}')", - input - )) - .into()); + return Err(CssError::new( + format!("Failed to parse all input (left: '{}')", input).as_str(), + )); } Ok(CssSyntaxTree::new(vec![components])) } - Err(err) => Err(Error::CssCompile(err.to_string()).into()), + Err(e) => Err(CssError::new(e.to_string().as_str())), } } } @@ -297,10 +292,7 @@ fn parse_unit(input: &str) -> IResult<&str, SyntaxComponent> { }, )) } else { - Err(Err::Error(nom::error::Error::new( - input, - nom::error::ErrorKind::Alpha, - ))) + Err(Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::Alpha))) }; } @@ -353,10 +345,7 @@ fn parse_comma_separated_multiplier(input: &str) -> IResult<&str, SyntaxComponen )); let (input, minmax) = alt(( - map( - delimited(ws(tag("#{")), range, ws(tag("}"))), - |(min, max)| (min, max), - ), + map(delimited(ws(tag("#{")), range, ws(tag("}"))), |(min, max)| (min, max)), // No range means one or more values map(ws(tag("#")), |_| (1, u32::MAX)), ))(input)?; @@ -391,8 +380,7 @@ fn parse_multipliers(input: &str) -> IResult<&str, Vec IResult<&str, SyntaxComponent> { debug_print!("Parsing group: {}", input); - let (input, components) = - delimited(ws(tag("[")), parse_component_singlebar_list, ws(tag("]")))(input)?; + let (input, components) = delimited(ws(tag("[")), parse_component_singlebar_list, ws(tag("]")))(input)?; Ok((input, components)) } @@ -420,8 +408,7 @@ fn parse_component_singlebar_list(input: &str) -> IResult<&str, SyntaxComponent> fn parse_component_doublebar_list(input: &str) -> IResult<&str, SyntaxComponent> { debug_print!("Parsing component doublebar list: {}", input); - let (input, components) = - separated_list1(ws(tag("||")), parse_component_doubleampersand_list)(input)?; + let (input, components) = separated_list1(ws(tag("||")), parse_component_doubleampersand_list)(input)?; if components.len() == 1 { return Ok((input, components[0].clone())); @@ -441,8 +428,7 @@ fn parse_component_doublebar_list(input: &str) -> IResult<&str, SyntaxComponent> fn parse_component_doubleampersand_list(input: &str) -> IResult<&str, SyntaxComponent> { debug_print!("Parsing component doubleampersand list: {}", input); - let (input, components) = - separated_list1(ws(tag("&&")), parse_component_juxtaposition_list)(input)?; + let (input, components) = separated_list1(ws(tag("&&")), parse_component_juxtaposition_list)(input)?; if components.len() == 1 { return Ok((input, components[0].clone())); @@ -597,11 +583,7 @@ fn parse_function(input: &str) -> IResult<&str, SyntaxComponent> { space0, tuple((space0, char(')'), space0)), ); - let arglist = delimited( - ws(tag("(")), - ws(parse_component_singlebar_list), - ws(tag(")")), - ); + let arglist = delimited(ws(tag("(")), ws(parse_component_singlebar_list), ws(tag(")"))); let (input, name) = parse_keyword(input)?; let (input, arglist) = alt((map(empty_arglist, |_| None), map(arglist, Some)))(input)?; @@ -662,21 +644,18 @@ fn parse_infinity(input: &str) -> IResult<&str, NumberOrInfinity> { /// Parses an integer (signed or unsigned) and returns NumberOrInfinity::FiniteI64, or errors when the integer is invalid fn parse_signed_integer(input: &str) -> IResult<&str, NumberOrInfinity> { - map_res( - pair(opt(char('-')), digit1), - |(sign, digits): (Option, &str)| { - let neg_multiplier = if sign == Some('-') { -1 } else { 1 }; - let num = digits.parse::().map(|num| num * neg_multiplier); - if let Ok(num) = num { - Ok(NumberOrInfinity::FiniteI64(num)) - } else { - Err(nom::Err::Error(nom::error::Error::new( - input, - nom::error::ErrorKind::Digit, - ))) - } - }, - )(input) + map_res(pair(opt(char('-')), digit1), |(sign, digits): (Option, &str)| { + let neg_multiplier = if sign == Some('-') { -1 } else { 1 }; + let num = digits.parse::().map(|num| num * neg_multiplier); + if let Ok(num) = num { + Ok(NumberOrInfinity::FiniteI64(num)) + } else { + Err(nom::Err::Error(nom::error::Error::new( + input, + nom::error::ErrorKind::Digit, + ))) + } + })(input) } fn parse_unit_range(input: &str) -> IResult<&str, NumberOrInfinity> { @@ -702,17 +681,9 @@ fn parse_unit_range(input: &str) -> IResult<&str, NumberOrInfinity> { /// Parses a range for a type definition (ie: the square bracket part of: ) fn datatype_range(input: &str) -> IResult<&str, RangeType> { let range = separated_pair( - opt(ws(alt(( - parse_infinity, - parse_unit_range, - parse_signed_integer, - )))), + opt(ws(alt((parse_infinity, parse_unit_range, parse_signed_integer)))), tag(","), - opt(ws(alt(( - parse_infinity, - parse_unit_range, - parse_signed_integer, - )))), + opt(ws(alt((parse_infinity, parse_unit_range, parse_signed_integer)))), ); let range = map(range, |(min, max)| RangeType { @@ -735,10 +706,9 @@ fn parse_datatype(input: &str) -> IResult<&str, SyntaxComponent> { let (input, (name, quoted, range)) = delimited( ws(tag("<")), alt(( - map( - pair(keyword_or_function, opt(datatype_range)), - |(name, range)| (name, false, range), - ), + map(pair(keyword_or_function, opt(datatype_range)), |(name, range)| { + (name, false, range) + }), map( pair( delimited(ws(tag("'")), keyword_or_function, ws(tag("'"))), @@ -789,13 +759,12 @@ fn parse_literal(input: &str) -> IResult<&str, SyntaxComponent> { literal: ",".to_string(), multipliers: vec![SyntaxComponentMultiplier::Once], }), - map( - delimited(tag("'"), take_while(|c| c != '\''), tag("'")), - |s: &str| SyntaxComponent::Literal { + map(delimited(tag("'"), take_while(|c| c != '\''), tag("'")), |s: &str| { + SyntaxComponent::Literal { literal: s.to_string(), multipliers: vec![SyntaxComponentMultiplier::Once], - }, - ), + } + }), ))(input) } @@ -832,9 +801,8 @@ fn parse(input: &str) -> IResult<&str, SyntaxComponent> { #[cfg(test)] mod tests { - use crate::property_definitions::get_css_definitions; - use super::*; + use crate::matcher::property_definitions::get_css_definitions; #[test] fn test_compile_empty() { @@ -1016,10 +984,7 @@ mod tests { parts.unwrap(), CssSyntaxTree::new(vec![SyntaxComponent::GenericKeyword { keyword: "color".to_string(), - multipliers: vec![SyntaxComponentMultiplier::CommaSeparatedRepeat( - 1, - u32::MAX as usize - )], + multipliers: vec![SyntaxComponentMultiplier::CommaSeparatedRepeat(1, u32::MAX as usize)], }]) ); @@ -1219,10 +1184,7 @@ mod tests { datatype: "foo".to_string(), quoted: false, range: RangeType::empty(), - multipliers: vec![SyntaxComponentMultiplier::CommaSeparatedRepeat( - 1, - u32::MAX as usize - )], + multipliers: vec![SyntaxComponentMultiplier::CommaSeparatedRepeat(1, u32::MAX as usize)], }] ); } @@ -1403,9 +1365,7 @@ mod tests { } ); - let c = CssSyntax::new("left | right || top && bottom") - .compile() - .unwrap(); + let c = CssSyntax::new("left | right || top && bottom").compile().unwrap(); assert_eq!( c.components[0], SyntaxComponent::Group { @@ -1444,9 +1404,7 @@ mod tests { } ); - let c = CssSyntax::new("left || right | top && bottom") - .compile() - .unwrap(); + let c = CssSyntax::new("left || right | top && bottom").compile().unwrap(); assert_eq!( c.components[0], SyntaxComponent::Group { @@ -1485,9 +1443,7 @@ mod tests { } ); - let c = CssSyntax::new("left && right || top | bottom") - .compile() - .unwrap(); + let c = CssSyntax::new("left && right || top | bottom").compile().unwrap(); assert_eq!( c.components[0], SyntaxComponent::Group { @@ -1526,9 +1482,7 @@ mod tests { } ); - let c = CssSyntax::new("left right || top | bottom") - .compile() - .unwrap(); + let c = CssSyntax::new("left right || top | bottom").compile().unwrap(); assert_eq!( c.components[0], SyntaxComponent::Group { @@ -1567,9 +1521,7 @@ mod tests { } ); - let c = CssSyntax::new("left | right || top | bottom") - .compile() - .unwrap(); + let c = CssSyntax::new("left | right || top | bottom").compile().unwrap(); assert_eq!( c.components[0], SyntaxComponent::Group { @@ -1602,9 +1554,7 @@ mod tests { } ); - let c = CssSyntax::new("left || right | top || bottom") - .compile() - .unwrap(); + let c = CssSyntax::new("left || right | top || bottom").compile().unwrap(); assert_eq!( c.components[0], SyntaxComponent::Group { @@ -1801,49 +1751,31 @@ mod tests { // @todo: These tests should also check if the syntax is correct, not only if it can // compile. The output could still be wrong. assert!(CssSyntax::new("le, ri ,co , bt,tp").compile().is_ok()); - assert!(CssSyntax::new("left | right | center && top") - .compile() - .is_ok()); + assert!(CssSyntax::new("left | right | center && top").compile().is_ok()); assert!(CssSyntax::new("left , right color()").compile().is_ok()); assert!(CssSyntax::new("left , right color() ").compile().is_ok()); assert!(CssSyntax::new("le, ri ,co , bt,tp").compile().is_ok()); assert!(CssSyntax::new("left, right color()").compile().is_ok()); - assert!(CssSyntax::new("left | right | center && top") + assert!(CssSyntax::new("left | right | center && top").compile().is_ok()); + assert!(CssSyntax::new("left | right | center && top ") .compile() .is_ok()); - assert!(CssSyntax::new("left | right | center && top ") + assert!(CssSyntax::new("[ [ ? ]]").compile().is_ok()); + assert!(CssSyntax::new("[ [ center | [ top | bottom ] ]]").compile().is_ok()); + assert!(CssSyntax::new("[ ? ]").compile().is_ok()); + assert!(CssSyntax::new("[ center ? ]").compile().is_ok()); + assert!(CssSyntax::new("center | [ top | bottom ] ") .compile() .is_ok()); - assert!(CssSyntax::new("[ [ ? ]]") + assert!(CssSyntax::new("[ center | [ top | bottom ] ]") .compile() .is_ok()); - assert!(CssSyntax::new("[ [ center | [ top | bottom ] ]]") + assert!(CssSyntax::new("[ center | [ top | bottom ] ? ]") .compile() .is_ok()); - assert!(CssSyntax::new("[ ? ]").compile().is_ok()); - assert!(CssSyntax::new("[ center ? ]") + assert!(CssSyntax::new("[ [ center | [ top | bottom ] ? ]]") .compile() .is_ok()); - assert!( - CssSyntax::new("center | [ top | bottom ] ") - .compile() - .is_ok() - ); - assert!( - CssSyntax::new("[ center | [ top | bottom ] ]") - .compile() - .is_ok() - ); - assert!( - CssSyntax::new("[ center | [ top | bottom ] ? ]") - .compile() - .is_ok() - ); - assert!( - CssSyntax::new("[ [ center | [ top | bottom ] ? ]]") - .compile() - .is_ok() - ); assert!(CssSyntax::new("[ [ top | center | bottom | ]| [ center | [ left | right ] ? ] && [ center | [ top | bottom ] ? ]]").compile().is_ok()); assert!(CssSyntax::new("[ [ left | center | right | top | bottom | ]| [ left | center | right | ] [ top | center | bottom | ]| [ center | [ left | right ] ? ] && [ center | [ top | bottom ] ? ]]").compile().is_ok()); } @@ -1962,9 +1894,7 @@ mod tests { }]) ); - let c = CssSyntax::new("left && right | foo || bar baz") - .compile() - .unwrap(); + let c = CssSyntax::new("left && right | foo || bar baz").compile().unwrap(); assert_eq!( c, CssSyntaxTree::new(vec![SyntaxComponent::Group { @@ -2013,9 +1943,7 @@ mod tests { }]) ); - let c = CssSyntax::new("[ left ] [ right ] [ top ]") - .compile() - .unwrap(); + let c = CssSyntax::new("[ left ] [ right ] [ top ]").compile().unwrap(); assert_eq!( c, CssSyntaxTree::new(vec![SyntaxComponent::Group { @@ -2127,9 +2055,7 @@ mod tests { }]) ); - let c = CssSyntax::new("[ [ left ] [ right ] [ top ] ] a") - .compile() - .unwrap(); + let c = CssSyntax::new("[ [ left ] [ right ] [ top ] ] a").compile().unwrap(); assert_eq!( c, CssSyntaxTree::new(vec![SyntaxComponent::Group { @@ -2162,9 +2088,7 @@ mod tests { }]) ); - let c = CssSyntax::new("[ [ left ] | [ right ] [ top ] ]") - .compile() - .unwrap(); + let c = CssSyntax::new("[ [ left ] | [ right ] [ top ] ]").compile().unwrap(); assert_eq!( c, CssSyntaxTree::new(vec![SyntaxComponent::Group { @@ -2312,15 +2236,11 @@ mod tests { components: vec![ SyntaxComponent::GenericKeyword { keyword: "left".to_string(), - multipliers: vec![ - SyntaxComponentMultiplier::Once - ], + multipliers: vec![SyntaxComponentMultiplier::Once], }, SyntaxComponent::GenericKeyword { keyword: "right".to_string(), - multipliers: vec![ - SyntaxComponentMultiplier::Once - ], + multipliers: vec![SyntaxComponentMultiplier::Once], }, ], multipliers: vec![SyntaxComponentMultiplier::Once], @@ -2332,9 +2252,7 @@ mod tests { min: NumberOrInfinity::None, max: NumberOrInfinity::None, }, - multipliers: vec![ - SyntaxComponentMultiplier::Optional - ], + multipliers: vec![SyntaxComponentMultiplier::Optional], }, ], multipliers: vec![SyntaxComponentMultiplier::Once], @@ -2357,15 +2275,11 @@ mod tests { components: vec![ SyntaxComponent::GenericKeyword { keyword: "top".to_string(), - multipliers: vec![ - SyntaxComponentMultiplier::Once - ], + multipliers: vec![SyntaxComponentMultiplier::Once], }, SyntaxComponent::GenericKeyword { keyword: "bottom".to_string(), - multipliers: vec![ - SyntaxComponentMultiplier::Once - ], + multipliers: vec![SyntaxComponentMultiplier::Once], }, ], multipliers: vec![SyntaxComponentMultiplier::Once], @@ -2377,9 +2291,7 @@ mod tests { min: NumberOrInfinity::None, max: NumberOrInfinity::None, }, - multipliers: vec![ - SyntaxComponentMultiplier::Optional - ], + multipliers: vec![SyntaxComponentMultiplier::Optional], }, ], multipliers: vec![SyntaxComponentMultiplier::Once], @@ -2395,11 +2307,9 @@ mod tests { }]) ); - let c = CssSyntax::new( - "[ [ left+ ] | [ center? ] [ top# ]{1,3} | [ center1 ]? && [ center2 ] ]*", - ) - .compile() - .unwrap(); + let c = CssSyntax::new("[ [ left+ ] | [ center? ] [ top# ]{1,3} | [ center1 ]? && [ center2 ] ]*") + .compile() + .unwrap(); assert_eq!( c, CssSyntaxTree::new(vec![SyntaxComponent::Group { diff --git a/crates/gosub_styling/src/syntax_matcher.rs b/crates/gosub_css3/src/matcher/syntax_matcher.rs similarity index 90% rename from crates/gosub_styling/src/syntax_matcher.rs rename to crates/gosub_css3/src/matcher/syntax_matcher.rs index 822a2775b..8e70cb4e0 100644 --- a/crates/gosub_styling/src/syntax_matcher.rs +++ b/crates/gosub_css3/src/matcher/syntax_matcher.rs @@ -1,8 +1,7 @@ -use gosub_css3::colors::{is_named_color, is_system_color}; -use gosub_css3::stylesheet::CssValue; - -use crate::shorthands::{copy_resolver, ShorthandResolver}; -use crate::syntax::{GroupCombinators, SyntaxComponent, SyntaxComponentMultiplier}; +use crate::colors::{is_named_color, is_system_color}; +use crate::matcher::shorthands::{copy_resolver, ShorthandResolver}; +use crate::matcher::syntax::{GroupCombinators, SyntaxComponent, SyntaxComponentMultiplier}; +use crate::stylesheet::CssValue; /// Structure to return from a matching function. #[derive(Debug, Clone)] @@ -17,9 +16,8 @@ pub struct MatchResult<'a> { #[allow(dead_code)] const LENGTH_UNITS: [&str; 31] = [ - "cap", "ch", "em", "ex", "ic", "lh", "rcap", "rch", "rem", "rex", "ric", "rlh", "vh", "vw", - "vmax", "vmin", "vb", "vi", "cqw", "cqh", "cqi", "cqb", "cqmin", "cqmax", "px", "cm", "mm", - "Q", "in", "pc", "pt", + "cap", "ch", "em", "ex", "ic", "lh", "rcap", "rch", "rem", "rex", "ric", "rlh", "vh", "vw", "vmax", "vmin", "vb", + "vi", "cqw", "cqh", "cqi", "cqb", "cqmin", "cqmax", "px", "cm", "mm", "Q", "in", "pc", "pt", ]; /// A CSS Syntax Tree is a tree sof CSS syntax components that can be used to match against CSS values. @@ -178,8 +176,7 @@ fn match_component<'a>( // CSV loop loop { - let inner_result = - match_component_inner(input, component, copy_resolver(&mut shorthand_resolver)); + let inner_result = match_component_inner(input, component, copy_resolver(&mut shorthand_resolver)); if !comma_separated { // We don't need to check for comma separated values, so just return this result return inner_result; @@ -234,25 +231,17 @@ fn match_component_group<'a>( ) -> MatchResult<'a> { match &component { SyntaxComponent::Group { - components, - combinator, - .. + components, combinator, .. } => { // println!("We need to do a group match on {:?}, our value is: {:?}", combinator, input); match combinator { - GroupCombinators::Juxtaposition => { - match_group_juxtaposition(input, components, shorthand_resolver) - } - GroupCombinators::AllAnyOrder => { - match_group_all_any_order(input, components, shorthand_resolver) - } + GroupCombinators::Juxtaposition => match_group_juxtaposition(input, components, shorthand_resolver), + GroupCombinators::AllAnyOrder => match_group_all_any_order(input, components, shorthand_resolver), GroupCombinators::AtLeastOneAnyOrder => { match_group_at_least_one_any_order(input, components, shorthand_resolver) } - GroupCombinators::ExactlyOne => { - match_group_exactly_one(input, components, shorthand_resolver) - } + GroupCombinators::ExactlyOne => match_group_exactly_one(input, components, shorthand_resolver), } } e => { @@ -262,10 +251,7 @@ fn match_component_group<'a>( } /// Matches a single component value -fn match_component_single<'a>( - input: &'a [CssValue], - component: &SyntaxComponent, -) -> MatchResult<'a> { +fn match_component_single<'a>(input: &'a [CssValue], component: &SyntaxComponent) -> MatchResult<'a> { // Get the first value from the input which we will use for matching let value = input.first().unwrap(); @@ -295,20 +281,14 @@ fn match_component_single<'a>( "angle" => match value { CssValue::Zero => return first_match(input), CssValue::Unit(_, u) if u.eq_ignore_ascii_case("deg") => return first_match(input), - CssValue::Unit(_, u) if u.eq_ignore_ascii_case("grad") => { - return first_match(input) - } + CssValue::Unit(_, u) if u.eq_ignore_ascii_case("grad") => return first_match(input), CssValue::Unit(_, u) if u.eq_ignore_ascii_case("rad") => return first_match(input), - CssValue::Unit(_, u) if u.eq_ignore_ascii_case("turn") => { - return first_match(input) - } + CssValue::Unit(_, u) if u.eq_ignore_ascii_case("turn") => return first_match(input), _ => {} }, "length" => match value { CssValue::Zero => return first_match(input), - CssValue::Unit(_, u) if LENGTH_UNITS.contains(&u.as_str()) => { - return first_match(input) - } + CssValue::Unit(_, u) if LENGTH_UNITS.contains(&u.as_str()) => return first_match(input), _ => {} }, "system-color" => { @@ -363,10 +343,7 @@ fn match_component_single<'a>( match value { CssValue::Number(n) if *n == 0.0 => return first_match(input), CssValue::Unit(n, u) => { - if unit.contains(u) - && *n >= from.unwrap_or(f32min) - && *n <= to.unwrap_or(f32max) - { + if unit.contains(u) && *n >= from.unwrap_or(f32min) && *n <= to.unwrap_or(f32max) { return first_match(input); } } @@ -398,9 +375,7 @@ fn match_component_single<'a>( // let list = CssValue::List(c_args.clone()); // return match_internal(&list, arguments); } - SyntaxComponent::Value { - value: css_value, .. - } => { + SyntaxComponent::Value { value: css_value, .. } => { if value == css_value { return first_match(input); } @@ -762,8 +737,7 @@ fn multiplier_fulfilled(component: &SyntaxComponent, cnt: usize) -> Fulfillment .filter(|m| { !matches!( m, - SyntaxComponentMultiplier::AtLeastOneValue - | SyntaxComponentMultiplier::CommaSeparatedRepeat(_, _) + SyntaxComponentMultiplier::AtLeastOneValue | SyntaxComponentMultiplier::CommaSeparatedRepeat(_, _) ) }) .collect(); @@ -818,12 +792,10 @@ fn first_match(input: &[CssValue]) -> MatchResult { #[cfg(test)] mod tests { - use gosub_css3::stylesheet::CssValue; - - use crate::property_definitions::{get_css_definitions, PropertyDefinition}; - use crate::syntax::CssSyntax; use super::*; + use crate::matcher::property_definitions::{get_css_definitions, PropertyDefinition}; + use crate::matcher::syntax::CssSyntax; macro_rules! str { ($s:expr) => { @@ -925,12 +897,7 @@ mod tests { assert_true!(tree.matches(&[CssValue::None, str!("auto"), str!("block")])); assert_false!(tree.matches(&[str!("auto"), str!("block")])); assert_false!(tree.matches(&[CssValue::None, str!("block")])); - assert_false!(tree.matches(&[ - str!("block"), - str!("block"), - CssValue::None, - CssValue::None - ])); + assert_false!(tree.matches(&[str!("block"), str!("block"), CssValue::None, CssValue::None])); } #[test] @@ -956,12 +923,7 @@ mod tests { CssValue::Comma, str!("block"), ])); - assert_false!(tree.matches(&[ - str!("block"), - str!("block"), - CssValue::None, - CssValue::None, - ])); + assert_false!(tree.matches(&[str!("block"), str!("block"), CssValue::None, CssValue::None,])); } #[test] @@ -984,13 +946,7 @@ mod tests { let res = match_group_juxtaposition(&input, components, None); assert_not_match!(res); - let input = [ - str!("none"), - str!("block"), - str!("block"), - str!("auto"), - str!("none"), - ]; + let input = [str!("none"), str!("block"), str!("block"), str!("auto"), str!("none")]; let res = match_group_juxtaposition(&input, components, None); assert_not_match!(res); @@ -1044,13 +1000,7 @@ mod tests { let res = match_group_all_any_order(&input, components, None); assert_match!(res); - let input = [ - str!("none"), - str!("block"), - str!("block"), - str!("auto"), - str!("none"), - ]; + let input = [str!("none"), str!("block"), str!("block"), str!("auto"), str!("none")]; let res = match_group_all_any_order(&input, components, None); assert_not_match!(res); @@ -1115,26 +1065,20 @@ mod tests { let tree = CssSyntax::new("foo bar baz").compile().unwrap(); assert_false!(tree.clone().matches(&[str!("foo")])); assert_false!(tree.clone().matches(&[str!("foo")])); - assert_true!(tree - .clone() - .matches(&[str!("foo"), str!("bar"), str!("baz"),])); + assert_true!(tree.clone().matches(&[str!("foo"), str!("bar"), str!("baz"),])); assert_false!(tree.clone().matches(&[str!("foo"), str!("baz"),])); let tree = CssSyntax::new("foo bar?").compile().unwrap(); dbg!(&tree); assert_true!(tree.clone().matches(&[str!("foo")])); assert_true!(tree.clone().matches(&[str!("foo"), str!("bar"),])); - assert_false!(tree - .clone() - .matches(&[str!("foo"), str!("bar"), str!("bar"),])); + assert_false!(tree.clone().matches(&[str!("foo"), str!("bar"), str!("bar"),])); assert_false!(tree.clone().matches(&[str!("bar"), str!("foo"),])); let tree = CssSyntax::new("foo bar? baz").compile().unwrap(); assert_false!(tree.clone().matches(&[str!("foo")])); assert_true!(tree.clone().matches(&[str!("foo"), str!("baz"),])); - assert_true!(tree - .clone() - .matches(&[str!("foo"), str!("bar"), str!("baz"),])); + assert_true!(tree.clone().matches(&[str!("foo"), str!("bar"), str!("baz"),])); assert_false!(tree .clone() @@ -1150,9 +1094,7 @@ mod tests { let tree = CssSyntax::new("foo bar* baz").compile().unwrap(); assert_false!(tree.clone().matches(&[str!("foo")])); assert_false!(tree.clone().matches(&[str!("foo")])); - assert_true!(tree - .clone() - .matches(&[str!("foo"), str!("bar"), str!("baz"),])); + assert_true!(tree.clone().matches(&[str!("foo"), str!("bar"), str!("baz"),])); assert_true!(tree.clone().matches(&[str!("foo"), str!("baz"),])); assert_true!(tree.clone().matches(&[ str!("foo"), @@ -1175,9 +1117,7 @@ mod tests { assert_true!(tree.clone().matches(&[str!("foo")])); assert_true!(tree.clone().matches(&[str!("foo")])); assert_true!(tree.clone().matches(&[str!("foo"), str!("bar"),])); - assert_true!(tree - .clone() - .matches(&[str!("foo"), str!("bar"), str!("bar"),])); + assert_true!(tree.clone().matches(&[str!("foo"), str!("bar"), str!("bar"),])); assert_false!(tree.clone().matches(&[str!("bar"), str!("foo"),])); } @@ -1186,9 +1126,7 @@ mod tests { let tree = CssSyntax::new("foo bar+ baz").compile().unwrap(); assert_false!(tree.clone().matches(&[str!("foo")])); assert_false!(tree.clone().matches(&[str!("foo")])); - assert_true!(tree - .clone() - .matches(&[str!("foo"), str!("bar"), str!("baz"),])); + assert_true!(tree.clone().matches(&[str!("foo"), str!("bar"), str!("baz"),])); assert_false!(tree.clone().matches(&[str!("foo"), str!("baz"),])); assert_true!(tree.clone().matches(&[ str!("foo"), @@ -1211,18 +1149,14 @@ mod tests { assert_false!(tree.clone().matches(&[str!("foo")])); assert_false!(tree.clone().matches(&[str!("bar")])); assert_true!(tree.clone().matches(&[str!("foo"), str!("bar"),])); - assert_true!(tree - .clone() - .matches(&[str!("foo"), str!("bar"), str!("bar"),])); + assert_true!(tree.clone().matches(&[str!("foo"), str!("bar"), str!("bar"),])); assert_false!(tree.clone().matches(&[str!("bar"), str!("foo"),])); let tree = CssSyntax::new("foo+ bar+").compile().unwrap(); assert_false!(tree.clone().matches(&[str!("foo")])); assert_false!(tree.clone().matches(&[str!("bar")])); assert_true!(tree.clone().matches(&[str!("foo"), str!("bar"),])); - assert_true!(tree - .clone() - .matches(&[str!("foo"), str!("bar"), str!("bar"),])); + assert_true!(tree.clone().matches(&[str!("foo"), str!("bar"), str!("bar"),])); assert_true!(tree .clone() .matches(&[str!("foo"), str!("foo"), str!("bar"), str!("bar"),])); @@ -1235,20 +1169,14 @@ mod tests { let tree = CssSyntax::new("foo bar{1,3} baz").compile().unwrap(); assert_false!(tree.clone().matches(&[str!("foo")])); assert_false!(tree.clone().matches(&[str!("foo")])); - assert_true!(tree - .clone() - .matches(&[str!("foo"), str!("bar"), str!("baz"),])); + assert_true!(tree.clone().matches(&[str!("foo"), str!("bar"), str!("baz"),])); assert_false!(tree.clone().matches(&[str!("foo"), str!("baz"),])); assert_true!(tree .clone() .matches(&[str!("foo"), str!("bar"), str!("bar"), str!("baz"),])); - assert_true!(tree.clone().matches(&[ - str!("foo"), - str!("bar"), - str!("bar"), - str!("bar"), - str!("baz"), - ])); + assert_true!(tree + .clone() + .matches(&[str!("foo"), str!("bar"), str!("bar"), str!("bar"), str!("baz"),])); assert_false!(tree.clone().matches(&[ str!("foo"), str!("bar"), @@ -1270,16 +1198,10 @@ mod tests { assert_true!(tree.clone().matches(&[str!("foo")])); assert_true!(tree.clone().matches(&[str!("foo")])); assert_true!(tree.clone().matches(&[str!("foo"), str!("bar"),])); - assert_true!(tree + assert_true!(tree.clone().matches(&[str!("foo"), str!("bar"), str!("bar"),])); + assert_false!(tree .clone() - .matches(&[str!("foo"), str!("bar"), str!("bar"),])); - assert_false!(tree.clone().matches(&[ - str!("foo"), - str!("bar"), - str!("bar"), - str!("bar"), - str!("bar"), - ])); + .matches(&[str!("foo"), str!("bar"), str!("bar"), str!("bar"), str!("bar"),])); assert_false!(tree.clone().matches(&[str!("bar"), str!("foo"),])); } @@ -1291,11 +1213,9 @@ mod tests { PropertyDefinition { name: "testprop".to_string(), computed: vec![], - syntax: CssSyntax::new( - "[ left | right ] ? | [ top | bottom ] | [ top | bottom ]", - ) - .compile() - .unwrap(), + syntax: CssSyntax::new("[ left | right ] ? | [ top | bottom ] | [ top | bottom ]") + .compile() + .unwrap(), inherited: false, initial_value: None, resolved: false, @@ -1306,12 +1226,8 @@ mod tests { let prop = definitions.find_property("testprop").unwrap(); - assert_true!(prop - .clone() - .matches(&[str!("left"), CssValue::Unit(5.0, "px".into()),])); - assert_true!(prop - .clone() - .matches(&[str!("top"), CssValue::Unit(5.0, "px".into()),])); + assert_true!(prop.clone().matches(&[str!("left"), CssValue::Unit(5.0, "px".into()),])); + assert_true!(prop.clone().matches(&[str!("top"), CssValue::Unit(5.0, "px".into()),])); assert_true!(prop .clone() .matches(&[str!("bottom"), CssValue::Unit(5.0, "px".into()),])); @@ -1362,21 +1278,16 @@ mod tests { assert_true!(prop .clone() .matches(&[CssValue::Percentage(10.0), CssValue::Percentage(20.0),])); - assert_true!(prop.clone().matches(&[ - CssValue::Unit(10.0, "px".into()), - CssValue::Percentage(20.0), - ])); assert_true!(prop .clone() - .matches(&[str!("left"), CssValue::Percentage(20.0),])); + .matches(&[CssValue::Unit(10.0, "px".into()), CssValue::Percentage(20.0),])); + assert_true!(prop.clone().matches(&[str!("left"), CssValue::Percentage(20.0),])); assert_true!(prop .clone() .matches(&[CssValue::Unit(10.0, "px".into()), str!("center"),])); - assert_true!(prop - .clone() - .matches(&[CssValue::Percentage(10.0), str!("top"),])); + assert_true!(prop.clone().matches(&[CssValue::Percentage(10.0), str!("top"),])); assert_true!(prop.clone().matches(&[str!("right")])); @@ -1391,9 +1302,7 @@ mod tests { PropertyDefinition { name: "testprop".to_string(), computed: vec![], - syntax: CssSyntax::new("foo | [ foo [ foo | bar ] ]") - .compile() - .unwrap(), + syntax: CssSyntax::new("foo | [ foo [ foo | bar ] ]").compile().unwrap(), inherited: false, initial_value: None, resolved: false, @@ -1575,9 +1484,7 @@ mod tests { assert_true!(prop.clone().matches(&[str!("left"),])); assert_true!(prop.clone().matches(&[str!("right"),])); - assert_true!(prop - .clone() - .matches(&[str!("top"), CssValue::Unit(10.0, "px".into()),])); + assert_true!(prop.clone().matches(&[str!("top"), CssValue::Unit(10.0, "px".into()),])); assert_true!(prop .clone() .matches(&[str!("bottom"), CssValue::Unit(10.0, "px".into()),])); @@ -1591,22 +1498,10 @@ mod tests { let tree = CssSyntax::new("[foo | bar | baz]#").compile().unwrap(); assert_true!(tree.matches(&[str!("foo")])); assert_true!(tree.matches(&[str!("foo"), CssValue::Comma, str!("foo")])); - assert_true!(tree.matches(&[ - str!("foo"), - CssValue::Comma, - str!("foo"), - CssValue::Comma, - str!("foo") - ])); + assert_true!(tree.matches(&[str!("foo"), CssValue::Comma, str!("foo"), CssValue::Comma, str!("foo")])); assert_true!(tree.matches(&[str!("foo"), CssValue::Comma, str!("bar")])); assert_true!(tree.matches(&[str!("foo"), CssValue::Comma, str!("baz")])); - assert_true!(tree.matches(&[ - str!("foo"), - CssValue::Comma, - str!("bar"), - CssValue::Comma, - str!("baz") - ])); + assert_true!(tree.matches(&[str!("foo"), CssValue::Comma, str!("bar"), CssValue::Comma, str!("baz")])); assert_false!(tree.matches(&[str!("foo"), CssValue::Comma])); assert_false!(tree.matches(&[str!("foo"), CssValue::Comma, str!("bar"), CssValue::Comma])); diff --git a/crates/gosub_css3/src/matcher/walker.rs b/crates/gosub_css3/src/matcher/walker.rs new file mode 100644 index 000000000..dccfc5440 --- /dev/null +++ b/crates/gosub_css3/src/matcher/walker.rs @@ -0,0 +1,48 @@ +use crate::matcher::syntax_matcher::CssSyntaxTree; + +pub struct MatchWalker { + tree: CssSyntaxTree, +} + +impl MatchWalker { + pub fn new(tree: &CssSyntaxTree) -> Self { + Self { tree: tree.clone() } + } + + // pub fn walk(&self) { + // let _ = self.inner_walk(&self.tree); + // } + // + // fn inner_walk(&self, node: &Node) -> Result<(), std::io::Error> { + // match node.node_type.deref() { + // NodeType::StyleSheet { children } => { + // for child in children.iter() { + // self.inner_walk(child)?; + // } + // } + // NodeType::Rule { block, .. } => { + // if block.is_some() { + // self.inner_walk(block.as_ref().unwrap())?; + // } + // } + // NodeType::AtRule { block, .. } => { + // if block.is_some() { + // self.inner_walk(block.as_ref().unwrap())?; + // } + // } + // NodeType::Block { children, .. } => { + // for child in children.iter() { + // self.inner_walk(child)?; + // } + // } + // NodeType::Declaration { property, value, .. } => { + // println!("Matching property '{}' against value '{:?}'", property, value); + // + // + // } + // _ => {} + // } + // + // Ok(()) + // } +} diff --git a/crates/gosub_css3/src/node.rs b/crates/gosub_css3/src/node.rs index 34e09fbee..d1a585ae8 100644 --- a/crates/gosub_css3/src/node.rs +++ b/crates/gosub_css3/src/node.rs @@ -395,11 +395,7 @@ impl Display for Node { .map(|s| s.to_string()) .collect::>() .join(", "), - NodeType::Selector { children } => children - .iter() - .map(|s| s.to_string()) - .collect::>() - .join(""), + NodeType::Selector { children } => children.iter().map(|s| s.to_string()).collect::>().join(""), NodeType::IdSelector { value } => value.clone(), NodeType::Ident { value } => value.clone(), NodeType::Number { value } => value.to_string(), @@ -422,10 +418,7 @@ impl Display for Node { value, flags, } => { - let matcher = matcher - .as_ref() - .map(|m| m.to_string()) - .unwrap_or("".to_string()); + let matcher = matcher.as_ref().map(|m| m.to_string()).unwrap_or("".to_string()); format!("[{}{}{}{}]", name, matcher, value, flags) } NodeType::PseudoClassSelector { value } => format!(":{}", value), @@ -441,10 +434,7 @@ impl Display for Node { } NodeType::Combinator { value } => value.clone(), NodeType::Nth { nth, selector } => { - let sel = selector - .as_ref() - .map(|s| s.to_string()) - .unwrap_or("".to_string()); + let sel = selector.as_ref().map(|s| s.to_string()).unwrap_or("".to_string()); format!("{}{}", nth, sel) } NodeType::AnPlusB { a, b } => format!("{}n+{}", a, b), diff --git a/crates/gosub_css3/src/parser.rs b/crates/gosub_css3/src/parser.rs index 6d2649f18..586486b1f 100644 --- a/crates/gosub_css3/src/parser.rs +++ b/crates/gosub_css3/src/parser.rs @@ -1,5 +1,6 @@ use crate::tokenizer::{Number, Token, TokenType}; -use crate::{Css3, Error}; +use crate::Css3; +use gosub_shared::errors::{CssError, CssResult}; mod anplusb; mod at_rule; @@ -21,11 +22,11 @@ mod value; impl Css3<'_> { /// Consumes a specific token - pub fn consume(&mut self, token_type: TokenType) -> Result { + pub fn consume(&mut self, token_type: TokenType) -> CssResult { let t = self.tokenizer.consume(); if t.token_type != token_type { - return Err(Error::new( - format!("Expected {:?}, got {:?}", token_type, t), + return Err(CssError::with_location( + format!("Expected {:?}, got {:?}", token_type, t).as_str(), self.tokenizer.current_location(), )); } @@ -34,60 +35,60 @@ impl Css3<'_> { } /// Consumes any token - pub fn consume_any(&mut self) -> Result { + pub fn consume_any(&mut self) -> CssResult { Ok(self.tokenizer.consume()) } - pub fn consume_function(&mut self) -> Result { + pub fn consume_function(&mut self) -> CssResult { let t = self.tokenizer.consume(); match t.token_type { TokenType::Function(name) => Ok(name), - _ => Err(Error::new( - format!("Expected function, got {:?}", t), + _ => Err(CssError::with_location( + format!("Expected function, got {:?}", t).as_str(), self.tokenizer.current_location(), )), } } - pub fn consume_any_number(&mut self) -> Result { + pub fn consume_any_number(&mut self) -> CssResult { let t = self.tokenizer.consume(); match t.token_type { TokenType::Number(value) => Ok(value), - _ => Err(Error::new( - format!("Expected number, got {:?}", t), + _ => Err(CssError::with_location( + format!("Expected number, got {:?}", t).as_str(), self.tokenizer.current_location(), )), } } - pub fn consume_any_delim(&mut self) -> Result { + pub fn consume_any_delim(&mut self) -> CssResult { let t = self.tokenizer.consume(); match t.token_type { TokenType::Delim(c) => Ok(c), - _ => Err(Error::new( - format!("Expected delimiter, got {:?}", t), + _ => Err(CssError::with_location( + format!("Expected delimiter, got {:?}", t).as_str(), self.tokenizer.current_location(), )), } } - pub fn consume_any_string(&mut self) -> Result { + pub fn consume_any_string(&mut self) -> CssResult { let t = self.tokenizer.consume(); match t.token_type { TokenType::QuotedString(s) => Ok(s), - _ => Err(Error::new( - format!("Expected string, got {:?}", t), + _ => Err(CssError::with_location( + format!("Expected string, got {:?}", t).as_str(), self.tokenizer.current_location(), )), } } - pub fn consume_delim(&mut self, delimiter: char) -> Result { + pub fn consume_delim(&mut self, delimiter: char) -> CssResult { let t = self.tokenizer.consume(); match t.token_type { TokenType::Delim(c) if c == delimiter => Ok(c), - _ => Err(Error::new( - format!("Expected delimiter '{}', got {:?}", delimiter, t), + _ => Err(CssError::with_location( + format!("Expected delimiter '{}', got {:?}", delimiter, t).as_str(), self.tokenizer.current_location(), )), } @@ -108,29 +109,29 @@ impl Css3<'_> { } } - pub fn consume_ident_ci(&mut self, ident: &str) -> Result { + pub fn consume_ident_ci(&mut self, ident: &str) -> CssResult { let t = self.tokenizer.consume(); match t.token_type { TokenType::Ident(s) if s.eq_ignore_ascii_case(ident) => Ok(s), - _ => Err(Error::new( - format!("Expected ident, got {:?}", t), + _ => Err(CssError::with_location( + format!("Expected ident, got {:?}", t).as_str(), self.tokenizer.current_location(), )), } } - pub fn consume_ident(&mut self, ident: &str) -> Result { + pub fn consume_ident(&mut self, ident: &str) -> CssResult { let t = self.tokenizer.consume(); match t.token_type { TokenType::Ident(s) if s == ident => Ok(s), - _ => Err(Error::new( - format!("Expected ident, got {:?}", t), + _ => Err(CssError::with_location( + format!("Expected ident, got {:?}", t).as_str(), self.tokenizer.current_location(), )), } } - pub fn consume_any_ident(&mut self) -> Result { + pub fn consume_any_ident(&mut self) -> CssResult { let t = self.tokenizer.consume(); match t.token_type { @@ -138,21 +139,21 @@ impl Css3<'_> { let t = self.tokenizer.consume(); match t.token_type { TokenType::Ident(s) => Ok(format!(".{}", s)), - _ => Err(Error::new( - format!("Expected ident, got {:?}", t), + _ => Err(CssError::with_location( + format!("Expected ident, got {:?}", t).as_str(), self.tokenizer.current_location(), )), } } TokenType::Ident(s) => Ok(s), - _ => Err(Error::new( - format!("Expected ident, got {:?}", t), + _ => Err(CssError::with_location( + format!("Expected ident, got {:?}", t).as_str(), self.tokenizer.current_location(), )), } } - pub fn consume_raw_condition(&mut self) -> Result { + pub fn consume_raw_condition(&mut self) -> CssResult { let start = self.tokenizer.tell(); while !self.tokenizer.eof() { diff --git a/crates/gosub_css3/src/parser/anplusb.rs b/crates/gosub_css3/src/parser/anplusb.rs index 5782acc3e..de440997e 100644 --- a/crates/gosub_css3/src/parser/anplusb.rs +++ b/crates/gosub_css3/src/parser/anplusb.rs @@ -1,20 +1,17 @@ use crate::node::{Node, NodeType}; use crate::tokenizer::{Number, TokenType}; -use crate::{Css3, Error}; +use crate::Css3; +use gosub_shared::errors::{CssError, CssResult}; impl Css3<'_> { - fn do_dimension_block( - &mut self, - value: Number, - unit: String, - ) -> Result<(String, String), Error> { + fn do_dimension_block(&mut self, value: Number, unit: String) -> CssResult<(String, String)> { log::trace!("do_dimension_block"); let value = value.to_string(); if unit.chars().nth(0).unwrap().to_lowercase().to_string() != "n" { - return Err(Error::new( - format!("Expected n, found {}", unit).to_string(), + return Err(CssError::with_location( + format!("Expected n, found {}", unit).as_str(), self.tokenizer.current_location(), )); } @@ -25,24 +22,14 @@ impl Css3<'_> { }) } - fn check_integer( - &mut self, - value: &str, - offset: usize, - allow_sign: bool, - ) -> Result { - let sign = value - .chars() - .nth(offset) - .unwrap_or(' ') - .to_lowercase() - .to_string(); + fn check_integer(&mut self, value: &str, offset: usize, allow_sign: bool) -> CssResult { + let sign = value.chars().nth(offset).unwrap_or(' ').to_lowercase().to_string(); let mut pos = offset; if sign == "+" || sign == "-" { if !allow_sign { - return Err(Error::new( - format!("Unexpected sign {}", sign).to_string(), + return Err(CssError::with_location( + format!("Unexpected sign {}", sign).as_str(), self.tokenizer.current_location(), )); } @@ -58,16 +45,11 @@ impl Css3<'_> { Ok(true) } - fn expect_char(&mut self, value: &str, c: &str, offset: usize) -> Result { - let nval = value - .chars() - .nth(offset) - .unwrap_or(' ') - .to_lowercase() - .to_string(); + fn expect_char(&mut self, value: &str, c: &str, offset: usize) -> CssResult { + let nval = value.chars().nth(offset).unwrap_or(' ').to_lowercase().to_string(); if nval != c { - return Err(Error::new( - format!("Expected {}", c).to_string(), + return Err(CssError::with_location( + format!("Expected {}", c).as_str(), self.tokenizer.current_location(), )); } @@ -75,7 +57,7 @@ impl Css3<'_> { Ok(true) } - fn parse_anplusb_b(&mut self) -> Result { + fn parse_anplusb_b(&mut self) -> CssResult { log::trace!("parse_anplusb_b"); self.consume_whitespace_comments(); @@ -107,12 +89,12 @@ impl Css3<'_> { false } _ => { - return Err(Error::new( + return Err(CssError::with_location( format!( "Expected +, - or number, found {:?}", self.tokenizer.lookahead(0).token_type ) - .to_string(), + .as_str(), self.tokenizer.current_location(), )); } @@ -128,7 +110,7 @@ impl Css3<'_> { Ok(val.to_string()) } - fn do_negative_block(&mut self, value: &str) -> Result<(String, String), Error> { + fn do_negative_block(&mut self, value: &str) -> CssResult<(String, String)> { log::trace!("do_negative_block"); let a = String::from("-1"); @@ -162,7 +144,7 @@ impl Css3<'_> { Ok((a, b)) } - fn do_plus_block(&mut self, value: &str) -> Result<(String, String), Error> { + fn do_plus_block(&mut self, value: &str) -> CssResult<(String, String)> { log::trace!("do_plus_block"); let a = String::from("1"); @@ -196,7 +178,7 @@ impl Css3<'_> { Ok((a, b)) } - pub fn parse_anplusb(&mut self) -> Result { + pub fn parse_anplusb(&mut self) -> CssResult { log::trace!("parse_anplusb"); let loc = self.tokenizer.current_location(); @@ -228,8 +210,8 @@ impl Css3<'_> { } _ => { self.tokenizer.reconsume(); - return Err(Error::new( - "Expected anplusb".to_string(), + return Err(CssError::with_location( + "Expected anplusb", self.tokenizer.current_location(), )); } @@ -251,6 +233,8 @@ impl Css3<'_> { mod test { use super::*; use gosub_shared::byte_stream::{ByteStream, Encoding}; + use gosub_shared::traits::css3::CssOrigin; + use gosub_shared::traits::ParserConfig; macro_rules! test { ($func:ident, $input:expr, $expected:expr) => { @@ -258,7 +242,7 @@ mod test { stream.read_from_str($input, Some(Encoding::UTF8)); stream.close(); - let mut parser = crate::Css3::new(&mut stream); + let mut parser = crate::Css3::new(&mut stream, ParserConfig::default(), CssOrigin::User, ""); let result = parser.$func().unwrap(); assert_eq!(result.node_type, $expected); diff --git a/crates/gosub_css3/src/parser/at_rule.rs b/crates/gosub_css3/src/parser/at_rule.rs index 6fb154577..409ad909d 100644 --- a/crates/gosub_css3/src/parser/at_rule.rs +++ b/crates/gosub_css3/src/parser/at_rule.rs @@ -12,7 +12,8 @@ mod supports; use crate::node::{Node, NodeType}; use crate::parser::block::BlockParseMode; use crate::tokenizer::TokenType; -use crate::{Css3, Error}; +use crate::Css3; +use gosub_shared::errors::{CssError, CssResult}; impl Css3<'_> { fn declaration_block_at_rule(&mut self) -> BlockParseMode { @@ -41,10 +42,10 @@ impl Css3<'_> { } } - fn read_sequence_at_rule_prelude(&mut self) -> Result { + fn read_sequence_at_rule_prelude(&mut self) -> CssResult { log::trace!("read_sequence_at_rule_prelude"); - let loc = self.tokenizer.lookahead(0).location.clone(); + let loc = self.tokenizer.lookahead(0).location; Ok(Node::new( NodeType::Container { @@ -54,7 +55,7 @@ impl Css3<'_> { )) } - fn parse_at_rule_prelude(&mut self, name: String) -> Result, Error> { + fn parse_at_rule_prelude(&mut self, name: String) -> CssResult> { log::trace!("parse_at_rule_prelude"); self.consume_whitespace_comments(); @@ -75,24 +76,17 @@ impl Css3<'_> { self.consume_whitespace_comments(); let t = self.tokenizer.lookahead(0); - if !self.tokenizer.eof() - && t.token_type != TokenType::Semicolon - && t.token_type != TokenType::LCurly - { - return Err(Error::new( - "Expected semicolon or left curly brace".to_string(), - t.location.clone(), + if !self.tokenizer.eof() && t.token_type != TokenType::Semicolon && t.token_type != TokenType::LCurly { + return Err(CssError::with_location( + "Expected semicolon or left curly brace", + t.location, )); } Ok(node) } - fn parse_at_rule_block( - &mut self, - name: String, - is_declaration: bool, - ) -> Result, Error> { + fn parse_at_rule_block(&mut self, name: String, is_declaration: bool) -> CssResult> { log::trace!("parse_at_rule_block"); let t = self.tokenizer.consume(); @@ -136,7 +130,7 @@ impl Css3<'_> { // Either the at_rule parsing succeeds as a whole, or not. When not a valid at_rule is found, we // return None if the config.ignore_errors is set to true, otherwise this will return an Err // and is handled by the caller - pub fn parse_at_rule(&mut self, is_declaration: bool) -> Result, Error> { + pub fn parse_at_rule(&mut self, is_declaration: bool) -> CssResult> { log::trace!("parse_at_rule"); let result = self.parse_at_rule_internal(is_declaration); @@ -153,14 +147,14 @@ impl Css3<'_> { Ok(None) } - fn parse_at_rule_internal(&mut self, is_declaration: bool) -> Result { + fn parse_at_rule_internal(&mut self, is_declaration: bool) -> CssResult { let name; let t = self.consume_any()?; if let TokenType::AtKeyword(keyword) = t.token_type { name = keyword; } else { - return Err(Error::new("Expected at keyword".to_string(), t.location)); + return Err(CssError::with_location("Expected at keyword", t.location)); } self.consume_whitespace_comments(); @@ -176,7 +170,7 @@ impl Css3<'_> { prelude, block, }, - t.location.clone(), + t.location, )) } } diff --git a/crates/gosub_css3/src/parser/at_rule/container.rs b/crates/gosub_css3/src/parser/at_rule/container.rs index d1f3129c2..df6025479 100644 --- a/crates/gosub_css3/src/parser/at_rule/container.rs +++ b/crates/gosub_css3/src/parser/at_rule/container.rs @@ -1,9 +1,10 @@ use crate::node::{FeatureKind, Node, NodeType}; use crate::tokenizer::TokenType; -use crate::{Css3, Error}; +use crate::Css3; +use gosub_shared::errors::CssResult; impl Css3<'_> { - pub fn parse_at_rule_container_prelude(&mut self) -> Result { + pub fn parse_at_rule_container_prelude(&mut self) -> CssResult { log::trace!("parse_at_rule_container_prelude"); let mut children = Vec::new(); @@ -11,15 +12,12 @@ impl Css3<'_> { let t = self.consume_any()?; if let TokenType::Ident(value) = t.token_type { if !["none", "and", "not", "or"].contains(&value.as_str()) { - children.push(Node::new(NodeType::Ident { value }, t.location.clone())); + children.push(Node::new(NodeType::Ident { value }, t.location)); } } children.push(self.parse_condition(FeatureKind::Container)?); - Ok(Node::new( - NodeType::Container { children }, - t.location.clone(), - )) + Ok(Node::new(NodeType::Container { children }, t.location)) } } diff --git a/crates/gosub_css3/src/parser/at_rule/font_face.rs b/crates/gosub_css3/src/parser/at_rule/font_face.rs index 7310a08f5..dc9248e4d 100644 --- a/crates/gosub_css3/src/parser/at_rule/font_face.rs +++ b/crates/gosub_css3/src/parser/at_rule/font_face.rs @@ -1,8 +1,9 @@ use crate::node::Node; -use crate::{Css3, Error}; +use crate::Css3; +use gosub_shared::errors::CssResult; impl Css3<'_> { - pub fn parse_at_rule_font_face_block(&mut self) -> Result { + pub fn parse_at_rule_font_face_block(&mut self) -> CssResult { log::trace!("parse_at_rule_font_face_block"); todo!(); diff --git a/crates/gosub_css3/src/parser/at_rule/import.rs b/crates/gosub_css3/src/parser/at_rule/import.rs index 9aa50aaa0..088c3f2bc 100644 --- a/crates/gosub_css3/src/parser/at_rule/import.rs +++ b/crates/gosub_css3/src/parser/at_rule/import.rs @@ -1,9 +1,10 @@ use crate::node::{Node, NodeType}; use crate::tokenizer::TokenType; -use crate::{Css3, Error}; +use crate::Css3; +use gosub_shared::errors::{CssError, CssResult}; impl Css3<'_> { - pub fn parse_at_rule_import_prelude(&mut self) -> Result { + pub fn parse_at_rule_import_prelude(&mut self) -> CssResult { log::trace!("parse_at_rule_import"); let mut children = Vec::new(); @@ -13,19 +14,19 @@ impl Css3<'_> { let t = self.consume_any()?; match t.token_type { TokenType::QuotedString(value) => { - children.push(Node::new(NodeType::String { value }, loc.clone())); + children.push(Node::new(NodeType::String { value }, loc)); } TokenType::Url(url) => { - children.push(Node::new(NodeType::Url { url }, loc.clone())); + children.push(Node::new(NodeType::Url { url }, loc)); } TokenType::Function(name) if name.eq_ignore_ascii_case("url") => { self.tokenizer.reconsume(); children.push(self.parse_url()?); } _ => { - return Err(Error::new( - "Expected string or url()".to_string(), - t.location.clone(), + return Err(CssError::with_location( + "Expected string or url()", + self.tokenizer.current_location(), )); } } @@ -35,7 +36,7 @@ impl Css3<'_> { let t = self.tokenizer.lookahead_sc(0); match t.token_type { TokenType::Ident(value) if value.eq_ignore_ascii_case("layer") => { - children.push(Node::new(NodeType::Ident { value }, t.location.clone())); + children.push(Node::new(NodeType::Ident { value }, t.location)); } TokenType::Function(name) if name.eq_ignore_ascii_case("layer") => { children.push(self.parse_function()?); @@ -69,6 +70,6 @@ impl Css3<'_> { // _ => {} // } - Ok(Node::new(NodeType::ImportList { children }, loc.clone())) + Ok(Node::new(NodeType::ImportList { children }, loc)) } } diff --git a/crates/gosub_css3/src/parser/at_rule/layer.rs b/crates/gosub_css3/src/parser/at_rule/layer.rs index fd797ea1f..6ca1e1dd4 100644 --- a/crates/gosub_css3/src/parser/at_rule/layer.rs +++ b/crates/gosub_css3/src/parser/at_rule/layer.rs @@ -1,20 +1,21 @@ use crate::node::{Node, NodeType}; -use crate::{Css3, Error}; +use crate::Css3; +use gosub_shared::errors::CssResult; impl Css3<'_> { #[allow(dead_code)] - fn parse_at_rule_layer_list(&mut self) -> Result { + fn parse_at_rule_layer_list(&mut self) -> CssResult { let _children: Vec = Vec::new(); todo!(); } - fn parse_layer_query(&mut self) -> Result { + fn parse_layer_query(&mut self) -> CssResult { let _children: Vec = Vec::new(); todo!(); } - pub fn parse_at_rule_layer_prelude(&mut self) -> Result { + pub fn parse_at_rule_layer_prelude(&mut self) -> CssResult { log::trace!("parse_at_rule_layer_prelude"); let loc = self.tokenizer.current_location(); diff --git a/crates/gosub_css3/src/parser/at_rule/media.rs b/crates/gosub_css3/src/parser/at_rule/media.rs index 50e05f2cc..421d92263 100644 --- a/crates/gosub_css3/src/parser/at_rule/media.rs +++ b/crates/gosub_css3/src/parser/at_rule/media.rs @@ -1,9 +1,10 @@ use crate::node::{FeatureKind, Node, NodeType}; use crate::tokenizer::TokenType; -use crate::{Css3, Error}; +use crate::Css3; +use gosub_shared::errors::{CssError, CssResult}; impl Css3<'_> { - fn parse_media_read_term(&mut self) -> Result { + fn parse_media_read_term(&mut self) -> CssResult { self.consume_whitespace_comments(); let loc = self.tokenizer.current_location(); @@ -12,9 +13,7 @@ impl Css3<'_> { match t.token_type { TokenType::Ident(ident) => Ok(Node::new(NodeType::Ident { value: ident }, loc)), TokenType::Number(value) => Ok(Node::new(NodeType::Number { value }, loc)), - TokenType::Dimension { value, unit } => { - Ok(Node::new(NodeType::Dimension { value, unit }, loc)) - } + TokenType::Dimension { value, unit } => Ok(Node::new(NodeType::Dimension { value, unit }, loc)), TokenType::Function(name) => { let name = name.to_lowercase(); let args = self.parse_pseudo_function(name.as_str())?; @@ -28,14 +27,14 @@ impl Css3<'_> { loc, )) } - _ => Err(Error::new( - "Expected identifier, number, dimension, or ratio".to_string(), + _ => Err(CssError::with_location( + "Expected identifier, number, dimension, or ratio", loc, )), } } - fn parse_media_read_comparison(&mut self) -> Result { + fn parse_media_read_comparison(&mut self) -> CssResult { self.consume_whitespace_comments(); let loc = self.tokenizer.current_location(); @@ -55,10 +54,10 @@ impl Css3<'_> { return Ok(Node::new(NodeType::Operator(format!("{}", delim)), loc)); } - Err(Error::new("Expected comparison operator".to_string(), loc)) + Err(CssError::with_location("Expected comparison operator", loc)) } - pub fn parse_media_query_list(&mut self) -> Result { + pub fn parse_media_query_list(&mut self) -> CssResult { log::trace!("parse_media_query_list"); let loc = self.tokenizer.current_location(); @@ -78,15 +77,10 @@ impl Css3<'_> { } } - Ok(Node::new( - NodeType::MediaQueryList { - media_queries: queries, - }, - loc, - )) + Ok(Node::new(NodeType::MediaQueryList { media_queries: queries }, loc)) } - fn parse_media_feature_feature(&mut self, kind: FeatureKind) -> Result { + fn parse_media_feature_feature(&mut self, kind: FeatureKind) -> CssResult { log::trace!("parse_media_feature_feature"); let loc = self.tokenizer.current_location(); @@ -105,7 +99,7 @@ impl Css3<'_> { if t.token_type != TokenType::RParen { if !t.is_colon() { - return Err(Error::new("Expected colon".to_string(), t.location)); + return Err(CssError::with_location("Expected colon", t.location)); } self.consume_whitespace_comments(); @@ -130,8 +124,8 @@ impl Css3<'_> { )) } _ => { - return Err(Error::new( - "Expected identifier, number, dimension, or ratio".to_string(), + return Err(CssError::with_location( + "Expected identifier, number, dimension, or ratio", t.location, )); } @@ -147,7 +141,7 @@ impl Css3<'_> { Ok(Node::new(NodeType::Feature { kind, name, value }, loc)) } - fn parse_media_feature_range(&mut self, _kind: FeatureKind) -> Result { + fn parse_media_feature_range(&mut self, _kind: FeatureKind) -> CssResult { log::trace!("parse_media_feature_range"); let loc = self.tokenizer.current_location(); @@ -181,7 +175,7 @@ impl Css3<'_> { )) } - pub fn parse_media_feature_or_range(&mut self, kind: FeatureKind) -> Result { + pub fn parse_media_feature_or_range(&mut self, kind: FeatureKind) -> CssResult { log::trace!("parse_media_feature_or_range"); let t = self.tokenizer.lookahead_sc(1); @@ -195,7 +189,7 @@ impl Css3<'_> { self.parse_media_feature_range(kind) } - pub fn parse_media_query(&mut self) -> Result { + pub fn parse_media_query(&mut self) -> CssResult { log::trace!("parse_media_query"); let loc = self.tokenizer.current_location(); @@ -228,7 +222,7 @@ impl Css3<'_> { match nt.token_type { TokenType::Ident(s) => { if s != "and" { - return Err(Error::new("Expected 'and'".to_string(), nt.location)); + return Err(CssError::with_location("Expected 'and'", t.location)); } self.consume_ident("and")?; @@ -238,8 +232,8 @@ impl Css3<'_> { // skip; } _ => { - return Err(Error::new( - "Expected identifier or parenthesis".to_string(), + return Err(CssError::with_location( + "Expected identifier or parenthesis", t.location, )); } @@ -255,8 +249,8 @@ impl Css3<'_> { // skip } _ => { - return Err(Error::new( - "Expected identifier or parenthesis".to_string(), + return Err(CssError::with_location( + "Expected identifier or parenthesis", t.location, )); } @@ -273,7 +267,7 @@ impl Css3<'_> { )) } - pub fn parse_at_rule_media_prelude(&mut self) -> Result { + pub fn parse_at_rule_media_prelude(&mut self) -> CssResult { log::trace!("parse_at_rule_media_prelude"); self.parse_media_query_list() diff --git a/crates/gosub_css3/src/parser/at_rule/nest.rs b/crates/gosub_css3/src/parser/at_rule/nest.rs index f1fd96c9a..a54420280 100644 --- a/crates/gosub_css3/src/parser/at_rule/nest.rs +++ b/crates/gosub_css3/src/parser/at_rule/nest.rs @@ -1,8 +1,9 @@ use crate::node::{Node, NodeType}; -use crate::{Css3, Error}; +use crate::Css3; +use gosub_shared::errors::CssResult; impl Css3<'_> { - pub fn parse_at_rule_nest_prelude(&mut self) -> Result { + pub fn parse_at_rule_nest_prelude(&mut self) -> CssResult { log::trace!("parse_at_rule_nest_prelude"); let loc = self.tokenizer.current_location(); diff --git a/crates/gosub_css3/src/parser/at_rule/page.rs b/crates/gosub_css3/src/parser/at_rule/page.rs index 86e04f669..cbbb4dd58 100644 --- a/crates/gosub_css3/src/parser/at_rule/page.rs +++ b/crates/gosub_css3/src/parser/at_rule/page.rs @@ -1,8 +1,9 @@ use crate::node::{Node, NodeType}; -use crate::{Css3, Error}; +use crate::Css3; +use gosub_shared::errors::CssResult; impl Css3<'_> { - pub fn parse_at_rule_page_prelude(&mut self) -> Result { + pub fn parse_at_rule_page_prelude(&mut self) -> CssResult { log::trace!("parse_at_rule_page_prelude"); let loc = self.tokenizer.current_location(); diff --git a/crates/gosub_css3/src/parser/at_rule/scope.rs b/crates/gosub_css3/src/parser/at_rule/scope.rs index af0f3a1e0..f129ff378 100644 --- a/crates/gosub_css3/src/parser/at_rule/scope.rs +++ b/crates/gosub_css3/src/parser/at_rule/scope.rs @@ -1,9 +1,10 @@ use crate::node::{Node, NodeType}; use crate::tokenizer::TokenType; -use crate::{Css3, Error}; +use crate::Css3; +use gosub_shared::errors::CssResult; impl Css3<'_> { - pub fn parse_at_rule_scope_prelude(&mut self) -> Result { + pub fn parse_at_rule_scope_prelude(&mut self) -> CssResult { log::trace!("parse_at_rule_scope_prelude"); let mut root = None; diff --git a/crates/gosub_css3/src/parser/at_rule/starting_style.rs b/crates/gosub_css3/src/parser/at_rule/starting_style.rs index ea5f3c1a9..0189fe084 100644 --- a/crates/gosub_css3/src/parser/at_rule/starting_style.rs +++ b/crates/gosub_css3/src/parser/at_rule/starting_style.rs @@ -1,8 +1,9 @@ use crate::node::Node; -use crate::{Css3, Error}; +use crate::Css3; +use gosub_shared::errors::CssResult; impl Css3<'_> { - pub fn parse_at_rule_starting_style_block(&mut self) -> Result { + pub fn parse_at_rule_starting_style_block(&mut self) -> CssResult { log::trace!("parse_at_rule_starting_style_block"); todo!(); } diff --git a/crates/gosub_css3/src/parser/at_rule/supports.rs b/crates/gosub_css3/src/parser/at_rule/supports.rs index dfae55621..903397347 100644 --- a/crates/gosub_css3/src/parser/at_rule/supports.rs +++ b/crates/gosub_css3/src/parser/at_rule/supports.rs @@ -1,8 +1,9 @@ use crate::node::{Node, NodeType}; -use crate::{Css3, Error}; +use crate::Css3; +use gosub_shared::errors::CssResult; impl Css3<'_> { - pub fn parse_at_rule_supports_prelude(&mut self) -> Result { + pub fn parse_at_rule_supports_prelude(&mut self) -> CssResult { log::trace!("parse_at_rule_supports_prelude"); let loc = self.tokenizer.current_location(); @@ -17,6 +18,7 @@ impl Css3<'_> { #[cfg(test)] mod tests { use crate::walker::Walker; + use crate::{CssOrigin, ParserConfig}; use gosub_shared::byte_stream::{ByteStream, Encoding}; #[test] @@ -25,7 +27,7 @@ mod tests { stream.read_from_str("(display: flex)", Some(Encoding::UTF8)); stream.close(); - let mut parser = crate::Css3::new(&mut stream); + let mut parser = crate::Css3::new(&mut stream, ParserConfig::default(), CssOrigin::User, ""); let node = parser.parse_at_rule_supports_prelude().unwrap(); let w = Walker::new(&node); diff --git a/crates/gosub_css3/src/parser/block.rs b/crates/gosub_css3/src/parser/block.rs index 656e107b7..22334aca4 100644 --- a/crates/gosub_css3/src/parser/block.rs +++ b/crates/gosub_css3/src/parser/block.rs @@ -1,6 +1,7 @@ use crate::node::{Node, NodeType}; use crate::tokenizer::TokenType; -use crate::{Css3, Error}; +use crate::Css3; +use gosub_shared::errors::{CssError, CssResult}; #[derive(Debug, PartialEq)] pub enum BlockParseMode { @@ -9,12 +10,12 @@ pub enum BlockParseMode { } impl Css3<'_> { - fn parse_consume_rule(&mut self) -> Result, Error> { + fn parse_consume_rule(&mut self) -> CssResult> { log::trace!("parse_consume_rule"); self.parse_rule() } - fn parse_consume_declaration(&mut self) -> Result, Error> { + fn parse_consume_declaration(&mut self) -> CssResult> { log::trace!("parse_consume_declaration"); match self.parse_declaration()? { @@ -48,7 +49,7 @@ impl Css3<'_> { } } - pub fn parse_block(&mut self, mode: BlockParseMode) -> Result { + pub fn parse_block(&mut self, mode: BlockParseMode) -> CssResult { log::trace!("parse_block with parse mode: {:?}", mode); let loc = self.tokenizer.current_location(); @@ -62,7 +63,7 @@ impl Css3<'_> { // End the block self.tokenizer.reconsume(); - let n = Node::new(NodeType::Block { children }, t.location.clone()); + let n = Node::new(NodeType::Block { children }, t.location); return Ok(n); } TokenType::Whitespace(_) | TokenType::Comment(_) => { @@ -71,9 +72,7 @@ impl Css3<'_> { TokenType::AtKeyword(_) => { self.tokenizer.reconsume(); - if let Some(at_rule_node) = - self.parse_at_rule(mode == BlockParseMode::StyleBlock)? - { + if let Some(at_rule_node) = self.parse_at_rule(mode == BlockParseMode::StyleBlock)? { children.push(at_rule_node); } semicolon_seperated = false; @@ -85,8 +84,8 @@ impl Css3<'_> { _ => match mode { BlockParseMode::StyleBlock => { if !semicolon_seperated { - return Err(Error::new( - format!("Expected a ; got {:?}", t), + return Err(CssError::with_location( + format!("Expected a ; got {:?}", t).as_str(), self.tokenizer.current_location(), )); } diff --git a/crates/gosub_css3/src/parser/calc.rs b/crates/gosub_css3/src/parser/calc.rs index 74f3643d7..b9ae1d578 100644 --- a/crates/gosub_css3/src/parser/calc.rs +++ b/crates/gosub_css3/src/parser/calc.rs @@ -1,9 +1,10 @@ use crate::node::{Node, NodeType}; use crate::tokenizer::TokenType; -use crate::{Css3, Error}; +use crate::Css3; +use gosub_shared::errors::CssResult; impl Css3<'_> { - pub fn parse_calc(&mut self) -> Result { + pub fn parse_calc(&mut self) -> CssResult { log::trace!("parse_calc"); let loc = self.tokenizer.current_location(); @@ -13,7 +14,7 @@ impl Css3<'_> { Ok(Node::new(NodeType::Calc { expr }, loc)) } - fn parse_calc_expr(&mut self) -> Result { + fn parse_calc_expr(&mut self) -> CssResult { log::trace!("parse_calc_expr"); let loc = self.tokenizer.current_location(); diff --git a/crates/gosub_css3/src/parser/combinator.rs b/crates/gosub_css3/src/parser/combinator.rs index e8613044e..2003c8663 100644 --- a/crates/gosub_css3/src/parser/combinator.rs +++ b/crates/gosub_css3/src/parser/combinator.rs @@ -1,9 +1,11 @@ use crate::node::{Node, NodeType}; use crate::tokenizer::TokenType; -use crate::{Css3, Error}; +use crate::Css3; +use gosub_shared::errors::CssError; +use gosub_shared::errors::CssResult; impl Css3<'_> { - pub fn parse_combinator(&mut self) -> Result { + pub fn parse_combinator(&mut self) -> CssResult { log::trace!("parse_combinator"); let t = self.consume_any()?; @@ -15,20 +17,18 @@ impl Css3<'_> { TokenType::Delim('/') => { let tn1 = self.tokenizer.lookahead(1); let tn2 = self.tokenizer.lookahead(2); - if tn1.token_type == TokenType::Ident("deep".to_string()) - && tn2.token_type == TokenType::Delim('/') - { + if tn1.token_type == TokenType::Ident("deep".to_string()) && tn2.token_type == TokenType::Delim('/') { "/deep/".to_string() } else { - return Err(Error::new( - format!("Unexpected token {:?}", tn1), + return Err(CssError::with_location( + format!("Unexpected token {:?}", tn1).as_str(), self.tokenizer.current_location(), )); } } _ => { - return Err(Error::new( - format!("Unexpected token {:?}", t), + return Err(CssError::with_location( + format!("Unexpected token {:?}", t).as_str(), self.tokenizer.current_location(), )); } diff --git a/crates/gosub_css3/src/parser/condition.rs b/crates/gosub_css3/src/parser/condition.rs index e53234e5a..95cadbe29 100644 --- a/crates/gosub_css3/src/parser/condition.rs +++ b/crates/gosub_css3/src/parser/condition.rs @@ -1,9 +1,11 @@ use crate::node::{FeatureKind, Node, NodeType}; use crate::tokenizer::TokenType; -use crate::{Css3, Error}; +use crate::Css3; +use gosub_shared::errors::CssError; +use gosub_shared::errors::CssResult; impl Css3<'_> { - pub fn parse_condition(&mut self, kind: FeatureKind) -> Result { + pub fn parse_condition(&mut self, kind: FeatureKind) -> CssResult { log::trace!("parse_condition"); let loc = self.tokenizer.current_location(); @@ -55,7 +57,7 @@ impl Css3<'_> { } if list.is_empty() { - return Err(Error::new("Expected condition".to_string(), loc)); + return Err(CssError::with_location("Expected condition", loc)); } Ok(Node::new(NodeType::Condition { list }, loc)) diff --git a/crates/gosub_css3/src/parser/declaration.rs b/crates/gosub_css3/src/parser/declaration.rs index 4569ae8e4..f51c10245 100644 --- a/crates/gosub_css3/src/parser/declaration.rs +++ b/crates/gosub_css3/src/parser/declaration.rs @@ -1,9 +1,11 @@ use crate::node::{Node, NodeType}; use crate::tokenizer::TokenType; -use crate::{Css3, Error}; +use crate::Css3; +use gosub_shared::errors::CssError; +use gosub_shared::errors::CssResult; impl Css3<'_> { - pub fn parse_property_name(&mut self) -> Result { + pub fn parse_property_name(&mut self) -> CssResult { log::trace!("parse_property_name"); let t = self.consume_any()?; match t.token_type { @@ -27,14 +29,14 @@ impl Css3<'_> { match t.token_type { TokenType::Ident(value) => Ok(value), TokenType::Hash(value) => Ok(value), - _ => Err(Error::new( - format!("Unexpected token {:?}", t), + _ => Err(CssError::with_location( + format!("Unexpected token {:?}", t).as_str(), self.tokenizer.current_location(), )), } } - pub fn parse_declaration(&mut self) -> Result, Error> { + pub fn parse_declaration(&mut self) -> CssResult> { log::trace!("parse_declaration"); let result = self.parse_declaration_internal(); @@ -50,7 +52,7 @@ impl Css3<'_> { Ok(None) } - fn parse_declaration_internal(&mut self) -> Result { + fn parse_declaration_internal(&mut self) -> CssResult { let loc = self.tokenizer.current_location(); let mut important = false; @@ -69,8 +71,8 @@ impl Css3<'_> { let value = self.parse_value_sequence()?; if value.is_empty() { - return Err(Error::new( - "Expected value in declaration".to_string(), + return Err(CssError::with_location( + "Expected value in declaration", self.tokenizer.current_location(), )); } diff --git a/crates/gosub_css3/src/parser/feature_function.rs b/crates/gosub_css3/src/parser/feature_function.rs index 36ddfc6f1..db8a92166 100644 --- a/crates/gosub_css3/src/parser/feature_function.rs +++ b/crates/gosub_css3/src/parser/feature_function.rs @@ -1,13 +1,11 @@ use crate::node::{FeatureKind, Node, NodeType}; -use crate::{Css3, Error}; +use crate::Css3; +use gosub_shared::errors::CssResult; impl Css3<'_> { - pub fn parse_feature_function(&mut self, _kind: FeatureKind) -> Result { + pub fn parse_feature_function(&mut self, _kind: FeatureKind) -> CssResult { log::trace!("parse_feature_function"); - Ok(Node::new( - NodeType::FeatureFunction, - self.tokenizer.current_location(), - )) + Ok(Node::new(NodeType::FeatureFunction, self.tokenizer.current_location())) } } diff --git a/crates/gosub_css3/src/parser/function.rs b/crates/gosub_css3/src/parser/function.rs index 56ae01dfe..2f11af83b 100644 --- a/crates/gosub_css3/src/parser/function.rs +++ b/crates/gosub_css3/src/parser/function.rs @@ -1,14 +1,15 @@ use crate::node::{Node, NodeType}; use crate::tokenizer::TokenType; -use crate::{Css3, Error}; +use crate::Css3; +use gosub_shared::errors::CssResult; impl Css3<'_> { - fn parse_function_arguments(&mut self) -> Result, Error> { + fn parse_function_arguments(&mut self) -> CssResult> { log::trace!("parse_function_arguments"); self.parse_value_sequence() } - pub fn parse_function(&mut self) -> Result { + pub fn parse_function(&mut self) -> CssResult { log::trace!("parse_function"); let loc = self.tokenizer.current_location(); diff --git a/crates/gosub_css3/src/parser/operator.rs b/crates/gosub_css3/src/parser/operator.rs index 073f9522e..a78d087c0 100644 --- a/crates/gosub_css3/src/parser/operator.rs +++ b/crates/gosub_css3/src/parser/operator.rs @@ -1,9 +1,11 @@ use crate::node::{Node, NodeType}; use crate::tokenizer::TokenType; -use crate::{Css3, Error}; +use crate::Css3; +use gosub_shared::errors::CssError; +use gosub_shared::errors::CssResult; impl Css3<'_> { - pub fn parse_operator(&mut self) -> Result { + pub fn parse_operator(&mut self) -> CssResult { log::trace!("parse_operator"); let loc = self.tokenizer.current_location(); @@ -18,8 +20,8 @@ impl Css3<'_> { } } - Err(Error::new( - format!("Expected operator, got {:?}", operator), + Err(CssError::with_location( + format!("Expected operator, got {:?}", operator).as_str(), self.tokenizer.current_location(), )) } diff --git a/crates/gosub_css3/src/parser/pseudo.rs b/crates/gosub_css3/src/parser/pseudo.rs index 8e5ff612e..2651ac211 100644 --- a/crates/gosub_css3/src/parser/pseudo.rs +++ b/crates/gosub_css3/src/parser/pseudo.rs @@ -1,20 +1,22 @@ use crate::node::{Node, NodeType}; use crate::tokenizer::TokenType; -use crate::{Css3, Error}; +use crate::Css3; +use gosub_shared::errors::CssError; +use gosub_shared::errors::CssResult; impl Css3<'_> { - fn parse_pseudo_function_selector_list(&mut self) -> Result { + fn parse_pseudo_function_selector_list(&mut self) -> CssResult { log::trace!("parse_pseudo_function_selector_list"); self.parse_selector_list() } - fn parse_pseudo_function_selector(&mut self) -> Result { + fn parse_pseudo_function_selector(&mut self) -> CssResult { log::trace!("parse_pseudo_function_selector"); self.parse_selector() } - fn parse_pseudo_function_ident_list(&mut self) -> Result { + fn parse_pseudo_function_ident_list(&mut self) -> CssResult { log::trace!("parse_pseudo_function_ident_list"); let loc = self.tokenizer.current_location(); @@ -24,7 +26,7 @@ impl Css3<'_> { Ok(Node::new(NodeType::Ident { value }, loc)) } - fn parse_pseudo_function_nth(&mut self) -> Result { + fn parse_pseudo_function_nth(&mut self) -> CssResult { log::trace!("parse_pseudo_function_nth"); self.consume_whitespace_comments(); @@ -39,14 +41,14 @@ impl Css3<'_> { a: "2".into(), b: "1".into(), }, - loc.clone(), + loc, ), TokenType::Ident(value) if value == "even" => Node::new( NodeType::AnPlusB { a: "2".into(), b: "0".into(), }, - loc.clone(), + loc, ), TokenType::Ident(_) => { self.tokenizer.reconsume(); @@ -56,10 +58,10 @@ impl Css3<'_> { self.tokenizer.reconsume(); self.parse_anplusb()? } - TokenType::Number(value) => Node::new(NodeType::Number { value }, loc.clone()), + TokenType::Number(value) => Node::new(NodeType::Number { value }, loc), _ => { - return Err(Error::new( - format!("Unexpected token {:?}", self.tokenizer.lookahead(0)), + return Err(CssError::with_location( + format!("Unexpected token {:?}", self.tokenizer.lookahead(0)).as_str(), self.tokenizer.current_location(), )); } @@ -76,10 +78,10 @@ impl Css3<'_> { } } - Ok(Node::new(NodeType::Nth { nth, selector }, loc.clone())) + Ok(Node::new(NodeType::Nth { nth, selector }, loc)) } - pub(crate) fn parse_pseudo_function(&mut self, name: &str) -> Result { + pub(crate) fn parse_pseudo_function(&mut self, name: &str) -> CssResult { log::trace!("parse_pseudo_function"); match name { "dir" => self.parse_pseudo_function_ident_list(), @@ -98,8 +100,8 @@ impl Css3<'_> { "slotted" => self.parse_pseudo_function_selector(), "host" => self.parse_pseudo_function_selector(), "host-context" => self.parse_pseudo_function_selector(), - _ => Err(Error::new( - format!("Unexpected pseudo function {:?}", name), + _ => Err(CssError::with_location( + format!("Unexpected pseudo function {:?}", name).as_str(), self.tokenizer.current_location(), )), } diff --git a/crates/gosub_css3/src/parser/rule.rs b/crates/gosub_css3/src/parser/rule.rs index c76fa1049..72b7d1eb2 100644 --- a/crates/gosub_css3/src/parser/rule.rs +++ b/crates/gosub_css3/src/parser/rule.rs @@ -1,13 +1,14 @@ use crate::node::{Node, NodeType}; use crate::parser::block::BlockParseMode; use crate::tokenizer::TokenType; -use crate::{Css3, Error}; +use crate::Css3; +use gosub_shared::errors::CssResult; impl Css3<'_> { // Either the rule parsing succeeds as a whole, or not. When not a valid rule is found, we // return None if the config.ignore_errors is set to true, otherwise this will return an Err // and is handled by the caller - pub fn parse_rule(&mut self) -> Result, Error> { + pub fn parse_rule(&mut self) -> CssResult> { log::trace!("parse_rule"); let result = self.parse_rule_internal(); @@ -24,7 +25,7 @@ impl Css3<'_> { Ok(None) } - fn parse_rule_internal(&mut self) -> Result { + fn parse_rule_internal(&mut self) -> CssResult { let loc = self.tokenizer.current_location(); let prelude = self.parse_selector_list()?; @@ -49,6 +50,7 @@ impl Css3<'_> { #[cfg(test)] mod tests { use crate::walker::Walker; + use crate::{CssOrigin, ParserConfig}; use gosub_shared::byte_stream::{ByteStream, Encoding}; macro_rules! test { @@ -57,7 +59,7 @@ mod tests { stream.read_from_str($input, Some(Encoding::UTF8)); stream.close(); - let mut parser = crate::Css3::new(&mut stream); + let mut parser = crate::Css3::new(&mut stream, ParserConfig::default(), CssOrigin::User, ""); let result = parser.$func().unwrap().unwrap(); let w = Walker::new(&result); diff --git a/crates/gosub_css3/src/parser/selector.rs b/crates/gosub_css3/src/parser/selector.rs index 90535e92f..97b44bf2f 100644 --- a/crates/gosub_css3/src/parser/selector.rs +++ b/crates/gosub_css3/src/parser/selector.rs @@ -1,9 +1,11 @@ use crate::node::{Node, NodeType}; use crate::tokenizer::TokenType; -use crate::{Css3, Error}; +use crate::Css3; +use gosub_shared::errors::CssError; +use gosub_shared::errors::CssResult; impl Css3<'_> { - fn parse_attribute_operator(&mut self) -> Result { + fn parse_attribute_operator(&mut self) -> CssResult { log::trace!("parse_attribute_operator"); let mut value = String::new(); @@ -17,8 +19,8 @@ impl Css3<'_> { _ => { self.tokenizer.reconsume(); - return Err(Error::new( - format!("Expected attribute operator, got {:?}", c), + return Err(CssError::with_location( + format!("Expected attribute operator, got {:?}", c).as_str(), loc, )); } @@ -32,7 +34,7 @@ impl Css3<'_> { Ok(Node::new(NodeType::Operator(value), loc)) } - fn parse_class_selector(&mut self) -> Result { + fn parse_class_selector(&mut self) -> CssResult { log::trace!("parse_class_selector"); let loc = self.tokenizer.current_location(); @@ -44,7 +46,7 @@ impl Css3<'_> { Ok(Node::new(NodeType::ClassSelector { value }, loc)) } - fn parse_nesting_selector(&mut self) -> Result { + fn parse_nesting_selector(&mut self) -> CssResult { log::trace!("parse_nesting_selector"); let loc = self.tokenizer.current_location(); @@ -54,7 +56,7 @@ impl Css3<'_> { Ok(Node::new(NodeType::NestingSelector, loc)) } - fn parse_type_selector_ident_or_asterisk(&mut self) -> Result { + fn parse_type_selector_ident_or_asterisk(&mut self) -> CssResult { let t = self.tokenizer.lookahead(0); match t.token_type { TokenType::Ident(value) => { @@ -65,14 +67,14 @@ impl Css3<'_> { self.tokenizer.consume(); Ok("*".to_string()) } - _ => Err(Error::new( - format!("Unexpected token {:?}", t), + _ => Err(CssError::with_location( + format!("Unexpected token {:?}", t).as_str(), self.tokenizer.current_location(), )), } } - fn parse_type_selector(&mut self) -> Result { + fn parse_type_selector(&mut self) -> CssResult { log::trace!("parse_type_selector"); let loc = self.tokenizer.current_location(); @@ -108,7 +110,7 @@ impl Css3<'_> { )) } - fn parse_attribute_selector(&mut self) -> Result { + fn parse_attribute_selector(&mut self) -> CssResult { log::trace!("parse_attribute_selector"); let loc = self.tokenizer.current_location(); @@ -137,8 +139,8 @@ impl Css3<'_> { } else if t.is_ident() { value = self.consume_any_ident()?; } else { - return Err(Error::new( - format!("Unexpected token {:?}", t), + return Err(CssError::with_location( + format!("Unexpected token {:?}", t).as_str(), self.tokenizer.current_location(), )); } @@ -167,7 +169,7 @@ impl Css3<'_> { )) } - fn parse_id_selector(&mut self) -> Result { + fn parse_id_selector(&mut self) -> CssResult { log::trace!("parse_id_selector"); let loc = self.tokenizer.current_location(); @@ -178,8 +180,8 @@ impl Css3<'_> { let value = match t.token_type { TokenType::Ident(s) => s, _ => { - return Err(Error::new( - format!("Unexpected token {:?}", t), + return Err(CssError::with_location( + format!("Unexpected token {:?}", t).as_str(), self.tokenizer.current_location(), )); } @@ -188,7 +190,7 @@ impl Css3<'_> { Ok(Node::new(NodeType::IdSelector { value }, loc)) } - fn parse_pseudo_element_selector(&mut self) -> Result { + fn parse_pseudo_element_selector(&mut self) -> CssResult { log::trace!("parse_pseudo_element_selector"); let loc = self.tokenizer.current_location(); @@ -200,8 +202,8 @@ impl Css3<'_> { let value = if t.is_ident() { self.consume_any_ident()? } else { - return Err(Error::new( - format!("Unexpected token {:?}", t), + return Err(CssError::with_location( + format!("Unexpected token {:?}", t).as_str(), self.tokenizer.current_location(), )); }; @@ -209,7 +211,7 @@ impl Css3<'_> { Ok(Node::new(NodeType::PseudoElementSelector { value }, loc)) } - fn parse_pseudo_selector(&mut self) -> Result { + fn parse_pseudo_selector(&mut self) -> CssResult { log::trace!("parse_pseudo_selector"); let loc = self.tokenizer.current_location(); @@ -233,8 +235,8 @@ impl Css3<'_> { ) } _ => { - return Err(Error::new( - format!("Unexpected token {:?}", t), + return Err(CssError::with_location( + format!("Unexpected token {:?}", t).as_str(), self.tokenizer.current_location(), )); } @@ -243,7 +245,7 @@ impl Css3<'_> { Ok(Node::new(NodeType::PseudoClassSelector { value }, loc)) } - pub fn parse_selector(&mut self) -> Result { + pub fn parse_selector(&mut self) -> CssResult { log::trace!("parse_selector"); let loc = self.tokenizer.current_location(); @@ -253,7 +255,7 @@ impl Css3<'_> { // When true, we have encountered a space which means we need to emit a descendant combinator let mut space = false; - let mut whitespace_location = loc.clone(); + let mut whitespace_location = loc; let mut skip_space = false; @@ -273,7 +275,7 @@ impl Css3<'_> { if t.is_whitespace() { // on whitespace for selector - whitespace_location = t.location.clone(); + whitespace_location = t.location; space = true; continue; } @@ -299,18 +301,11 @@ impl Css3<'_> { TokenType::Number(value) => Node::new(NodeType::Number { value }, t.location), - TokenType::Percentage(value) => { - Node::new(NodeType::Percentage { value }, t.location) - } + TokenType::Percentage(value) => Node::new(NodeType::Percentage { value }, t.location), - TokenType::Dimension { value, unit } => { - Node::new(NodeType::Dimension { value, unit }, t.location) - } + TokenType::Dimension { value, unit } => Node::new(NodeType::Dimension { value, unit }, t.location), - TokenType::Delim('+') - | TokenType::Delim('>') - | TokenType::Delim('~') - | TokenType::Delim('/') => { + TokenType::Delim('+') | TokenType::Delim('>') | TokenType::Delim('~') | TokenType::Delim('/') => { // Dont add descendant combinator since we are now adding another one space = false; @@ -347,12 +342,7 @@ impl Css3<'_> { if space { // Detected a space previously, so we need to emit a descendant combinator - let node = Node::new( - NodeType::Combinator { - value: " ".to_string(), - }, - whitespace_location.clone(), - ); + let node = Node::new(NodeType::Combinator { value: " ".to_string() }, whitespace_location); // insert before the last added node children.push(node); space = false; diff --git a/crates/gosub_css3/src/parser/selector_list.rs b/crates/gosub_css3/src/parser/selector_list.rs index 1e068c640..3f2c92fbb 100644 --- a/crates/gosub_css3/src/parser/selector_list.rs +++ b/crates/gosub_css3/src/parser/selector_list.rs @@ -1,8 +1,9 @@ use crate::node::{Node, NodeType}; -use crate::{Css3, Error}; +use crate::Css3; +use gosub_shared::errors::CssResult; impl Css3<'_> { - pub fn parse_selector_list(&mut self) -> Result { + pub fn parse_selector_list(&mut self) -> CssResult { log::trace!("parse_selector_list"); let loc = self.tokenizer.current_location(); diff --git a/crates/gosub_css3/src/parser/stylesheet.rs b/crates/gosub_css3/src/parser/stylesheet.rs index 8bb2eb42c..741946566 100644 --- a/crates/gosub_css3/src/parser/stylesheet.rs +++ b/crates/gosub_css3/src/parser/stylesheet.rs @@ -1,9 +1,10 @@ use crate::node::{Node, NodeType}; use crate::tokenizer::TokenType; -use crate::{Css3, Error}; +use crate::Css3; +use gosub_shared::errors::CssResult; impl Css3<'_> { - pub fn parse_stylesheet(&mut self) -> Result, Error> { + pub fn parse_stylesheet_internal(&mut self) -> CssResult> { log::trace!("parse_stylesheet"); let loc = self.tokenizer.current_location(); @@ -18,17 +19,14 @@ impl Css3<'_> { TokenType::Whitespace(_) => {} TokenType::Comment(comment) => { if comment.chars().nth(2) == Some('!') { - children.push(Node::new( - NodeType::Comment { value: comment }, - t.location.clone(), - )); + children.push(Node::new(NodeType::Comment { value: comment }, t.location)); } } TokenType::Cdo => { - children.push(Node::new(NodeType::Cdo, t.location.clone())); + children.push(Node::new(NodeType::Cdo, t.location)); } TokenType::Cdc => { - children.push(Node::new(NodeType::Cdc, t.location.clone())); + children.push(Node::new(NodeType::Cdc, t.location)); } TokenType::AtKeyword(_keyword) => { self.tokenizer.reconsume(); diff --git a/crates/gosub_css3/src/parser/url.rs b/crates/gosub_css3/src/parser/url.rs index 979ea42de..71e400461 100644 --- a/crates/gosub_css3/src/parser/url.rs +++ b/crates/gosub_css3/src/parser/url.rs @@ -1,17 +1,19 @@ use crate::node::{Node, NodeType}; use crate::tokenizer::TokenType; -use crate::{Css3, Error}; +use crate::Css3; +use gosub_shared::errors::CssError; +use gosub_shared::errors::CssResult; impl Css3<'_> { - pub fn parse_url(&mut self) -> Result { + pub fn parse_url(&mut self) -> CssResult { log::trace!("parse_url"); let loc = self.tokenizer.current_location(); let name = self.consume_function()?; if name.to_ascii_lowercase() != "url" { - return Err(Error::new( - format!("Expected url, got {:?}", name), + return Err(CssError::with_location( + format!("Expected url, got {:?}", name).as_str(), self.tokenizer.current_location(), )); } @@ -20,10 +22,10 @@ impl Css3<'_> { let url = match t.token_type { TokenType::QuotedString(url) => url, _ => { - return Err(Error::new( - format!("Expected url, got {:?}", t), + return Err(CssError::with_location( + format!("Expected url, got {:?}", t).as_str(), self.tokenizer.current_location(), - )) + )); } }; @@ -36,6 +38,7 @@ impl Css3<'_> { #[cfg(test)] mod tests { use crate::walker::Walker; + use crate::{CssOrigin, ParserConfig}; use gosub_shared::byte_stream::{ByteStream, Encoding}; macro_rules! test { @@ -44,7 +47,7 @@ mod tests { stream.read_from_str($input, Some(Encoding::UTF8)); stream.close(); - let mut parser = crate::Css3::new(&mut stream); + let mut parser = crate::Css3::new(&mut stream, ParserConfig::default(), CssOrigin::User, ""); let result = parser.$func().unwrap(); let w = Walker::new(&result); @@ -58,7 +61,7 @@ mod tests { // stream.read_from_str($input, Some(Encoding::UTF8)); // stream.close(); // - // let mut parser = crate::Css3::new(&mut stream); + // let mut parser = crate::Css3::new(&mut stream, Default::default(), Default::default(), ""); // let result = parser.$func(); // // assert_eq!(true, result.is_err()); diff --git a/crates/gosub_css3/src/parser/value.rs b/crates/gosub_css3/src/parser/value.rs index d7697b3c0..7c5b1567d 100644 --- a/crates/gosub_css3/src/parser/value.rs +++ b/crates/gosub_css3/src/parser/value.rs @@ -1,9 +1,11 @@ use crate::node::{Node, NodeType}; use crate::tokenizer::TokenType; -use crate::{Css3, Error}; +use crate::Css3; +use gosub_shared::errors::CssError; +use gosub_shared::errors::CssResult; impl Css3<'_> { - pub fn parse_value_sequence(&mut self) -> Result, Error> { + pub fn parse_value_sequence(&mut self) -> CssResult> { log::trace!("parse_value_sequence"); let mut children = Vec::new(); @@ -38,7 +40,7 @@ impl Css3<'_> { // none: no value is found (but this is not an error) // err: // parsing went wrong - fn parse_value(&mut self) -> Result, Error> { + fn parse_value(&mut self) -> CssResult> { log::trace!("parse_value"); let t = self.consume_any()?; @@ -51,8 +53,8 @@ impl Css3<'_> { let node = Node::new(NodeType::Operator(",".into()), t.location); Ok(Some(node)) } - TokenType::LBracket => Err(Error::new( - "Unexpected token [".to_string(), + TokenType::LBracket => Err(CssError::with_location( + "Unexpected token [", self.tokenizer.current_location(), )), TokenType::QuotedString(value) => { @@ -104,9 +106,7 @@ impl Css3<'_> { return Ok(Some(n)); } - if !self.allow_values_in_argument_list.is_empty() - && self.tokenizer.lookahead(0).is_delim('=') - { + if !self.allow_values_in_argument_list.is_empty() && self.tokenizer.lookahead(0).is_delim('=') { self.consume_delim('=')?; let t = self.consume_any()?; let node = match t.token_type { @@ -124,16 +124,12 @@ impl Css3<'_> { }, t.location, ), - TokenType::Ident(default_value) => Node::new( - NodeType::MSIdent { - value, - default_value, - }, - t.location, - ), + TokenType::Ident(default_value) => { + Node::new(NodeType::MSIdent { value, default_value }, t.location) + } _ => { - return Err(Error::new( - format!("Expected number or ident, got {:?}", t), + return Err(CssError::with_location( + format!("Expected number or ident, got {:?}", t).as_str(), self.tokenizer.current_location(), )) } @@ -156,8 +152,8 @@ impl Css3<'_> { let node = self.parse_operator()?; Ok(Some(node)) } - '#' => Err(Error::new( - format!("Unexpected token {:?}", t), + '#' => Err(CssError::with_location( + format!("Unexpected token {:?}", t).as_str(), self.tokenizer.current_location(), )), _ => { diff --git a/crates/gosub_css3/src/stylesheet.rs b/crates/gosub_css3/src/stylesheet.rs index 1a4a40a8b..54ca3a00f 100644 --- a/crates/gosub_css3/src/stylesheet.rs +++ b/crates/gosub_css3/src/stylesheet.rs @@ -1,33 +1,120 @@ use core::fmt::Debug; +use gosub_shared::byte_stream::Location; +use gosub_shared::errors::CssError; +use gosub_shared::errors::CssResult; +use gosub_shared::traits::css3::CssOrigin; use std::cmp::Ordering; use std::fmt::Display; -use anyhow::anyhow; +use crate::colors::RgbColor; -use gosub_shared::types::Result; +/// Severity of a CSS error +#[derive(Debug, PartialEq)] +pub enum Severity { + /// A critical error that will prevent the stylesheet from being applied + Error, + /// A warning that will be displayed but will not prevent the stylesheet from being applied + Warning, + /// An information message that can be displayed to the user + Info, +} -use crate::colors::RgbColor; +impl Display for Severity { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Severity::Error => write!(f, "Error"), + Severity::Warning => write!(f, "Warning"), + Severity::Info => write!(f, "Info"), + } + } +} + +/// Defines a CSS log during +#[derive(PartialEq)] +pub struct CssLog { + /// Severity of the error + pub severity: Severity, + /// Error message + pub message: String, + /// Location of the error + pub location: Location, +} + +impl CssLog { + pub fn log(severity: Severity, message: &str, location: Location) -> Self { + Self { + severity, + message: message.to_string(), + location, + } + } + + pub fn error(message: &str, location: Location) -> Self { + Self { + severity: Severity::Error, + message: message.to_string(), + location, + } + } + + pub fn warn(message: &str, location: Location) -> Self { + Self { + severity: Severity::Warning, + message: message.to_string(), + location, + } + } + + pub fn info(message: &str, location: Location) -> Self { + Self { + severity: Severity::Info, + message: message.to_string(), + location, + } + } +} + +impl Display for CssLog { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "[{}] ({}:{}): {}", + self.severity, self.location.line, self.location.column, self.message + ) + } +} + +impl Debug for CssLog { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "[{}] ({}:{}): {}", + self.severity, self.location.line, self.location.column, self.message + ) + } +} /// Defines a complete stylesheet with all its rules and the location where it was found -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq)] pub struct CssStylesheet { /// List of rules found in this stylesheet pub rules: Vec, /// Origin of the stylesheet (user agent, author, user) pub origin: CssOrigin, /// Url or file path where the stylesheet was found - pub location: String, + pub url: String, + /// Any issues during parsing of the stylesheet + pub parse_log: Vec, } -/// Defines the origin of the stylesheet (or declaration) -#[derive(Debug, PartialEq, Clone)] -pub enum CssOrigin { - /// Browser/user agent defined stylesheets - UserAgent, - /// Author defined stylesheets that are linked or embedded in the HTML files - Author, - /// User defined stylesheets that will override the author and user agent stylesheets (for instance, custom user styles or extensions) - User, +impl gosub_shared::traits::css3::CssStylesheet for CssStylesheet { + fn origin(&self) -> CssOrigin { + self.origin + } + + fn url(&self) -> &str { + &self.url + } } /// A CSS rule, which contains a list of selectors and a list of declarations @@ -54,7 +141,7 @@ impl CssRule { pub struct CssDeclaration { // Css property color pub property: String, - // Raw values of the declaration. It is not calculated or converted in any way (ie: "red", "50px" etc) + // Raw values of the declaration. It is not calculated or converted in any way (ie: "red", "50px" etc.) // There can be multiple values (ie: "1px solid black" are split into 3 values) pub value: Vec, // ie: !important @@ -352,7 +439,7 @@ impl CssValue { } /// Converts a CSS AST node to a CSS value - pub fn parse_ast_node(node: &crate::node::Node) -> Result { + pub fn parse_ast_node(node: &crate::node::Node) -> CssResult { match *node.node_type.clone() { crate::node::NodeType::Ident { value } => Ok(CssValue::String(value)), crate::node::NodeType::Number { value } => { @@ -373,13 +460,10 @@ impl CssValue { Ok(CssValue::String(value)) } crate::node::NodeType::Operator(_) => Ok(CssValue::None), - crate::node::NodeType::Calc { .. } => { - Ok(CssValue::Function("calc".to_string(), vec![])) + crate::node::NodeType::Calc { .. } => Ok(CssValue::Function("calc".to_string(), vec![])), + crate::node::NodeType::Url { url } => { + Ok(CssValue::Function("url".to_string(), vec![CssValue::String(url)])) } - crate::node::NodeType::Url { url } => Ok(CssValue::Function( - "url".to_string(), - vec![CssValue::String(url)], - )), crate::node::NodeType::Function { name, arguments } => { let mut list = vec![]; for node in arguments.iter() { @@ -390,15 +474,14 @@ impl CssValue { } Ok(CssValue::Function(name, list)) } - _ => Err(anyhow!(format!( - "Cannot convert node to CssValue: {:?}", - node - ))), + _ => Err(CssError::new( + format!("Cannot convert node to CssValue: {:?}", node).as_str(), + )), } } /// Parses a string into a CSS value or list of css values - pub fn parse_str(value: &str) -> Result { + pub fn parse_str(value: &str) -> CssResult { match value { "initial" => return Ok(CssValue::Initial), "inherit" => return Ok(CssValue::Inherit), @@ -444,6 +527,64 @@ impl CssValue { } } +impl gosub_shared::traits::css3::CssValue for CssValue { + fn unit_to_px(&self) -> f32 { + self.unit_to_px() + } + + fn as_string(&self) -> Option<&str> { + if let CssValue::String(str) = &self { + Some(str) + } else { + None + } + } + + fn as_percentage(&self) -> Option { + if let CssValue::Percentage(percent) = &self { + Some(*percent) + } else { + None + } + } + + fn as_unit(&self) -> Option<(f32, &str)> { + if let CssValue::Unit(value, unit) = &self { + Some((*value, unit)) + } else { + None + } + } + + fn as_color(&self) -> Option<(f32, f32, f32, f32)> { + if let CssValue::Color(color) = &self { + Some((color.r, color.g, color.b, color.a)) + } else { + None + } + } + + fn as_number(&self) -> Option { + if let CssValue::Number(num) = &self { + Some(*num) + } else { + None + } + } + + fn as_list(&self) -> Option> { + if let CssValue::List(list) = &self { + Some(list.to_vec()) + } else { + None + } + } + + fn is_none(&self) -> bool { + matches!(self, CssValue::None) + } +} + #[cfg(test)] mod test { use std::vec; diff --git a/crates/gosub_css3/src/system.rs b/crates/gosub_css3/src/system.rs new file mode 100644 index 000000000..1c44c83c7 --- /dev/null +++ b/crates/gosub_css3/src/system.rs @@ -0,0 +1,244 @@ +use crate::functions::attr::resolve_attr; +use crate::functions::calc::resolve_calc; +use crate::functions::var::resolve_var; +use crate::matcher::property_definitions::get_css_definitions; +use crate::matcher::shorthands::FixList; +use crate::matcher::styling::{match_selector, CssProperties, CssProperty, DeclarationProperty}; +use crate::stylesheet::{CssDeclaration, CssValue, Specificity}; +use crate::Css3; +use gosub_shared::document::DocumentHandle; +use gosub_shared::errors::CssResult; +use gosub_shared::node::NodeId; +use gosub_shared::traits::css3::{CssOrigin, CssPropertyMap, CssSystem}; +use gosub_shared::traits::document::Document; +use gosub_shared::traits::node::{ElementDataType, Node, TextDataType}; +use gosub_shared::traits::render_tree::{RenderTree, RenderTreeNode}; +use gosub_shared::traits::ParserConfig; +use log::warn; + +#[derive(Debug, Clone)] +pub struct Css3System; + +impl CssSystem for Css3System { + type Stylesheet = crate::stylesheet::CssStylesheet; + + type PropertyMap = CssProperties; + + type Property = CssProperty; + + fn parse_str(str: &str, config: ParserConfig, origin: CssOrigin, url: &str) -> CssResult { + Css3::parse_str(str, config, origin, url) + } + + fn properties_from_node>( + node: &D::Node, + sheets: &[Self::Stylesheet], + handle: DocumentHandle, + id: NodeId, + ) -> Option { + let mut css_map_entry = CssProperties::new(); + + if node_is_unrenderable::(node) { + return None; + } + + let definitions = get_css_definitions(); + + let mut fix_list = FixList::new(); + + for sheet in sheets { + for rule in &sheet.rules { + for selector in rule.selectors().iter() { + let (matched, specificity) = match_selector(DocumentHandle::clone(&handle), id, selector); + + if !matched { + continue; + } + + // Selector matched, so we add all declared values to the map + for declaration in rule.declarations().iter() { + // Step 1: find the property in our CSS definition list + let Some(definition) = definitions.find_property(&declaration.property) else { + // If not found, we skip this declaration + warn!("Definition is not found for property {:?}", declaration.property); + continue; + }; + + let value = resolve_functions(&declaration.value, node, handle.clone()); + + // Check if the declaration matches the definition and return the "expanded" order + let res = definition.matches_and_shorthands(&value, &mut fix_list); + if !res { + warn!("Declaration does not match definition: {:?}", declaration); + continue; + } + + // create property for the given values + let property_name = declaration.property.clone(); + let decl = CssDeclaration { + property: property_name.to_string(), + value, + important: declaration.important, + }; + + add_property_to_map(&mut css_map_entry, sheet, specificity.clone(), &decl); + } + } + } + } + + fix_list.resolve_nested(definitions); + + fix_list.apply(&mut css_map_entry); + + Some(css_map_entry) + } + + fn inheritance>(tree: &mut T) { + Self::resolve_inheritance(tree, tree.root(), &Vec::new()); + } +} + +impl Css3System { + fn resolve_inheritance>( + tree: &mut T, + node_id: T::NodeId, + inherit_props: &Vec<(String, CssValue)>, + ) { + let Some(current_node) = tree.get_node_mut(node_id) else { + return; + }; + + for prop in inherit_props { + let mut p = CssProperty::new(prop.0.as_str()); + + p.inherited = prop.1.clone(); + + current_node.props_mut().insert_inherited(prop.0.as_str(), p); + } + + let mut inherit_props = inherit_props.clone(); + + 'props: for (name, prop) in &mut current_node.props_mut().iter_mut() { + prop.compute_value(); + + let value = prop.actual.clone(); + + if prop_is_inherit(name) { + for (k, v) in &mut inherit_props { + if k == name { + *v = value; + continue 'props; + } + } + + inherit_props.push((name.to_owned(), value)); + } + } + + let Some(children) = tree.get_children(node_id) else { + return; + }; + + for child in children { + Self::resolve_inheritance(tree, child, &inherit_props); + } + } +} + +pub fn prop_is_inherit(name: &str) -> bool { + get_css_definitions() + .find_property(name) + .map(|def| def.inherited) + .unwrap_or(false) +} + +pub fn add_property_to_map( + css_map_entry: &mut CssProperties, + sheet: &crate::stylesheet::CssStylesheet, + specificity: Specificity, + declaration: &CssDeclaration, +) { + let property_name = declaration.property.clone(); + // let entry = CssProperty::new(property_name.as_str()); + + // If the property is a shorthand css property, we need fetch the individual properties + // It's possible that need to recurse here as these individual properties can be shorthand as well + // if entry.is_shorthand() { + // for property_name in entry.get_props_from_shorthand() { + // let decl = CssDeclaration { + // property: property_name.to_string(), + // value: declaration.value.clone(), + // important: declaration.important, + // }; + // + // add_property_to_map(css_map_entry, sheet, selector, &decl); + // } + // } + // + let declaration = DeclarationProperty { + // @todo: this seems wrong. We only get the first values from the declared values + value: declaration.value.first().unwrap().clone(), + origin: sheet.origin, + important: declaration.important, + location: sheet.url.clone(), + specificity, + }; + + if let std::collections::hash_map::Entry::Vacant(e) = css_map_entry.properties.entry(property_name.clone()) { + // Generate new property in the css map + let mut entry = CssProperty::new(property_name.as_str()); + entry.declared.push(declaration); + e.insert(entry); + } else { + // Just add the declaration to the existing property + let entry = css_map_entry.properties.get_mut(&property_name).unwrap(); + entry.declared.push(declaration); + } +} + +pub fn node_is_unrenderable, C: CssSystem>(node: &D::Node) -> bool { + // There are more elements that are not renderable, but for now we only remove the most common ones + + const REMOVABLE_ELEMENTS: [&str; 6] = ["head", "script", "style", "svg", "noscript", "title"]; + + if let Some(element_data) = node.get_element_data() { + if REMOVABLE_ELEMENTS.contains(&element_data.name()) { + return true; + } + } + + if let Some(text_data) = &node.get_text_data() { + if text_data.value().chars().all(|c| c.is_whitespace()) { + return true; + } + } + + false +} + +pub fn resolve_functions, C: CssSystem>( + value: &[CssValue], + node: &D::Node, + handle: DocumentHandle, +) -> Vec { + let mut result = Vec::with_capacity(value.len()); //TODO: we could give it a &mut Vec and reuse the allocation + + for val in value { + match val { + CssValue::Function(func, values) => { + let resolved = match func.as_str() { + "calc" => resolve_calc(values), + "attr" => resolve_attr(values, node), + "var" => resolve_var(values, &*handle.get(), node), + _ => vec![val.clone()], + }; + + result.extend(resolved); + } + _ => result.push(val.clone()), + } + } + + result +} diff --git a/crates/gosub_css3/src/tokenizer.rs b/crates/gosub_css3/src/tokenizer.rs index 8dce822a8..4925cf6e8 100644 --- a/crates/gosub_css3/src/tokenizer.rs +++ b/crates/gosub_css3/src/tokenizer.rs @@ -95,10 +95,7 @@ impl Debug for Token { impl Token { /// Returns a new token for the given type on the given location fn new(token_type: TokenType, location: Location) -> Token { - Token { - token_type, - location, - } + Token { token_type, location } } fn new_delim(c: char, location: Location) -> Token { @@ -256,7 +253,7 @@ impl<'stream> Tokenizer<'stream> { /// Returns the current location (line/col) of the tokenizer pub fn current_location(&self) -> Location { - self.location_handler.cur_location.clone() + self.location_handler.cur_location } /// Returns true when there is no next element, and the stream is closed @@ -602,9 +599,7 @@ impl<'stream> Tokenizer<'stream> { value.push_str(&self.consume_digits()); - if self.current_char() == Ch('.') - && matches!(self.stream.look_ahead(1), Ch(c) if c.is_numeric()) - { + if self.current_char() == Ch('.') && matches!(self.stream.look_ahead(1), Ch(c) if c.is_numeric()) { value.push_str(&self.consume_chars(2)); // type should be "number" @@ -747,9 +742,7 @@ impl<'stream> Tokenizer<'stream> { // todo: look for better implementation if let Some(char) = char::from_u32(as_u32) { - if char == get_unicode_char(&UnicodeChar::Null) - || char >= get_unicode_char(&UnicodeChar::MaxAllowed) - { + if char == get_unicode_char(&UnicodeChar::Null) || char >= get_unicode_char(&UnicodeChar::MaxAllowed) { return default_char; } @@ -842,8 +835,7 @@ impl<'stream> Tokenizer<'stream> { /// def: [non-printable code point](https://www.w3.org/TR/css-syntax-3/#non-printable-code-point) fn is_non_printable_char(&self) -> bool { if let Ch(char) = self.current_char() { - (char >= get_unicode_char(&UnicodeChar::Null) - && char <= get_unicode_char(&UnicodeChar::Backspace)) + (char >= get_unicode_char(&UnicodeChar::Null) && char <= get_unicode_char(&UnicodeChar::Backspace)) || (char >= get_unicode_char(&UnicodeChar::ShiftOut) && char <= get_unicode_char(&UnicodeChar::InformationSeparatorOne)) || char == get_unicode_char(&UnicodeChar::Tab) @@ -867,9 +859,7 @@ impl<'stream> Tokenizer<'stream> { let second = self.stream.look_ahead(start + 1); if first == Ch('-') { - return self.is_ident_start(second.into()) - || second == Ch('-') - || self.is_start_of_escape(start + 1); + return self.is_ident_start(second.into()) || second == Ch('-') || self.is_start_of_escape(start + 1); } if first == Ch('\\') { @@ -885,8 +875,7 @@ impl<'stream> Tokenizer<'stream> { let last = self.stream.look_ahead(start + 2); // e.g. +1, -1, +.1, -0.01 - matches!(current, Ch('+' | '-')) - && ((next == Ch('.') && last.is_numeric()) || next.is_numeric()) + matches!(current, Ch('+' | '-')) && ((next == Ch('.') && last.is_numeric()) || next.is_numeric()) } fn is_any_of(&self, chars: Vec) -> bool { @@ -990,9 +979,7 @@ mod test { let mut tokenizer = Tokenizer::new(&mut chars, Location::default()); for (raw_num, num_token) in num_tokens { - tokenizer - .stream - .read_from_str(raw_num, Some(Encoding::UTF8)); + tokenizer.stream.read_from_str(raw_num, Some(Encoding::UTF8)); assert_eq!(tokenizer.consume_number(), num_token); } } @@ -1012,9 +999,7 @@ mod test { let mut tokenizer = Tokenizer::new(&mut chars, Location::default()); for (raw_ident, ident_tokens) in ident_tokens { - tokenizer - .stream - .read_from_str(raw_ident, Some(Encoding::UTF8)); + tokenizer.stream.read_from_str(raw_ident, Some(Encoding::UTF8)); assert_eq!(tokenizer.consume_ident(), ident_tokens); } @@ -1028,26 +1013,15 @@ mod test { let escaped_chars = vec![ ("\\005F ", get_unicode_char(&UnicodeChar::LowLine)), ("\\2A", '*'), - ( - "\\000000 ", - get_unicode_char(&UnicodeChar::ReplacementCharacter), - ), - ( - "\\FFFFFF ", - get_unicode_char(&UnicodeChar::ReplacementCharacter), - ), - ( - "\\10FFFF ", - get_unicode_char(&UnicodeChar::ReplacementCharacter), - ), + ("\\000000 ", get_unicode_char(&UnicodeChar::ReplacementCharacter)), + ("\\FFFFFF ", get_unicode_char(&UnicodeChar::ReplacementCharacter)), + ("\\10FFFF ", get_unicode_char(&UnicodeChar::ReplacementCharacter)), ]; let mut tokenizer = Tokenizer::new(&mut chars, Location::default()); for (raw_escaped, escaped_char) in escaped_chars { - tokenizer - .stream - .read_from_str(raw_escaped, Some(Encoding::UTF8)); + tokenizer.stream.read_from_str(raw_escaped, Some(Encoding::UTF8)); assert_eq!(tokenizer.consume_escaped_token(), escaped_char); } } @@ -1062,30 +1036,16 @@ mod test { "url(https://gosub.io/)", Token::new_url("https://gosub.io/", Location::default()), ), - ( - "url( gosub.io )", - Token::new_url("gosub.io", Location::default()), - ), - ( - "url(gosub\u{002E}io)", - Token::new_url("gosub.io", Location::default()), - ), - ( - "url(gosub\u{FFFD}io)", - Token::new_url("gosub๏ฟฝio", Location::default()), - ), - ( - "url(gosub\u{0000}io)", - Token::new_bad_url("gosub", Location::default()), - ), + ("url( gosub.io )", Token::new_url("gosub.io", Location::default())), + ("url(gosub\u{002E}io)", Token::new_url("gosub.io", Location::default())), + ("url(gosub\u{FFFD}io)", Token::new_url("gosub๏ฟฝio", Location::default())), + ("url(gosub\u{0000}io)", Token::new_bad_url("gosub", Location::default())), ]; let mut tokenizer = Tokenizer::new(&mut chars, Location::default()); for (raw_url, url_token) in urls { - tokenizer - .stream - .read_from_str(raw_url, Some(Encoding::UTF8)); + tokenizer.stream.read_from_str(raw_url, Some(Encoding::UTF8)); assert_token_eq!(tokenizer.consume_ident_like_seq(), url_token); } } @@ -1101,47 +1061,24 @@ mod test { ("url( \'", Token::new_function("url", Location::default())), ("url(\"", Token::new_function("url", Location::default())), ("attr('", Token::new_function("attr", Location::default())), - ( - "rotateX( '", - Token::new_function("rotateX", Location::default()), - ), - ( - "rotateY( \"", - Token::new_function("rotateY", Location::default()), - ), + ("rotateX( '", Token::new_function("rotateX", Location::default())), + ("rotateY( \"", Token::new_function("rotateY", Location::default())), ("-rgba(", Token::new_function("-rgba", Location::default())), - ( - "--rgba(", - Token::new_function("--rgba", Location::default()), - ), - ( - "-\\26 -rgba(", - Token::new_function("-&-rgba", Location::default()), - ), + ("--rgba(", Token::new_function("--rgba", Location::default())), + ("-\\26 -rgba(", Token::new_function("-&-rgba", Location::default())), ("0rgba()", Token::new_function("0rgba", Location::default())), - ( - "-0rgba()", - Token::new_function("-0rgba", Location::default()), - ), + ("-0rgba()", Token::new_function("-0rgba", Location::default())), ("_rgba()", Token::new_function("_rgba", Location::default())), ("rgbรข()", Token::new_function("rgbรข", Location::default())), - ( - "\\30rgba()", - Token::new_function("0rgba", Location::default()), - ), + ("\\30rgba()", Token::new_function("0rgba", Location::default())), ("rgba ()", Token::new_ident("rgba", Location::default())), - ( - "-\\-rgba(", - Token::new_function("--rgba", Location::default()), - ), + ("-\\-rgba(", Token::new_function("--rgba", Location::default())), ]; let mut tokenizer = Tokenizer::new(&mut chars, Location::default()); for (raw_function, function_token) in functions { - tokenizer - .stream - .read_from_str(raw_function, Some(Encoding::UTF8)); + tokenizer.stream.read_from_str(raw_function, Some(Encoding::UTF8)); assert_token_eq!(tokenizer.consume_ident_like_seq(), function_token); } } @@ -1151,10 +1088,7 @@ mod test { let mut chars = ByteStream::new(Encoding::UTF8, None); let numeric_tokens = vec![ - ( - "1.1rem", - Token::new_dimension(1.1, "rem", Location::default()), - ), + ("1.1rem", Token::new_dimension(1.1, "rem", Location::default())), ("1px", Token::new_dimension(1.0, "px", Location::default())), ("1em", Token::new_dimension(1.0, "em", Location::default())), ("1 em", Token::new_number(1.0, Location::default())), @@ -1167,9 +1101,7 @@ mod test { let mut tokenizer = Tokenizer::new(&mut chars, Location::default()); for (raw_token, token) in numeric_tokens { - tokenizer - .stream - .read_from_str(raw_token, Some(Encoding::UTF8)); + tokenizer.stream.read_from_str(raw_token, Some(Encoding::UTF8)); assert_token_eq!(tokenizer.consume_numeric_token(), token); } } @@ -1179,10 +1111,7 @@ mod test { let mut stream = ByteStream::new(Encoding::UTF8, None); let string_tokens = vec![ - ( - "'line\nnewline'", - Token::new_bad_string("line", Location::default()), - ), + ("'line\nnewline'", Token::new_bad_string("line", Location::default())), ( "\"double quotes\"", Token::new_quoted_string("double quotes", Location::default()), @@ -1191,22 +1120,14 @@ mod test { "\'single quotes\'", Token::new_quoted_string("single quotes", Location::default()), ), - ( - "#hash#", - Token::new_quoted_string("hash", Location::default()), - ), - ( - "\"eof", - Token::new_quoted_string("eof", Location::default()), - ), + ("#hash#", Token::new_quoted_string("hash", Location::default())), + ("\"eof", Token::new_quoted_string("eof", Location::default())), ("\"\"", Token::new_quoted_string("", Location::default())), ]; for (raw_string, string_token) in string_tokens { let mut tokenizer = Tokenizer::new(&mut stream, Location::default()); - tokenizer - .stream - .read_from_str(raw_string, Some(Encoding::UTF8)); + tokenizer.stream.read_from_str(raw_string, Some(Encoding::UTF8)); tokenizer.stream.close(); let t = tokenizer.consume_string_token(); @@ -1218,10 +1139,7 @@ mod test { fn produce_stream_of_double_quoted_strings() { let mut stream = ByteStream::new(Encoding::UTF8, None); - stream.read_from_str( - "\"\" \"Lorem 'รฎpsum'\" \"a\\\nb\" \"a\nb \"eof", - Some(Encoding::UTF8), - ); + stream.read_from_str("\"\" \"Lorem 'รฎpsum'\" \"a\\\nb\" \"a\nb \"eof", Some(Encoding::UTF8)); stream.close(); let tokens = vec![ @@ -1253,10 +1171,7 @@ mod test { fn procude_stream_of_single_quoted_strings() { let mut stream = ByteStream::new(Encoding::UTF8, None); - stream.read_from_str( - "'' 'Lorem \"รฎpsum\"' 'a\\\nb' 'a\nb 'eof", - Some(Encoding::UTF8), - ); + stream.read_from_str("'' 'Lorem \"รฎpsum\"' 'a\\\nb' 'a\nb 'eof", Some(Encoding::UTF8)); stream.close(); let tokens = vec![ @@ -1446,10 +1361,7 @@ mod test { fn parse_cdo_and_cdc() { let mut stream = ByteStream::new(Encoding::UTF8, None); - stream.read_from_str( - "/* CDO/CDC are not special */ {}", - Some(Encoding::UTF8), - ); + stream.read_from_str("/* CDO/CDC are not special */ {}", Some(Encoding::UTF8)); stream.close(); let tokens = vec![ @@ -1857,7 +1769,10 @@ mod test { fn parse_css_seq_3() { let mut stream = ByteStream::new(Encoding::UTF8, None); - stream.read_from_str("\\- red0 -red --red -\\-red\\ blue 0red -0red \\0000red _Red .red rรชd r\\รชd \\007F\\0080\\0081", Some(Encoding::UTF8)); + stream.read_from_str( + "\\- red0 -red --red -\\-red\\ blue 0red -0red \\0000red _Red .red rรชd r\\รชd \\007F\\0080\\0081", + Some(Encoding::UTF8), + ); stream.close(); let tokens = vec![ @@ -1954,10 +1869,7 @@ mod test { tokenizer.lookahead(1), Token::new(TokenType::RBracket, Location::default()) ); - assert_token_eq!( - tokenizer.lookahead(4), - Token::new(TokenType::Eof, Location::default()) - ); + assert_token_eq!(tokenizer.lookahead(4), Token::new(TokenType::Eof, Location::default())); assert_token_eq!( tokenizer.consume(), diff --git a/crates/gosub_css3/src/walker.rs b/crates/gosub_css3/src/walker.rs index e0e93d7c3..955020334 100644 --- a/crates/gosub_css3/src/walker.rs +++ b/crates/gosub_css3/src/walker.rs @@ -46,11 +46,7 @@ fn inner_walk(node: &Node, depth: usize, f: &mut dyn Write) -> Result<(), std::i // writeln!(f, "{} - block: ", prefix)?; inner_walk(block.as_ref().unwrap(), depth + 1, f)?; } - NodeType::AtRule { - name, - prelude, - block, - } => { + NodeType::AtRule { name, prelude, block } => { writeln!(f, "{}[AtRule] name: {}", prefix, name)?; if prelude.is_some() { inner_walk(prelude.as_ref().unwrap(), depth + 1, f)?; @@ -219,10 +215,7 @@ fn inner_walk(node: &Node, depth: usize, f: &mut dyn Write) -> Result<(), std::i writeln!(f, "{}[MSFunction]", prefix)?; inner_walk(func, depth + 1, f)?; } - NodeType::MSIdent { - value, - default_value, - } => { + NodeType::MSIdent { value, default_value } => { writeln!( f, "{}[MSIdent] value: {} default_value: {}", diff --git a/crates/gosub_styling/tools/generate_definitions/go.mod b/crates/gosub_css3/tools/generate_definitions/go.mod similarity index 100% rename from crates/gosub_styling/tools/generate_definitions/go.mod rename to crates/gosub_css3/tools/generate_definitions/go.mod diff --git a/crates/gosub_styling/tools/generate_definitions/main.go b/crates/gosub_css3/tools/generate_definitions/main.go similarity index 98% rename from crates/gosub_styling/tools/generate_definitions/main.go rename to crates/gosub_css3/tools/generate_definitions/main.go index 584d4fcba..64fae373c 100644 --- a/crates/gosub_styling/tools/generate_definitions/main.go +++ b/crates/gosub_css3/tools/generate_definitions/main.go @@ -21,7 +21,7 @@ const ( const ( exportType = Both - ResourcePath = "crates/gosub_styling/resources/definitions" + ResourcePath = "crates/gosub_css3/resources/definitions" SingleFilePath = ResourcePath + "/definitions.json" MultiFileDir = ResourcePath MultiFilePrefix = "definitions_" diff --git a/crates/gosub_styling/tools/generate_definitions/mdn/mdn.go b/crates/gosub_css3/tools/generate_definitions/mdn/mdn.go similarity index 100% rename from crates/gosub_styling/tools/generate_definitions/mdn/mdn.go rename to crates/gosub_css3/tools/generate_definitions/mdn/mdn.go diff --git a/crates/gosub_styling/tools/generate_definitions/patch/patch_darwin.go b/crates/gosub_css3/tools/generate_definitions/patch/patch_darwin.go similarity index 100% rename from crates/gosub_styling/tools/generate_definitions/patch/patch_darwin.go rename to crates/gosub_css3/tools/generate_definitions/patch/patch_darwin.go diff --git a/crates/gosub_styling/tools/generate_definitions/patch/patch_linux.go b/crates/gosub_css3/tools/generate_definitions/patch/patch_linux.go similarity index 100% rename from crates/gosub_styling/tools/generate_definitions/patch/patch_linux.go rename to crates/gosub_css3/tools/generate_definitions/patch/patch_linux.go diff --git a/crates/gosub_styling/tools/generate_definitions/patch/patch_windows.go b/crates/gosub_css3/tools/generate_definitions/patch/patch_windows.go similarity index 100% rename from crates/gosub_styling/tools/generate_definitions/patch/patch_windows.go rename to crates/gosub_css3/tools/generate_definitions/patch/patch_windows.go diff --git a/crates/gosub_styling/tools/generate_definitions/patch/patcher.go b/crates/gosub_css3/tools/generate_definitions/patch/patcher.go similarity index 100% rename from crates/gosub_styling/tools/generate_definitions/patch/patcher.go rename to crates/gosub_css3/tools/generate_definitions/patch/patcher.go diff --git a/crates/gosub_styling/tools/generate_definitions/specs/specs.go b/crates/gosub_css3/tools/generate_definitions/specs/specs.go similarity index 100% rename from crates/gosub_styling/tools/generate_definitions/specs/specs.go rename to crates/gosub_css3/tools/generate_definitions/specs/specs.go diff --git a/crates/gosub_styling/tools/generate_definitions/utils/stringmaybearray.go b/crates/gosub_css3/tools/generate_definitions/utils/stringmaybearray.go similarity index 100% rename from crates/gosub_styling/tools/generate_definitions/utils/stringmaybearray.go rename to crates/gosub_css3/tools/generate_definitions/utils/stringmaybearray.go diff --git a/crates/gosub_styling/tools/generate_definitions/utils/types.go b/crates/gosub_css3/tools/generate_definitions/utils/types.go similarity index 100% rename from crates/gosub_styling/tools/generate_definitions/utils/types.go rename to crates/gosub_css3/tools/generate_definitions/utils/types.go diff --git a/crates/gosub_styling/tools/generate_definitions/utils/utils.go b/crates/gosub_css3/tools/generate_definitions/utils/utils.go similarity index 89% rename from crates/gosub_styling/tools/generate_definitions/utils/utils.go rename to crates/gosub_css3/tools/generate_definitions/utils/utils.go index 3599e0866..fd2a06441 100644 --- a/crates/gosub_styling/tools/generate_definitions/utils/utils.go +++ b/crates/gosub_css3/tools/generate_definitions/utils/utils.go @@ -10,9 +10,9 @@ const ( REPO = "w3c/webref" LOCATION = "ed/css" PATCH_LOCATION = "ed/csspatches" - CACHE_DIR = "crates/gosub_styling/resources/cache" + CACHE_DIR = "crates/gosub_css3/resources/cache" CACHE_INDEX_FILE = CACHE_DIR + "/index/cache_index.json" - CUSTOM_PATCH_DIR = "crates/gosub_styling/resources/patches" + CUSTOM_PATCH_DIR = "crates/gosub_css3/resources/patches" BRANCH = "curated" ) diff --git a/crates/gosub_styling/tools/generate_definitions/webref/webref.go b/crates/gosub_css3/tools/generate_definitions/webref/webref.go similarity index 100% rename from crates/gosub_styling/tools/generate_definitions/webref/webref.go rename to crates/gosub_css3/tools/generate_definitions/webref/webref.go diff --git a/crates/gosub_html5/Cargo.toml b/crates/gosub_html5/Cargo.toml index 25ec4762f..ce51b8e9c 100644 --- a/crates/gosub_html5/Cargo.toml +++ b/crates/gosub_html5/Cargo.toml @@ -15,8 +15,6 @@ thiserror = "1.0.64" url = { version = "2.5.2", features = [] } log = { version = "0.4.22", features = [] } - - [target.'cfg(not(target_arch = "wasm32"))'.dependencies] ureq = "2.10.1" diff --git a/crates/gosub_html5/benches/tree_construction.rs b/crates/gosub_html5/benches/tree_construction.rs index 077cf0aae..b118414d1 100644 --- a/crates/gosub_html5/benches/tree_construction.rs +++ b/crates/gosub_html5/benches/tree_construction.rs @@ -1,4 +1,7 @@ use criterion::{criterion_group, criterion_main, Criterion}; +use gosub_css3::system::Css3System; +use gosub_html5::document::document_impl::DocumentImpl; +use gosub_html5::parser::Html5Parser; use gosub_testing::testing::tree_construction; use gosub_testing::testing::tree_construction::Harness; @@ -8,8 +11,7 @@ fn criterion_benchmark(c: &mut Criterion) { // Careful about reading files inside the closure let filenames = Some(&["tests1.dat"][..]); - let fixtures = - tree_construction::fixture::read_fixtures(filenames).expect("problem loading fixtures"); + let fixtures = tree_construction::fixture::read_fixtures(filenames).expect("problem loading fixtures"); let mut harness = Harness::new(); @@ -18,7 +20,10 @@ fn criterion_benchmark(c: &mut Criterion) { for root in &fixtures { for test in &root.tests { for &scripting_enabled in test.script_modes() { - let _ = harness.run_test(test.clone(), scripting_enabled); + let _ = harness.run_test::, Css3System>, Css3System>( + test.clone(), + scripting_enabled, + ); } } } diff --git a/crates/gosub_html5/docs/parsing.md b/crates/gosub_html5/docs/parsing.md index 04dc7631d..2eec449f6 100644 --- a/crates/gosub_html5/docs/parsing.md +++ b/crates/gosub_html5/docs/parsing.md @@ -16,14 +16,14 @@ Next, we need to create a document, which will be the main object that will be f data that is generated during the parsing of the HTML. This also includes any stylesheets that are found, both internally and externally. ```rust - let document = DocumentBuilder::new_document(); + let doc_handle = DocumentBuilderImpl::new_document(); ``` -Note that a document itself isn't a document, but a HANDLE to a document (a `DocumentHandle`). Once we have our document handle, we can start the parser +Note that a doc_handle itself isn't a document, but a HANDLE to a document (a `DocumentHandle`). Once we have our document handle, we can start the parser by calling the `parse_document` method on the `Html5Parser` struct. This method will return a list of parse errors, if any. ```rust - let parse_errors = Html5Parser::parse_document(&mut stream, Document::clone(&document), None)?; + let parse_errors = Html5Parser::parse_document(&mut stream, doc_handle.clone(), None)?; for e in parse_errors { println!("Parse Error: {}", e.message); diff --git a/crates/gosub_html5/src/document.rs b/crates/gosub_html5/src/document.rs new file mode 100644 index 000000000..6456d6b49 --- /dev/null +++ b/crates/gosub_html5/src/document.rs @@ -0,0 +1,5 @@ +pub mod builder; +pub mod document_impl; +pub mod fragment; +pub mod query; +pub mod task_queue; diff --git a/crates/gosub_html5/src/document/builder.rs b/crates/gosub_html5/src/document/builder.rs new file mode 100644 index 000000000..2cdd48dc3 --- /dev/null +++ b/crates/gosub_html5/src/document/builder.rs @@ -0,0 +1,55 @@ +use std::collections::HashMap; + +use gosub_shared::traits::css3::CssSystem; +use url::Url; + +use crate::document::document_impl::DocumentImpl; +use crate::node::HTML_NAMESPACE; +use crate::DocumentHandle; +use gosub_shared::traits::document::DocumentBuilder; +use gosub_shared::traits::document::{Document, DocumentType}; +use gosub_shared::traits::node::{Node, QuirksMode}; + +/// This struct will be used to create a fully initialized document or document fragment +pub struct DocumentBuilderImpl {} + +impl DocumentBuilder for DocumentBuilderImpl { + type Document = DocumentImpl; + + /// Creates a new document with a document root node + fn new_document(url: Option) -> DocumentHandle { + >::new(DocumentType::HTML, url, None) + } + + /// Creates a new document fragment with the context as the root node + fn new_document_fragment( + context_node: &>::Node, + quirks_mode: QuirksMode, + ) -> DocumentHandle { + let handle = context_node.handle(); + + // Create a new document with an HTML node as the root node + let fragment_root_node = >::new_element_node( + handle.clone(), + "html", + Some(HTML_NAMESPACE), + HashMap::new(), + context_node.location(), + ); + let mut fragment_handle = + >::new(DocumentType::HTML, None, Some(fragment_root_node)); + + // let context_node = context_node.clone(); + match quirks_mode { + QuirksMode::Quirks => { + fragment_handle.get_mut().set_quirks_mode(QuirksMode::Quirks); + } + QuirksMode::LimitedQuirks => { + fragment_handle.get_mut().set_quirks_mode(QuirksMode::LimitedQuirks); + } + _ => {} + } + + fragment_handle + } +} diff --git a/crates/gosub_html5/src/document/document_impl.rs b/crates/gosub_html5/src/document/document_impl.rs new file mode 100755 index 000000000..374677a9f --- /dev/null +++ b/crates/gosub_html5/src/document/document_impl.rs @@ -0,0 +1,2277 @@ +use crate::DocumentHandle; +use core::fmt::Debug; +use gosub_shared::traits::document::{Document as OtherDocument, Document, DocumentType}; +use std::cell::RefCell; +use std::collections::hash_map::Entry; +use std::collections::HashMap; +use std::fmt; +use std::fmt::{Display, Formatter}; +use std::rc::Rc; +use url::Url; + +use crate::document::builder::DocumentBuilderImpl; +use crate::document::fragment::DocumentFragmentImpl; +use crate::document::task_queue::is_valid_id_attribute_value; +use crate::node::arena::NodeArena; +use crate::node::data::comment::CommentData; +use crate::node::data::doctype::DocTypeData; +use crate::node::data::document::DocumentData; +use crate::node::data::element::{ClassListImpl, ElementData}; +use crate::node::data::text::TextData; +use crate::node::node_impl::{NodeDataTypeInternal, NodeImpl}; +use crate::node::visitor::Visitor; +use gosub_shared::byte_stream::Location; +use gosub_shared::node::NodeId; +use gosub_shared::traits::css3::CssSystem; +use gosub_shared::traits::node::Node; +use gosub_shared::traits::node::QuirksMode; + +/// Defines a document +#[derive(Debug)] +pub struct DocumentImpl { + // pub handle: Weak>, + /// URL of the given document (if any) + pub url: Option, + /// Holds and owns all nodes in the document + pub(crate) arena: NodeArena, C>, + /// HTML elements with ID (e.g.,

) + named_id_elements: HashMap, + /// Document type of this document + pub doctype: DocumentType, + /// Quirks mode of this document + pub quirks_mode: QuirksMode, + /// Loaded stylesheets as extracted from the document + pub stylesheets: Vec, +} + +impl PartialEq for DocumentImpl { + fn eq(&self, other: &Self) -> bool { + self.url == other.url + && self.arena == other.arena + && self.named_id_elements == other.named_id_elements + && self.doctype == other.doctype + && self.quirks_mode == other.quirks_mode + && self.stylesheets == other.stylesheets + } +} + +impl Document for DocumentImpl { + type Node = NodeImpl; + type Fragment = DocumentFragmentImpl; + type Builder = DocumentBuilderImpl; + + /// Creates a new document without a doc handle + #[must_use] + fn new(document_type: DocumentType, url: Option, root_node: Option) -> DocumentHandle { + let doc = Self { + url, + arena: NodeArena::new(), + named_id_elements: HashMap::new(), + doctype: document_type, + quirks_mode: QuirksMode::NoQuirks, + stylesheets: Vec::new(), + }; + + let mut doc_handle = DocumentHandle(Rc::new(RefCell::new(doc)), Default::default()); + + if let Some(node) = root_node { + doc_handle.get_mut().register_node(node); + } else { + let node = Self::Node::new_document(doc_handle.clone(), Location::default(), QuirksMode::NoQuirks); + doc_handle.get_mut().arena.register_node(node); + } + + doc_handle + } + + /// Returns the URL of the document, or "" when no location is set + fn url(&self) -> Option { + self.url.clone() + } + + fn set_quirks_mode(&mut self, quirks_mode: QuirksMode) { + self.quirks_mode = quirks_mode; + } + + fn quirks_mode(&self) -> QuirksMode { + self.quirks_mode + } + + fn set_doctype(&mut self, doctype: DocumentType) { + self.doctype = doctype; + } + + fn doctype(&self) -> DocumentType { + self.doctype + } + + /// Fetches a node by id or returns None when no node with this ID is found + fn node_by_id(&self, node_id: NodeId) -> Option<&Self::Node> { + self.arena.node_ref(node_id) + } + + fn node_by_named_id(&self, id: &str) -> Option<&Self::Node> { + self.named_id_elements + .get(id) + .and_then(|node_id| self.arena.node_ref(*node_id)) + } + + fn stylesheets(&self) -> &Vec { + &self.stylesheets + } + + // /// Add given node to the named ID elements + // fn add_named_id(&mut self, id: &str, node_id: NodeId) { + // self.named_id_elements.insert(id.to_string(), node_id); + // } + // + // /// Remove a named ID from the document + // fn remove_named_id(&mut self, id: &str) { + // self.named_id_elements.remove(id); + // } + + fn add_stylesheet(&mut self, stylesheet: C::Stylesheet) { + self.stylesheets.push(stylesheet); + } + + /// returns the root node + fn get_root(&self) -> &Self::Node { + self.arena.node_ref(NodeId::root()).expect("Root node not found !?") + } + + fn attach_node(&mut self, node_id: NodeId, parent_id: NodeId, position: Option) { + // Check if any children of node have parent as child. This will keep adding the node to itself + if parent_id == node_id || self.has_node_id_recursive(node_id, parent_id) { + return; + } + + if let Some(parent_node) = self.node_by_id(parent_id) { + let mut parent_node = parent_node.clone(); + + // Make sure position can never be larger than the number of children in the parent + match position { + Some(position) => { + if position > parent_node.children().len() { + parent_node.push(node_id); + } else { + parent_node.insert(node_id, position); + } + } + None => { + // No position given, add to end of the children list + parent_node.push(node_id); + } + } + + self.update_node(parent_node); + } + + let mut node = self.arena.node(node_id).unwrap(); + node.parent = Some(parent_id); + self.update_node(node); + } + + // /// returns the root node + // fn get_root_mut(&mut self) -> &mut Self::Node { + // self.arena.node_mut(NodeId::root()).expect("Root node not found !?") + // } + + fn detach_node(&mut self, node_id: NodeId) { + let parent = self.node_by_id(node_id).expect("node not found").parent_id(); + + if let Some(parent_id) = parent { + let mut parent_node = self.node_by_id(parent_id).expect("parent node not found").clone(); + parent_node.remove(node_id); + self.update_node(parent_node); + + let mut node = self.node_by_id(node_id).expect("node not found").clone(); + node.set_parent(None); + self.update_node(node); + } + } + + /// Relocates a node to another parent node + fn relocate_node(&mut self, node_id: NodeId, parent_id: NodeId) { + let node = self.arena.node_ref(node_id).unwrap(); + assert!(node.registered, "Node is not registered to the arena"); + + if node.parent.is_some() && node.parent.unwrap() == parent_id { + // Nothing to do when we want to relocate to its own parent + return; + } + + self.detach_node(node_id); + self.attach_node(node_id, parent_id, None); + } + + fn update_node(&mut self, node: Self::Node) { + if !node.is_registered() { + log::warn!("Node is not registered to the arena"); + return; + } + + self.on_document_node_mutation(&node); + self.arena.update_node(node); + } + + fn update_node_ref(&mut self, node: &Self::Node) { + if !node.is_registered() { + log::warn!("Node is not registered to the arena"); + return; + } + + self.on_document_node_mutation(node); + self.arena.update_node(node.clone()); + } + + /// Removes a node by id from the arena. Note that this does not check the nodelist itself to see + /// if the node is still available as a child or parent in the tree. + fn delete_node_by_id(&mut self, node_id: NodeId) { + let node = self.arena.node(node_id).unwrap(); + let parent_id = node.parent_id(); + + if let Some(parent_id) = parent_id { + let mut parent = self.node_by_id(parent_id).unwrap().clone(); + parent.remove(node_id); + self.update_node(parent); + } + + self.arena.delete_node(node_id); + } + + // /// Returns the parent node of the given node, or None when no parent is found + // fn parent_node(&self, node: &Self::Node) -> Option<&Self::Node> { + // match node.parent_id() { + // Some(parent_node_id) => self.node_by_id(parent_node_id), + // None => None, + // } + // } + + /// Retrieves the next sibling NodeId (to the right) of the reference_node or None. + fn get_next_sibling(&self, reference_node: NodeId) -> Option { + let node = self.node_by_id(reference_node)?; + let parent = self.node_by_id(node.parent_id()?)?; + + let idx = parent + .children() + .iter() + .position(|&child_id| child_id == reference_node)?; + + let next_idx = idx + 1; + if parent.children().len() > next_idx { + return Some(parent.children()[next_idx]); + } + + None + } + + fn node_count(&self) -> usize { + self.arena.node_count() + } + + fn peek_next_id(&self) -> NodeId { + self.arena.peek_next_id() + } + + /// Register a node. It is not connected to anything yet, but it does receive a nodeId + fn register_node(&mut self, mut node: Self::Node) -> NodeId { + let node_id = self.arena.get_next_id(); + + node.set_id(node_id); + + if node.is_element_node() { + let element_data = node.get_element_data_mut().unwrap(); + element_data.node_id = Some(node_id); + } + + self.on_document_node_mutation(&node); + + self.arena.register_node_with_node_id(node, node_id); + + node_id + } + + /// Inserts a node to the parent node at the given position in the children (or none + /// to add at the end). Will automatically register the node if not done so already + fn register_node_at(&mut self, node: Self::Node, parent_id: NodeId, position: Option) -> NodeId { + self.on_document_node_mutation(&node); + + let node_id = self.register_node(node); + self.attach_node(node_id, parent_id, position); + + node_id + } + + /// Creates a new document node + fn new_document_node(handle: DocumentHandle, quirks_mode: QuirksMode, location: Location) -> Self::Node { + NodeImpl::new( + handle.clone(), + location, + &NodeDataTypeInternal::Document(DocumentData::new(quirks_mode)), + ) + } + + fn new_doctype_node( + handle: DocumentHandle, + name: &str, + public_id: Option<&str>, + system_id: Option<&str>, + location: Location, + ) -> Self::Node { + NodeImpl::new( + handle.clone(), + location, + &NodeDataTypeInternal::DocType(DocTypeData::new(name, public_id.unwrap_or(""), system_id.unwrap_or(""))), + ) + } + + /// Creates a new comment node + fn new_comment_node(handle: DocumentHandle, comment: &str, location: Location) -> Self::Node { + NodeImpl::new( + handle.clone(), + location, + &NodeDataTypeInternal::Comment(CommentData::with_value(comment)), + ) + } + + /// Creates a new text node + fn new_text_node(handle: DocumentHandle, value: &str, location: Location) -> Self::Node { + NodeImpl::new( + handle.clone(), + location, + &NodeDataTypeInternal::Text(TextData::with_value(value)), + ) + } + + /// Creates a new element node + fn new_element_node( + handle: DocumentHandle, + name: &str, + namespace: Option<&str>, + attributes: HashMap, + location: Location, + ) -> Self::Node { + // Extract class list from the class-attribute (if exists) + let class_list = match attributes.get("class") { + Some(class_value) => ClassListImpl::from(class_value.as_str()), + None => ClassListImpl::default(), + }; + + NodeImpl::new( + handle.clone(), + location, + &NodeDataTypeInternal::Element(ElementData::new( + handle.clone(), + name, + namespace, + attributes, + class_list, + )), + ) + } + + fn write(&self) -> String { + self.write_from_node(NodeId::root()) + } + + fn write_from_node(&self, _node_id: NodeId) -> String { + todo!(); //This should definitely be implemented + } + + fn cloned_node_by_id(&self, node_id: NodeId) -> Option { + self.arena.node(node_id) + } +} + +impl DocumentImpl { + // Called whenever a node is being mutated in the document. + fn on_document_node_mutation(&mut self, node: &NodeImpl) { + // self.on_document_node_mutation_update_id_in_node(node); + self.on_document_node_mutation_update_named_id(node); + } + + /// Update document's named id structure when the node has ID elements + fn on_document_node_mutation_update_named_id(&mut self, node: &NodeImpl) { + if !node.is_element_node() { + return; + } + + let element_data = node.get_element_data().unwrap(); + if let Some(id_value) = element_data.attributes.get("id") { + // When we have an ID attribute: update the named ID element map. + if is_valid_id_attribute_value(id_value) { + match self.named_id_elements.entry(id_value.clone()) { + Entry::Vacant(entry) => { + entry.insert(node.id()); + } + Entry::Occupied(_) => {} + } + } + } else { + // If we don't have an ID attribute in the node, make sure that we remove and "old" id's that might be in the map. + self.named_id_elements.retain(|_, id| *id != node.id()); + } + } + + /// Print a node and all its children in a tree-like structure + pub fn print_tree( + &self, + node: & as Document>::Node, + prefix: String, + last: bool, + f: &mut Formatter, + ) { + let mut buffer = prefix.clone(); + if last { + buffer.push_str("โ””โ”€ "); + } else { + buffer.push_str("โ”œโ”€ "); + } + // buffer.push_str(format!("{} ", node.id).as_str()); + + match &node.data { + NodeDataTypeInternal::Document(_) => { + _ = writeln!(f, "{buffer}Document"); + } + NodeDataTypeInternal::DocType(DocTypeData { + name, + pub_identifier, + sys_identifier, + }) => { + _ = writeln!(f, r#"{buffer}"#,); + } + NodeDataTypeInternal::Text(TextData { value, .. }) => { + _ = writeln!(f, r#"{buffer}"{value}""#); + } + NodeDataTypeInternal::Comment(CommentData { value, .. }) => { + _ = writeln!(f, "{buffer}"); + } + NodeDataTypeInternal::Element(element) => { + _ = write!(f, "{}<{}", buffer, element.name); + for (key, value) in &element.attributes { + _ = write!(f, " {key}={value}"); + } + + // for (key, value) in node.style.borrow().iter() { + // _ = write!(f, "[CSS: {key}={value}]"); + // } + + _ = writeln!(f, ">"); + } + } + + if prefix.len() > 40 { + _ = writeln!(f, "..."); + return; + } + + let mut buffer = prefix; + if last { + buffer.push_str(" "); + } else { + buffer.push_str("โ”‚ "); + } + + let len = node.children.len(); + for (i, child_id) in node.children.iter().enumerate() { + let child_node = self.node_by_id(*child_id).expect("Child not found"); + self.print_tree(child_node, buffer.clone(), i == len - 1, f); + } + } +} + +impl Display for DocumentImpl { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let root = self.get_root(); + self.print_tree(root, "".to_string(), true, f); + Ok(()) + } +} + +impl DocumentImpl { + /// Fetches a node by named id (string) or returns None when no node with this ID is found + pub fn get_node_by_named_id(&self, named_id: &str) -> Option<& as Document>::Node> { + let node_id = self.named_id_elements.get(named_id)?; + self.arena.node_ref(*node_id) + } + + // /// Fetches a mutable node by named id (string) or returns None when no node with this ID is found + // pub fn get_node_by_named_id_mut( + // &mut self, + // named_id: &str, + // ) -> Option<&mut as Document>::Node> { + // let node_id = self.named_id_elements.get(named_id)?; + // self.arena.node_mut(*node_id) + // } + + // pub fn count_nodes(&self) -> usize { + // self.arena.count_nodes() + // } + + pub fn has_node_id_recursive(&self, parent_id: NodeId, target_node_id: NodeId) -> bool { + let parent = self.arena.node_ref(parent_id); + if parent.is_none() { + return false; + } + + for child_node_id in parent.unwrap().children() { + if *child_node_id == target_node_id { + return true; + } + if self.has_node_id_recursive(*child_node_id, target_node_id) { + return true; + } + } + + false + } + + pub fn peek_next_id(&self) -> NodeId { + self.arena.peek_next_id() + } + + pub fn nodes(&self) -> &HashMap as Document>::Node> { + self.arena.nodes() + } +} + +// Walk the document tree with the given visitor +pub fn walk_document_tree( + handle: DocumentHandle, C>, + visitor: &mut Box as Document>::Node, C>>, +) { + let binding = handle.get(); + let root = binding.get_root(); + internal_visit(handle.clone(), root, visitor); +} + +fn internal_visit( + handle: DocumentHandle, C>, + node: & as Document>::Node, + visitor: &mut Box as Document>::Node, C>>, +) { + visitor.document_enter(node); + + let binding = handle.get(); + for child_id in node.children() { + let child = binding.node_by_id(*child_id).unwrap(); + internal_visit(handle.clone(), child, visitor); + } + drop(binding); + + // Leave node + visitor.document_leave(node); +} + +/// Constructs an iterator from a given DocumentHandle. +/// WARNING: mutations in the document would be reflected +/// in the iterator. It's advised to consume the entire iterator +/// before mutating the document again. +pub struct TreeIterator, C: CssSystem> { + current_node_id: Option, + node_stack: Vec, + document: DocumentHandle, +} + +impl, C: CssSystem> TreeIterator { + #[must_use] + pub fn new(doc: DocumentHandle) -> Self { + let node_stack = vec![doc.get().get_root().id()]; + + Self { + current_node_id: None, + document: doc, + node_stack, + } + } +} + +impl, C: CssSystem> Iterator for TreeIterator { + type Item = NodeId; + + fn next(&mut self) -> Option { + self.current_node_id = self.node_stack.pop(); + + if let Some(current_node_id) = self.current_node_id { + let doc_read = self.document.get(); + + if let Some(sibling_id) = self.document.get().get_next_sibling(current_node_id) { + self.node_stack.push(sibling_id); + } + + if let Some(current_node) = doc_read.node_by_id(current_node_id) { + if let Some(&child_id) = current_node.children().first() { + self.node_stack.push(child_id); + } + } + } + + self.current_node_id + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::document::builder::DocumentBuilderImpl; + use crate::document::query::DocumentQuery; + use crate::document::task_queue::DocumentTaskQueue; + use crate::node::HTML_NAMESPACE; + use crate::parser::query::Query; + use crate::parser::tree_builder::TreeBuilder; + use gosub_css3::system::Css3System; + use gosub_shared::byte_stream::Location; + use gosub_shared::traits::document::DocumentBuilder; + use gosub_shared::traits::node::ClassList; + use gosub_shared::traits::node::ElementDataType; + use gosub_shared::traits::node::NodeType; + use std::collections::HashMap; + + type Document = DocumentImpl; + + #[test] + fn relocate() { + let mut doc_handle = DocumentBuilderImpl::new_document(None); + + let parent_node = Document::new_element_node( + doc_handle.clone(), + "parent", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let node1 = Document::new_element_node( + doc_handle.clone(), + "div1", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let node2 = Document::new_element_node( + doc_handle.clone(), + "div2", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let node3 = Document::new_element_node( + doc_handle.clone(), + "div3", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let node3_1 = Document::new_element_node( + doc_handle.clone(), + "div3_1", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + + let parent_id = doc_handle.get_mut().register_node_at(parent_node, NodeId::root(), None); + let node1_id = doc_handle.get_mut().register_node_at(node1, parent_id, None); + let node2_id = doc_handle.get_mut().register_node_at(node2, parent_id, None); + let node3_id = doc_handle.get_mut().register_node_at(node3, parent_id, None); + let node3_1_id = doc_handle.get_mut().register_node_at(node3_1, node3_id, None); + + assert_eq!( + format!("{}", doc_handle.get()), + r#"โ””โ”€ Document + โ””โ”€ + โ”œโ”€ + โ”œโ”€ + โ””โ”€ + โ””โ”€ +"# + ); + + doc_handle.get_mut().relocate_node(node3_1_id, node1_id); + assert_eq!( + format!("{}", doc_handle.get()), + r#"โ””โ”€ Document + โ””โ”€ + โ”œโ”€ + โ”‚ โ””โ”€ + โ”œโ”€ + โ””โ”€ +"# + ); + + doc_handle.get_mut().relocate_node(node1_id, node2_id); + assert_eq!( + format!("{}", doc_handle.get()), + r#"โ””โ”€ Document + โ””โ”€ + โ”œโ”€ + โ”‚ โ””โ”€ + โ”‚ โ””โ”€ + โ””โ”€ +"# + ); + } + + #[test] + fn verify_node_ids_in_element_data() { + let mut doc_handle = DocumentBuilderImpl::new_document(None); + + let node_1: NodeImpl = DocumentImpl::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let node_2: NodeImpl = DocumentImpl::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + + doc_handle.get_mut().register_node_at(node_1, NodeId::root(), None); + doc_handle.get_mut().register_node_at(node_2, NodeId::root(), None); + + let binding = doc_handle.get(); + let get_node1 = binding.node_by_id(NodeId::from(1usize)).unwrap(); + let get_node2 = binding.node_by_id(NodeId::from(2usize)).unwrap(); + + let NodeDataTypeInternal::Element(_) = &get_node1.data else { + panic!() + }; + assert_eq!(get_node1.id(), NodeId::from(1usize)); + + let NodeDataTypeInternal::Element(_) = &get_node2.data else { + panic!() + }; + assert_eq!(get_node2.id(), NodeId::from(2usize)); + } + + #[test] + fn document_task_queue() { + let doc_handle: DocumentHandle, Css3System> = DocumentBuilderImpl::new_document(None); + + // Using task queue to create the following structure initially: + //
+ //

+ // + // hey + //

+ // + //
+ + // then flush the queue and use it again to add an attribute to

: + //

hey

+ let mut task_queue = DocumentTaskQueue::new(doc_handle.clone()); + + // NOTE: only elements return the ID + let div_id = task_queue.create_element("div", NodeId::root(), None, HTML_NAMESPACE, Location::default()); + assert_eq!(div_id, NodeId::from(1usize)); + + let p_id = task_queue.create_element("p", div_id, None, HTML_NAMESPACE, Location::default()); + assert_eq!(p_id, NodeId::from(2usize)); + + task_queue.create_comment("comment inside p", p_id, Location::default()); + task_queue.create_text("hey", p_id, Location::default()); + task_queue.create_comment("comment inside div", div_id, Location::default()); + + // at this point, the DOM should have NO nodes (besides root) + assert_eq!(doc_handle.get().node_count(), 1); + + // validate our queue is loaded + assert!(!task_queue.is_empty()); + let errors = task_queue.flush(); + assert!(errors.is_empty()); + + // validate queue is empty + assert!(task_queue.is_empty()); + + // DOM should now have all our nodes + assert_eq!(doc_handle.get().arena.node_count(), 6); + + // NOTE: these checks are scoped separately since this is using an + // immutable borrow, and we make a mutable borrow after (to insert the attribute). + // We need this immutable borrow to die off before making a new mutable borrow + // (and again an immutable borrow for validation afterward) + { + // validate DOM is correctly laid out + let doc_read = doc_handle.get(); + let root = doc_read.get_root(); // + let root_children = &root.children; + + // div child + let div_child = doc_read.node_by_id(root_children[0]).unwrap(); + assert_eq!(div_child.type_of(), NodeType::ElementNode); + assert_eq!(div_child.get_element_data().unwrap().name, "div"); + let div_children = &div_child.children; + + // p child + let p_child = doc_read.node_by_id(div_children[0]).unwrap(); + assert_eq!(p_child.type_of(), NodeType::ElementNode); + assert_eq!(p_child.get_element_data().unwrap().name, "p"); + let p_children = &p_child.children; + + // comment inside p + let p_comment = doc_read.node_by_id(p_children[0]).unwrap(); + assert_eq!(p_comment.type_of(), NodeType::CommentNode); + let NodeDataTypeInternal::Comment(p_comment_data) = &p_comment.data else { + panic!() + }; + assert_eq!(p_comment_data.value, "comment inside p"); + + // body inside p + let p_body = doc_read.node_by_id(p_children[1]).unwrap(); + assert_eq!(p_body.type_of(), NodeType::TextNode); + let NodeDataTypeInternal::Text(p_body_data) = &p_body.data else { + panic!() + }; + assert_eq!(p_body_data.value, "hey"); + + // comment inside div + let div_comment = doc_read.node_by_id(div_children[1]).unwrap(); + assert_eq!(div_comment.type_of(), NodeType::CommentNode); + let NodeDataTypeInternal::Comment(div_comment_data) = &div_comment.data else { + panic!() + }; + assert_eq!(div_comment_data.value, "comment inside div"); + } + + // use task queue again to add an ID attribute + // NOTE: inserting attribute in task queue always succeeds + // since it doesn't touch DOM until flush + let _ = task_queue.insert_attribute("id", "myid", p_id, Location::default()); + let errors = task_queue.flush(); + println!("{:?}", errors); + assert!(errors.is_empty()); + + let doc_read = doc_handle.get(); + // validate ID is searchable in dom + assert_eq!(*doc_read.named_id_elements.get("myid").unwrap(), p_id); + + // validate attribute is applied to underlying element + let p_node = doc_read.node_by_id(p_id).unwrap(); + let NodeDataTypeInternal::Element(p_element) = &p_node.data else { + panic!() + }; + assert_eq!(p_element.attributes().get("id").unwrap(), "myid"); + } + + #[test] + fn task_queue_insert_attribute_failues() { + let doc_handle: DocumentHandle, Css3System> = + >::new_document(None); + + let mut task_queue = DocumentTaskQueue::new(doc_handle.clone()); + let div_id = task_queue.create_element("div", NodeId::root(), None, HTML_NAMESPACE, Location::default()); + task_queue.create_comment("content", div_id, Location::default()); // this is NodeId::from(2) + task_queue.flush(); + + // NOTE: inserting attribute in task queue always succeeds + // since it doesn't touch DOM until flush + let _ = task_queue.insert_attribute("id", "myid", NodeId::from(1usize), Location::default()); + let _ = task_queue.insert_attribute("id", "myid", NodeId::from(2usize), Location::default()); + let _ = task_queue.insert_attribute("id", "otherid", NodeId::from(2usize), Location::default()); + let _ = task_queue.insert_attribute("id", "dummyid", NodeId::from(42usize), Location::default()); + let _ = task_queue.insert_attribute("id", "my id", NodeId::from(1usize), Location::default()); + let _ = task_queue.insert_attribute("id", "123", NodeId::from(1usize), Location::default()); + let _ = task_queue.insert_attribute("id", "", NodeId::from(1usize), Location::default()); + let errors = task_queue.flush(); + for error in &errors { + println!("{}", error); + } + assert_eq!(errors.len(), 5); + assert_eq!(errors[0], "ID attribute value 'myid' already exists in DOM"); + assert_eq!(errors[1], "Node id 2 is not an element"); + assert_eq!(errors[2], "Node id 42 not found"); + assert_eq!(errors[3], "ID attribute value 'my id' did not pass validation"); + assert_eq!(errors[4], "ID attribute value '' did not pass validation"); + + // validate that invalid changes did not apply to DOM + let doc_read = doc_handle.get(); + assert!(!doc_read.named_id_elements.contains_key("my id")); + assert!(!doc_read.named_id_elements.contains_key("")); + } + + // this is basically a replica of document_task_queue() test + // but using tree builder directly instead of the task queue + #[test] + fn document_tree_builder() { + let mut doc_handle: DocumentHandle, Css3System> = + >::new_document(None); + + // Using tree builder to create the following structure: + //
+ //

+ // + // hey + //

+ // + //
+ + // NOTE: only elements return the ID + let div_node = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id = doc_handle.get_mut().register_node_at(div_node, NodeId::root(), None); + assert_eq!(div_id, NodeId::from(1usize)); + + let p_node = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let p_id = doc_handle.get_mut().register_node_at(p_node, div_id, None); + assert_eq!(p_id, NodeId::from(2usize)); + + let node = Document::new_comment_node(doc_handle.clone(), "comment inside p", Location::default()); + doc_handle.get_mut().register_node_at(node, p_id, None); + + let node = Document::new_text_node(doc_handle.clone(), "hey", Location::default()); + doc_handle.get_mut().register_node_at(node, p_id, None); + + let node = Document::new_comment_node(doc_handle.clone(), "comment inside div", Location::default()); + doc_handle.get_mut().register_node_at(node, div_id, None); + + let mut binding = doc_handle.get_mut(); + let mut node = binding.cloned_node_by_id(p_id).unwrap(); + if let Some(data) = node.get_element_data_mut() { + data.add_attribute("id", "myid"); + } + binding.update_node(node); + // binding.add_named_id("myid", p_id); + drop(binding); + + // DOM should now have all our nodes + assert_eq!(doc_handle.get().node_count(), 6); + + // validate DOM is correctly laid out + let doc_read = doc_handle.get(); + let root = doc_read.get_root(); // + let root_children = &root.children; + + // div child + let div_child = doc_read.node_by_id(root_children[0]).unwrap(); + assert_eq!(div_child.type_of(), NodeType::ElementNode); + assert_eq!(div_child.get_element_data().unwrap().name, "div"); + let div_children = &div_child.children; + + // p child + let p_child = doc_read.node_by_id(div_children[0]).unwrap(); + assert_eq!(p_child.type_of(), NodeType::ElementNode); + assert_eq!(p_child.get_element_data().unwrap().name, "p"); + let p_children = &p_child.children; + + // comment inside p + let p_comment = doc_read.node_by_id(p_children[0]).unwrap(); + assert_eq!(p_comment.type_of(), NodeType::CommentNode); + let NodeDataTypeInternal::Comment(p_comment_data) = &p_comment.data else { + panic!() + }; + assert_eq!(p_comment_data.value, "comment inside p"); + + // body inside p + let p_body = doc_read.node_by_id(p_children[1]).unwrap(); + assert_eq!(p_body.type_of(), NodeType::TextNode); + let NodeDataTypeInternal::Text(p_body_data) = &p_body.data else { + panic!() + }; + assert_eq!(p_body_data.value, "hey"); + + // comment inside div + let div_comment = doc_read.node_by_id(div_children[1]).unwrap(); + assert_eq!(div_comment.type_of(), NodeType::CommentNode); + let NodeDataTypeInternal::Comment(div_comment_data) = &div_comment.data else { + panic!() + }; + assert_eq!(div_comment_data.value, "comment inside div"); + + // validate ID is searchable in dom + assert_eq!(*doc_read.named_id_elements.get("myid").unwrap(), p_id); + + // validate attribute is applied to underlying element + let p_node = doc_read.node_by_id(p_id).unwrap(); + let NodeDataTypeInternal::Element(p_element) = &p_node.data else { + panic!() + }; + assert_eq!(p_element.attributes().get("id").unwrap(), "myid"); + } + + #[test] + fn insert_generic_attribute() { + let mut doc_handle: DocumentHandle, Css3System> = + >::new_document(None); + + let node = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let node_id = doc_handle.get_mut().register_node_at(node, NodeId::root(), None); + + let mut binding = doc_handle.get_mut(); + let mut node = binding.cloned_node_by_id(node_id).unwrap(); + if let Some(data) = node.get_element_data_mut() { + data.add_attribute("key", "value"); + binding.update_node(node); + } + drop(binding); + + let doc_read = doc_handle.get(); + let Some(data) = doc_read.node_by_id(node_id).unwrap().get_element_data() else { + panic!() + }; + assert_eq!(data.attributes().get("key").unwrap(), "value"); + } + + #[test] + fn task_queue_insert_generic_attribute() { + let doc_handle: DocumentHandle, Css3System> = + >::new_document(None); + + let mut task_queue = DocumentTaskQueue::new(doc_handle.clone()); + let div_id = task_queue.create_element("div", NodeId::root(), None, HTML_NAMESPACE, Location::default()); + let _ = task_queue.insert_attribute("key", "value", div_id, Location::default()); + let errors = task_queue.flush(); + assert!(errors.is_empty()); + let doc_read = doc_handle.get(); + let NodeDataTypeInternal::Element(element) = &doc_read.node_by_id(div_id).unwrap().data else { + panic!() + }; + assert_eq!(element.attributes().get("key").unwrap(), "value"); + } + + #[test] + fn insert_class_attribute() { + let mut doc_handle: DocumentHandle, Css3System> = + >::new_document(None); + + let div_node = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id = doc_handle.get_mut().register_node_at(div_node, NodeId::root(), None); + + let mut binding = doc_handle.get_mut(); + let mut node = binding.cloned_node_by_id(div_id).unwrap(); + if let Some(data) = node.get_element_data_mut() { + data.add_attribute("class", "one two three"); + binding.update_node(node); + } + drop(binding); + + let binding = doc_handle.get(); + let NodeDataTypeInternal::Element(element_data) = &binding.node_by_id(div_id).unwrap().data else { + panic!() + }; + assert!(element_data.classlist().contains("one")); + assert!(element_data.classlist().contains("two")); + assert!(element_data.classlist().contains("three")); + } + + #[test] + fn task_queue_insert_class_attribute() { + let doc_handle: DocumentHandle, Css3System> = DocumentBuilderImpl::new_document(None); + + let mut task_queue = DocumentTaskQueue::new(doc_handle.clone()); + let div_id = task_queue.create_element("div", NodeId::root(), None, HTML_NAMESPACE, Location::default()); + let _ = task_queue.insert_attribute("class", "one two three", div_id, Location::default()); + let errors = task_queue.flush(); + println!("{:?}", errors); + assert!(errors.is_empty()); + + let binding = doc_handle.get(); + let element = binding.node_by_id(div_id).unwrap().get_element_data().unwrap(); + + assert!(element.classlist().contains("one")); + assert!(element.classlist().contains("two")); + assert!(element.classlist().contains("three")); + } + + #[test] + fn uninitialized_query() { + let doc_handle: DocumentHandle, Css3System> = DocumentBuilderImpl::new_document(None); + + let query = Query::new(); + let found_ids = DocumentQuery::query(doc_handle.clone(), &query); + if let Err(err) = found_ids { + assert_eq!( + err.to_string(), + "query: generic error: Query predicate is uninitialized" + ); + } else { + panic!() + } + } + + #[test] + fn single_query_equals_tag_find_first() { + //
+ //
+ //

+ //

+ //

+ //

+ //

+ let mut doc_handle: DocumentHandle, Css3System> = + DocumentBuilderImpl::new_document(None); + + let div_node = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id = doc_handle.get_mut().register_node_at(div_node, NodeId::root(), None); + + let div_node_2 = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id_2 = doc_handle.get_mut().register_node_at(div_node_2, div_id, None); + + let p_node = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let p_id = doc_handle.get_mut().register_node_at(p_node, div_id_2, None); + + let p_node = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let _ = doc_handle.get_mut().register_node_at(p_node, div_id, None); + + let div_node_3 = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id_3 = doc_handle.get_mut().register_node_at(div_node_3, NodeId::root(), None); + + let p_node = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let _ = doc_handle.get_mut().register_node_at(p_node, div_id_3, None); + + let p_node = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + doc_handle.get_mut().register_node_at(p_node, NodeId::root(), None); + + let query = Query::new().equals_tag("p").find_first(); + let found_ids = DocumentQuery::query(doc_handle.clone(), &query).unwrap(); + assert_eq!(found_ids.len(), 1); + assert_eq!(found_ids, [p_id]); + } + + #[test] + fn single_query_equals_tag_find_all() { + //

+ //
+ //

+ //

+ //

+ //

+ //

+ let mut doc_handle: DocumentHandle, Css3System> = + DocumentBuilderImpl::new_document(None); + + let div_node = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id = doc_handle.get_mut().register_node_at(div_node, NodeId::root(), None); + + let div_node_2 = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id_2 = doc_handle.get_mut().register_node_at(div_node_2, div_id, None); + + let p_node = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let p_id = doc_handle.get_mut().register_node_at(p_node, div_id_2, None); + + let p_node_2 = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let p_id_2 = doc_handle.get_mut().register_node_at(p_node_2, div_id, None); + + let div_node_3 = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id_3 = doc_handle.get_mut().register_node_at(div_node_3, NodeId::root(), None); + + let p_node_3 = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let p_id_3 = doc_handle.get_mut().register_node_at(p_node_3, div_id_3, None); + + let p_node_4 = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let p_id_4 = doc_handle.get_mut().register_node_at(p_node_4, NodeId::root(), None); + + let query = Query::new().equals_tag("p").find_all(); + let found_ids = DocumentQuery::query(doc_handle.clone(), &query).unwrap(); + assert_eq!(found_ids.len(), 4); + assert_eq!(found_ids, [p_id, p_id_2, p_id_3, p_id_4]); + } + + #[test] + fn single_query_equals_id() { + //

+ //
+ //

+ //

+ //

+ //

+ //

+ let mut doc_handle: DocumentHandle, Css3System> = + DocumentBuilderImpl::new_document(None); + + let div_node = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id = doc_handle.get_mut().register_node_at(div_node, NodeId::root(), None); + + let div_node_2 = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id_2 = doc_handle.get_mut().register_node_at(div_node_2, div_id, None); + + let p_node = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let _ = doc_handle.get_mut().register_node_at(p_node, div_id_2, None); + + let p_node_2 = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let p_id_2 = doc_handle.get_mut().register_node_at(p_node_2, div_id, None); + + let mut binding = doc_handle.get_mut(); + let mut node = binding.cloned_node_by_id(p_id_2).unwrap(); + if let Some(data) = node.get_element_data_mut() { + data.add_attribute("id", "myid"); + binding.update_node(node); + } + drop(binding); + + let div_node_3 = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id_3 = doc_handle.get_mut().register_node_at(div_node_3, NodeId::root(), None); + + let p_node = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let _ = doc_handle.get_mut().register_node_at(p_node, div_id_3, None); + + let p_node = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let _ = doc_handle.get_mut().register_node_at(p_node, NodeId::root(), None); + + let query = Query::new().equals_id("myid").find_first(); + let found_ids = DocumentQuery::query(doc_handle.clone(), &query).unwrap(); + assert_eq!(found_ids.len(), 1); + assert_eq!(found_ids, [p_id_2]); + } + + #[test] + fn single_query_contains_class_find_first() { + //

+ //
+ //

+ //

+ //

+ //

+ //

+ let mut doc_handle: DocumentHandle, Css3System> = + DocumentBuilderImpl::new_document(None); + + let div_node = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id = doc_handle.get_mut().register_node_at(div_node, NodeId::root(), None); + + let div_node_2 = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id_2 = doc_handle.get_mut().register_node_at(div_node_2, div_id, None); + + let p_node = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let p_id = doc_handle.get_mut().register_node_at(p_node, div_id_2, None); + + let mut binding = doc_handle.get_mut(); + let mut node = binding.cloned_node_by_id(p_id).unwrap(); + if let Some(data) = node.get_element_data_mut() { + data.add_attribute("class", "one two"); + binding.update_node(node); + } + drop(binding); + + let p_node_2 = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let _ = doc_handle.get_mut().register_node_at(p_node_2, div_id, None); + + let mut binding = doc_handle.get_mut(); + let mut node = binding.cloned_node_by_id(div_id).unwrap(); + if let Some(data) = node.get_element_data_mut() { + data.add_attribute("class", "one"); + data.add_attribute("id", "myid"); + binding.update_node(node); + } + drop(binding); + + let div_node_3 = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id_3 = doc_handle.get_mut().register_node_at(div_node_3, NodeId::root(), None); + + let p_node_3 = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let p_id_3 = doc_handle.get_mut().register_node_at(p_node_3, div_id_3, None); + + let mut binding = doc_handle.get_mut(); + let mut node = binding.cloned_node_by_id(p_id_3).unwrap(); + if let Some(data) = node.get_element_data_mut() { + data.add_attribute("class", "two_tree"); + binding.update_node(node); + } + drop(binding); + + let p_node_4 = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let p_id_4 = doc_handle.get_mut().register_node_at(p_node_4, NodeId::root(), None); + + let mut binding = doc_handle.get_mut(); + let mut node = binding.cloned_node_by_id(p_id_4).unwrap(); + if let Some(data) = node.get_element_data_mut() { + data.add_attribute("class", "three"); + binding.update_node(node); + } + drop(binding); + + let query = Query::new().contains_class("two").find_first(); + let found_ids = DocumentQuery::query(doc_handle.clone(), &query).unwrap(); + assert_eq!(found_ids.len(), 1); + assert_eq!(found_ids, [p_id]); + } + + #[test] + fn single_query_contains_class_find_all() { + //

+ //
+ //

+ //

+ //

+ //

+ //

+ let mut doc_handle: DocumentHandle, Css3System> = + DocumentBuilderImpl::new_document(None); + + let div_node = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id = doc_handle.get_mut().register_node_at(div_node, NodeId::root(), None); + + let div_node_2 = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id_2 = doc_handle.get_mut().register_node_at(div_node_2, div_id, None); + + let p_node = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let p_id = doc_handle.get_mut().register_node_at(p_node, div_id_2, None); + + let mut binding = doc_handle.get_mut(); + let mut node = binding.cloned_node_by_id(p_id).unwrap(); + if let Some(data) = node.get_element_data_mut() { + data.add_attribute("class", "one two"); + binding.update_node(node); + } + drop(binding); + + let p_node_2 = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let p_id_2 = doc_handle.get_mut().register_node_at(p_node_2, div_id, None); + + let mut binding = doc_handle.get_mut(); + let mut node = binding.cloned_node_by_id(p_id_2).unwrap(); + if let Some(data) = node.get_element_data_mut() { + data.add_attribute("class", "one"); + data.add_attribute("id", "myid"); + binding.update_node(node); + } + drop(binding); + + let div_node_3 = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id_3 = doc_handle.get_mut().register_node_at(div_node_3, NodeId::root(), None); + + let p_node_3 = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let p_id_3 = doc_handle.get_mut().register_node_at(p_node_3, div_id_3, None); + + let mut binding = doc_handle.get_mut(); + let mut node = binding.cloned_node_by_id(p_id_3).unwrap(); + if let Some(data) = node.get_element_data_mut() { + data.add_attribute("class", "two three"); + binding.update_node(node); + } + drop(binding); + + let p_node_4 = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let p_id_4 = doc_handle.get_mut().register_node_at(p_node_4, NodeId::root(), None); + + let mut binding = doc_handle.get_mut(); + let mut node = binding.cloned_node_by_id(p_id_4).unwrap(); + if let Some(data) = node.get_element_data_mut() { + data.add_attribute("class", "three"); + binding.update_node(node); + } + drop(binding); + + let query = Query::new().contains_class("two").find_all(); + let found_ids = DocumentQuery::query(doc_handle.clone(), &query).unwrap(); + assert_eq!(found_ids.len(), 2); + assert_eq!(found_ids, [p_id, p_id_3]); + } + + #[test] + fn single_query_contains_attribute_find_first() { + //

+ //
+ //

+ //

+ //

+ //

+ //

+ let mut doc_handle: DocumentHandle, Css3System> = + DocumentBuilderImpl::new_document(None); + + let div_node = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id = doc_handle.get_mut().register_node_at(div_node, NodeId::root(), None); + + let div_node_2 = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id_2 = doc_handle.get_mut().register_node_at(div_node_2, div_id, None); + + let mut binding = doc_handle.get_mut(); + let mut node = binding.cloned_node_by_id(div_id_2).unwrap(); + if let Some(data) = node.get_element_data_mut() { + data.add_attribute("id", "myid"); + data.add_attribute("style", "somestyle"); + binding.update_node(node); + } + drop(binding); + + let p_node = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let p_id = doc_handle.get_mut().register_node_at(p_node, div_id_2, None); + + let mut binding = doc_handle.get_mut(); + let mut node = binding.cloned_node_by_id(p_id).unwrap(); + if let Some(data) = node.get_element_data_mut() { + data.add_attribute("title", "key"); + binding.update_node(node); + } + drop(binding); + + let p_node = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let _ = doc_handle.get_mut().register_node_at(p_node, div_id, None); + + let div_node_3 = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id_3 = doc_handle.get_mut().register_node_at(div_node_3, NodeId::root(), None); + + let mut binding = doc_handle.get_mut(); + let mut node = binding.cloned_node_by_id(div_id_3).unwrap(); + if let Some(data) = node.get_element_data_mut() { + data.add_attribute("style", "otherstyle"); + data.add_attribute("id", "otherid"); + binding.update_node(node); + } + drop(binding); + + let p_node = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let _ = doc_handle.get_mut().register_node_at(p_node, div_id_3, None); + + let p_node_4 = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let p_id_4 = doc_handle.get_mut().register_node_at(p_node_4, NodeId::root(), None); + + let mut binding = doc_handle.get_mut(); + let mut node = binding.cloned_node_by_id(p_id_4).unwrap(); + if let Some(data) = node.get_element_data_mut() { + data.add_attribute("title", "yo"); + data.add_attribute("style", "cat"); + binding.update_node(node); + } + drop(binding); + + let query = Query::new().contains_attribute("style").find_first(); + let found_ids = DocumentQuery::query(doc_handle.clone(), &query).unwrap(); + assert_eq!(found_ids.len(), 1); + assert_eq!(found_ids, [div_id_2]); + } + + #[test] + fn single_query_contains_attribute_find_all() { + //

+ //
+ //

+ //

+ //

+ //

+ //

+ let mut doc_handle: DocumentHandle, Css3System> = + DocumentBuilderImpl::new_document(None); + + let div_node = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id = doc_handle.get_mut().register_node_at(div_node, NodeId::root(), None); + + let div_node_2 = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id_2 = doc_handle.get_mut().register_node_at(div_node_2, div_id, None); + + let mut binding = doc_handle.get_mut(); + let mut node = binding.cloned_node_by_id(div_id_2).unwrap(); + if let Some(data) = node.get_element_data_mut() { + data.add_attribute("id", "myid"); + data.add_attribute("style", "somestyle"); + binding.update_node(node); + } + drop(binding); + + let p_node = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let p_id = doc_handle.get_mut().register_node_at(p_node, div_id_2, None); + + let mut binding = doc_handle.get_mut(); + let mut node = binding.cloned_node_by_id(p_id).unwrap(); + if let Some(data) = node.get_element_data_mut() { + data.add_attribute("title", "key"); + binding.update_node(node); + } + drop(binding); + + let p_node = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let _ = doc_handle.get_mut().register_node_at(p_node, div_id, None); + + let div_node_3 = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id_3 = doc_handle.get_mut().register_node_at(div_node_3, NodeId::root(), None); + + let mut binding = doc_handle.get_mut(); + let mut node = binding.cloned_node_by_id(div_id_3).unwrap(); + if let Some(data) = node.get_element_data_mut() { + data.add_attribute("style", "otherstyle"); + data.add_attribute("id", "otherid"); + + binding.update_node(node); + } + drop(binding); + + let p_node = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let _ = doc_handle.get_mut().register_node_at(p_node, div_id_3, None); + + let p_node_4 = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let p_id_4 = doc_handle.get_mut().register_node_at(p_node_4, NodeId::root(), None); + + let mut binding = doc_handle.get_mut(); + let mut node = binding.cloned_node_by_id(p_id_4).unwrap(); + if let Some(data) = node.get_element_data_mut() { + data.add_attribute("title", "yo"); + data.add_attribute("style", "cat"); + + binding.update_node(node); + } + drop(binding); + + let query = Query::new().contains_attribute("style").find_all(); + let found_ids = DocumentQuery::query(doc_handle.clone(), &query).unwrap(); + assert_eq!(found_ids.len(), 3); + assert_eq!(found_ids, [div_id_2, div_id_3, p_id_4]); + } + + #[test] + fn single_query_contains_child_find_first() { + //

+ //
+ //

+ //

+ //

+ //

+ //

+ let mut doc_handle: DocumentHandle, Css3System> = + DocumentBuilderImpl::new_document(None); + + let div_node = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id = doc_handle.get_mut().register_node_at(div_node, NodeId::root(), None); + + let div_node_2 = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id_2 = doc_handle.get_mut().register_node_at(div_node_2, div_id, None); + + let p_node = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let _ = doc_handle.get_mut().register_node_at(p_node, div_id_2, None); + + let p_node = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let _ = doc_handle.get_mut().register_node_at(p_node, div_id, None); + + let div_node_3 = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id_3 = doc_handle.get_mut().register_node_at(div_node_3, NodeId::root(), None); + + let p_node = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let _ = doc_handle.get_mut().register_node_at(p_node, div_id_3, None); + + let p_node = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let _ = doc_handle.get_mut().register_node_at(p_node, NodeId::root(), None); + + let query = Query::new().contains_child_tag("p").find_first(); + let found_ids = DocumentQuery::query(doc_handle.clone(), &query).unwrap(); + assert_eq!(found_ids.len(), 1); + assert_eq!(found_ids, [NodeId::root()]); + } + + #[test] + fn single_query_contains_child_find_all() { + //

+ //
+ //

+ //

+ //

+ //

+ //

+ let mut doc_handle: DocumentHandle, Css3System> = + DocumentBuilderImpl::new_document(None); + + let div_node = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id = doc_handle.get_mut().register_node_at(div_node, NodeId::root(), None); + + let div_node_2 = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id_2 = doc_handle.get_mut().register_node_at(div_node_2, div_id, None); + + let p_node = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let _ = doc_handle.get_mut().register_node_at(p_node, div_id_2, None); + + let p_node = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let _ = doc_handle.get_mut().register_node_at(p_node, div_id, None); + + let div_node_3 = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id_3 = doc_handle.get_mut().register_node_at(div_node_3, NodeId::root(), None); + + let p_node = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let _ = doc_handle.get_mut().register_node_at(p_node, div_id_3, None); + + let p_node = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let _ = doc_handle.get_mut().register_node_at(p_node, NodeId::root(), None); + + let query = Query::new().contains_child_tag("p").find_all(); + let found_ids = DocumentQuery::query(doc_handle.clone(), &query).unwrap(); + assert_eq!(found_ids.len(), 4); + assert_eq!(found_ids, [NodeId::root(), div_id, div_id_2, div_id_3]); + } + + #[test] + fn single_query_has_parent_find_first() { + //

+ //
+ //

+ //

+ //

+ //

+ //

+ let mut doc_handle: DocumentHandle, Css3System> = + DocumentBuilderImpl::new_document(None); + + let div_node = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id = doc_handle.get_mut().register_node_at(div_node, NodeId::root(), None); + + let div_node_2 = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id_2 = doc_handle.get_mut().register_node_at(div_node_2, div_id, None); + + let p_node = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let _ = doc_handle.get_mut().register_node_at(p_node, div_id_2, None); + + let p_node = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let _ = doc_handle.get_mut().register_node_at(p_node, div_id, None); + + let div_node_3 = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id_3 = doc_handle.get_mut().register_node_at(div_node_3, NodeId::root(), None); + + let p_node = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let _ = doc_handle.get_mut().register_node_at(p_node, div_id_3, None); + + let p_node = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let _ = doc_handle.get_mut().register_node_at(p_node, NodeId::root(), None); + + let query = Query::new().has_parent_tag("div").find_first(); + let found_ids = DocumentQuery::query(doc_handle.clone(), &query).unwrap(); + assert_eq!(found_ids.len(), 1); + assert_eq!(found_ids, [div_id_2]); + } + + #[test] + fn single_query_has_parent_find_all() { + //

+ //
+ //

+ //

+ //

+ //

+ //

+ let mut doc_handle: DocumentHandle, Css3System> = + DocumentBuilderImpl::new_document(None); + + let div_node = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id = doc_handle.get_mut().register_node_at(div_node, NodeId::root(), None); + + let div_node_2 = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id_2 = doc_handle.get_mut().register_node_at(div_node_2, div_id, None); + + let p_node = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let p_id = doc_handle.get_mut().register_node_at(p_node, div_id_2, None); + + let p_node_2 = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let p_id_2 = doc_handle.get_mut().register_node_at(p_node_2, div_id, None); + + let div_node_3 = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id_3 = doc_handle.get_mut().register_node_at(div_node_3, NodeId::root(), None); + + let p_node_3 = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let p_id_3 = doc_handle.get_mut().register_node_at(p_node_3, div_id_3, None); + + let p_node = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let _ = doc_handle.get_mut().register_node_at(p_node, NodeId::root(), None); + + let query = Query::new().has_parent_tag("div").find_all(); + let found_ids = DocumentQuery::query(doc_handle.clone(), &query).unwrap(); + assert_eq!(found_ids.len(), 4); + assert_eq!(found_ids, [div_id_2, p_id, p_id_2, p_id_3]); + } + + #[test] + fn tree_iterator() { + let mut doc_handle: DocumentHandle, Css3System> = + DocumentBuilderImpl::new_document(None); + + //

+ //
+ //

first p tag + //

second p tag + //

third p tag + let div_node = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id = doc_handle.get_mut().register_node_at(div_node, NodeId::root(), None); + + let div_node_2 = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id_2 = doc_handle.get_mut().register_node_at(div_node_2, div_id, None); + + let p_node = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let p_id = doc_handle.get_mut().register_node_at(p_node, div_id_2, None); + + let text_node = Document::new_text_node(doc_handle.clone(), "first p tag", Location::default()); + let text_id = doc_handle.get_mut().register_node_at(text_node, p_id, None); + + let p_node_2 = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let p_id_2 = doc_handle.get_mut().register_node_at(p_node_2, div_id_2, None); + + let text_node_2 = Document::new_text_node(doc_handle.clone(), "second p tag", Location::default()); + let text_id_2 = doc_handle.get_mut().register_node_at(text_node_2, p_id_2, None); + let p_node_3 = Document::new_element_node( + doc_handle.clone(), + "p", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let p_id_3 = doc_handle.get_mut().register_node_at(p_node_3, div_id, None); + + let text_node_3 = Document::new_text_node(doc_handle.clone(), "third p tag", Location::default()); + let text_id_3 = doc_handle.get_mut().register_node_at(text_node_3, p_id_3, None); + + let tree_iterator = TreeIterator::new(doc_handle.clone()); + + let expected_order = vec![ + NodeId::root(), + div_id, + div_id_2, + p_id, + text_id, + p_id_2, + text_id_2, + p_id_3, + text_id_3, + ]; + + let mut traversed_nodes = Vec::new(); + for current_node_id in tree_iterator { + traversed_nodes.push(current_node_id); + } + + assert_eq!(expected_order, traversed_nodes); + } + + #[test] + fn tree_iterator_mutation() { + let mut doc_handle: DocumentHandle, Css3System> = + DocumentBuilderImpl::new_document(None); + + let div_node = Document::new_element_node( + doc_handle.clone(), + "div", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id = doc_handle.get_mut().register_node_at(div_node, NodeId::root(), None); + + let mut tree_iterator = TreeIterator::new(doc_handle.clone()); + let mut current_node_id; + + current_node_id = tree_iterator.next(); + assert_eq!(current_node_id.unwrap(), NodeId::root()); + + // we mutate the tree while the iterator is still "open" + let div_node_2 = Document::new_element_node( + doc_handle.clone(), + "div_1", + Some(HTML_NAMESPACE), + HashMap::new(), + Location::default(), + ); + let div_id_2 = doc_handle.get_mut().register_node_at(div_node_2, NodeId::root(), None); + + current_node_id = tree_iterator.next(); + assert_eq!(current_node_id.unwrap(), div_id); + + // and find this node on next iteration + current_node_id = tree_iterator.next(); + assert_eq!(current_node_id.unwrap(), div_id_2); + } +} diff --git a/crates/gosub_html5/src/document/fragment.rs b/crates/gosub_html5/src/document/fragment.rs new file mode 100644 index 000000000..4853a3803 --- /dev/null +++ b/crates/gosub_html5/src/document/fragment.rs @@ -0,0 +1,63 @@ +use crate::DocumentHandle; +use core::fmt; +use core::fmt::Debug; + +use crate::document::document_impl::DocumentImpl; +use crate::node::arena::NodeArena; +use crate::node::node_impl::NodeImpl; +use gosub_shared::node::NodeId; +use gosub_shared::traits::css3::CssSystem; +use gosub_shared::traits::document::DocumentFragment; + +/// Defines a document fragment which can be attached to for instance a