diff --git a/.editorconfig b/.editorconfig
index 7a22e08..e637dc0 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -16,5 +16,5 @@ indent_size = 2
[*.css]
indent_size = 2
-[*.{json,json5,js}]
+[*.{json,json5,js,sh}]
indent_size = 2
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..c97b87f
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,5 @@
+* text=auto
+Cargo.lock -diff
+
+# Don't export/archive these files
+.github export-ignore
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index d3a2a14..c8a2602 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -42,6 +42,8 @@ jobs:
features:
- []
- [tracing]
+ - [strict]
+ - [tracing, strict]
steps:
- uses: actions/checkout@v4
- name: Setup Rust
@@ -53,13 +55,16 @@ jobs:
~/.cargo/registry
~/.cargo/git
target
- key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.toml') }}-${{ matrix.rust-toolchain }}-${{ join(matrix.features, ',') }}
- - name: Run
+ key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}-${{ matrix.rust-toolchain }}-${{ join(matrix.features, '-') }}
+ - name: Unit tests
if: matrix.features[0] != null
run: cargo test -p svg-path-cst --features ${{ join(matrix.features, ',') }}
- - name: Run
+ - name: Unit tests
if: matrix.features[0] == null
run: cargo test -p svg-path-cst
+ - name: Integration tests
+ if: matrix.os != 'windows-latest'
+ run: sh src/tests/integration.sh
test-release-crate:
name: Test crate release
diff --git a/.gitignore b/.gitignore
index 81685b0..51dfe1c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,5 @@
target/
-Cargo.lock
.prettier-cache
*.svg
*.data
-*.data.old
\ No newline at end of file
+*.data.old
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 4030069..1f63877 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -7,6 +7,25 @@ repos:
- --cache
- --cache-location=.prettier-cache
- --ignore-path=.gitignore
+ - repo: meta
+ hooks:
+ - id: check-hooks-apply
+ name: check-hooks-apply
+ - id: check-useless-excludes
+ name: check-useless-excludes
+ - repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v4.6.0
+ hooks:
+ - id: trailing-whitespace
+ name: trailing-whitespace
+ - id: end-of-file-fixer
+ name: end-of-file-fixer
+ exclude: fuzz/
+ - repo: https://github.com/DavidAnson/markdownlint-cli2
+ rev: v0.13.0
+ hooks:
+ - id: markdownlint-cli2
+ exclude: ^LICENSE\.md$
- repo: https://github.com/doublify/pre-commit-rust
rev: v1.0
hooks:
@@ -50,6 +69,26 @@ repos:
-D,
clippy::semicolon_if_nothing_returned,
]
+ - id: clippy
+ alias: clippy-strict
+ name: clippy-strict
+ args:
+ [
+ --features=strict,
+ --,
+ -D,
+ warnings,
+ -D,
+ clippy::perf,
+ -D,
+ clippy::print_stdout,
+ -D,
+ clippy::explicit_iter_loop,
+ -D,
+ clippy::uninlined_format_args,
+ -D,
+ clippy::semicolon_if_nothing_returned,
+ ]
- repo: https://github.com/mondeja/rust-pc-hooks
rev: v1.2.0
hooks:
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..c1a9a81
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,884 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "anes"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
+
+[[package]]
+name = "anstyle"
+version = "1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
+
+[[package]]
+name = "autocfg"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
+
+[[package]]
+name = "bumpalo"
+version = "3.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
+
+[[package]]
+name = "cast"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "ciborium"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
+dependencies = [
+ "ciborium-io",
+ "ciborium-ll",
+ "serde",
+]
+
+[[package]]
+name = "ciborium-io"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
+
+[[package]]
+name = "ciborium-ll"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
+dependencies = [
+ "ciborium-io",
+ "half",
+]
+
+[[package]]
+name = "clap"
+version = "4.5.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019"
+dependencies = [
+ "clap_builder",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.5.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6"
+dependencies = [
+ "anstyle",
+ "clap_lex",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
+
+[[package]]
+name = "criterion"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f"
+dependencies = [
+ "anes",
+ "cast",
+ "ciborium",
+ "clap",
+ "criterion-plot",
+ "is-terminal",
+ "itertools",
+ "num-traits",
+ "once_cell",
+ "oorandom",
+ "plotters",
+ "rayon",
+ "regex",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "tinytemplate",
+ "walkdir",
+]
+
+[[package]]
+name = "criterion-plot"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
+dependencies = [
+ "cast",
+ "itertools",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
+dependencies = [
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
+
+[[package]]
+name = "crunchy"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
+
+[[package]]
+name = "deranged"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
+dependencies = [
+ "powerfmt",
+]
+
+[[package]]
+name = "doc-comment"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
+
+[[package]]
+name = "either"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
+
+[[package]]
+name = "half"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888"
+dependencies = [
+ "cfg-if",
+ "crunchy",
+]
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "hermit-abi"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
+
+[[package]]
+name = "is-terminal"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "itertools"
+version = "0.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
+
+[[package]]
+name = "js-sys"
+version = "0.3.70"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+
+[[package]]
+name = "libc"
+version = "0.2.158"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
+
+[[package]]
+name = "log"
+version = "0.4.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
+
+[[package]]
+name = "matchers"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
+dependencies = [
+ "regex-automata 0.1.10",
+]
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "nu-ansi-term"
+version = "0.46.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
+dependencies = [
+ "overload",
+ "winapi",
+]
+
+[[package]]
+name = "num-conv"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "oorandom"
+version = "11.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9"
+
+[[package]]
+name = "overload"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
+
+[[package]]
+name = "plotters"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3"
+dependencies = [
+ "num-traits",
+ "plotters-backend",
+ "plotters-svg",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "plotters-backend"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7"
+
+[[package]]
+name = "plotters-svg"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705"
+dependencies = [
+ "plotters-backend",
+]
+
+[[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rayon"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
+dependencies = [
+ "crossbeam-deque",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "regex"
+version = "1.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata 0.4.7",
+ "regex-syntax 0.8.4",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
+dependencies = [
+ "regex-syntax 0.6.29",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax 0.8.4",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
+
+[[package]]
+name = "ryu"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.209"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.209"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.127"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad"
+dependencies = [
+ "itoa",
+ "memchr",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "sharded-slab"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "snafu"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b835cb902660db3415a672d862905e791e54d306c6e8189168c7f3d9ae1c79d"
+dependencies = [
+ "snafu-derive",
+]
+
+[[package]]
+name = "snafu-derive"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38d1e02fca405f6280643174a50c942219f0bbf4dbf7d480f1dd864d6f211ae5"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "svg-path-cst"
+version = "0.1.3"
+dependencies = [
+ "criterion",
+ "doc-comment",
+ "snafu",
+ "tracing",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.76"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.63"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.63"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+]
+
+[[package]]
+name = "time"
+version = "0.3.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
+dependencies = [
+ "deranged",
+ "itoa",
+ "num-conv",
+ "powerfmt",
+ "serde",
+ "time-core",
+ "time-macros",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
+
+[[package]]
+name = "time-macros"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
+dependencies = [
+ "num-conv",
+ "time-core",
+]
+
+[[package]]
+name = "tinytemplate"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
+dependencies = [
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "tracing"
+version = "0.1.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
+dependencies = [
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-appender"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf"
+dependencies = [
+ "crossbeam-channel",
+ "thiserror",
+ "time",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
+name = "tracing-example"
+version = "0.1.0"
+dependencies = [
+ "svg-path-cst",
+ "tracing",
+ "tracing-appender",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
+dependencies = [
+ "matchers",
+ "nu-ansi-term",
+ "once_cell",
+ "regex",
+ "sharded-slab",
+ "thread_local",
+ "tracing",
+ "tracing-core",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "walkdir"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.93"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.93"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.93"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.93"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.93"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
+
+[[package]]
+name = "web-sys"
+version = "0.3.70"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
+dependencies = [
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
diff --git a/Cargo.toml b/Cargo.toml
index 26ef94b..b1375ab 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "svg-path-cst"
-version = "0.1.2"
+version = "0.1.3"
edition = "2021"
readme = "README.md"
description = "CST SVG path parser."
@@ -18,15 +18,21 @@ path = "src/main.rs"
[features]
tracing = ["dep:tracing"]
+strict = []
[dependencies]
snafu = { version = "0.8", default-features = false }
-tracing = { version = "0.1", optional = true, default-features = false, features = ["attributes"]}
+tracing = { version = "0.1", optional = true, default-features = false, features = [
+ "attributes"
+] }
[dev-dependencies]
-doc-comment = "0.3.3"
+doc-comment = "0.3"
criterion = { version = "0.5", features = ["html_reports"] }
[[bench]]
-name = "performance"
+name = "no_features"
harness = false
+
+[workspace]
+members = [".", "examples/tracing"]
diff --git a/README.md b/README.md
index 22a0626..34602c3 100644
--- a/README.md
+++ b/README.md
@@ -96,10 +96,29 @@ assert_eq!(cst, Ok(expected_cst));
## Compatibility
-This crate is compatible with SVG v1.1 paths, as defined in the [W3C SVG 1.1](https://www.w3.org/TR/SVG11/paths.html#PathData) and [`no_std` environments](https://docs.rust-embedded.org/book/intro/no-std.html).
+This crate is compatible with SVG v1.1 paths, as defined in the [W3C SVG 1.1]
+and [`no_std` environments].
## Features
-- **`tracing`**: Adds [`tracing`] support.
+### **`tracing`**
+Add [`tracing`] support. See the [`tracing` example] to learn how to use it.
+
+### **`strict`**
+
+Enable strict mode. The differences between strict and non-strict modes are:
+
+- With empty input (`b""`), non-strict mode returns an empty vector, while
+ strict mode returns a `SyntaxError::UnexpectedEnding` error.
+- With the input `b"none"`, non-strict mode returns a `SVGPathCSTNode::None`
+ node, while strict mode returns a `SyntaxError::ExpectedMovetoCommand` error.
+ The `"none"` input is defined
+ [by the SVG specification](https://www.w3.org/TR/SVG/paths.html#TheDProperty).
+- With input containing only whitespaces, non-strict mode returns an empty vector,
+ while strict mode returns a `SyntaxError::ExpectedMovetoCommand` error.
+
+[W3C SVG 1.1]: https://www.w3.org/TR/SVG11/paths.html#PathData
+[`no_std` environments]: https://docs.rust-embedded.org/book/intro/no-std.html
[`tracing`]: https://docs.rs/tracing/latest/tracing
+[`tracing` example]: https://github.com/mondeja/svg-path-cst/tree/master/examples/tracing
diff --git a/benches/performance.rs b/benches/no_features.rs
similarity index 100%
rename from benches/performance.rs
rename to benches/no_features.rs
diff --git a/examples/tracing/Cargo.toml b/examples/tracing/Cargo.toml
new file mode 100644
index 0000000..2fc6929
--- /dev/null
+++ b/examples/tracing/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "tracing-example"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+svg-path-cst = { path = "../..", features = ["tracing"] }
+tracing = { version = "0.1", default-features = false }
+tracing-appender = "0.2"
+tracing-subscriber = { version = "0.3", default-features = false, features = [
+ "env-filter",
+ "ansi",
+] }
diff --git a/examples/tracing/src/main.rs b/examples/tracing/src/main.rs
new file mode 100644
index 0000000..8cac582
--- /dev/null
+++ b/examples/tracing/src/main.rs
@@ -0,0 +1,23 @@
+fn init_tracing() -> tracing_appender::non_blocking::WorkerGuard {
+ let (non_blocking, guard) = tracing_appender::non_blocking(std::io::stdout());
+ let filter = tracing_subscriber::EnvFilter::builder()
+ .with_default_directive(tracing::metadata::LevelFilter::TRACE.into())
+ .from_env()
+ .unwrap();
+
+ tracing_subscriber::fmt()
+ .with_writer(non_blocking)
+ .with_env_filter(filter)
+ .with_target(false)
+ .with_file(false)
+ .with_thread_ids(true)
+ .with_span_events(tracing_subscriber::fmt::format::FmtSpan::ACTIVE)
+ .init();
+ guard
+}
+
+fn main() {
+ let _tracing_guard = init_tracing();
+ let path = include_bytes!("../../../fuzz/corpus/simpleicons.txt");
+ _ = svg_path_cst::svg_path_cst(path).unwrap();
+}
diff --git a/shunit2 b/shunit2
new file mode 100644
index 0000000..fd7dc2c
--- /dev/null
+++ b/shunit2
@@ -0,0 +1,1463 @@
+#! /bin/sh
+# vim:et:ft=sh:sts=2:sw=2
+#
+# shUnit2 -- Unit testing framework for Unix shell scripts.
+#
+# Copyright 2008-2021 Kate Ward. All Rights Reserved.
+# Released under the Apache 2.0 license.
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Author: kate.ward@forestent.com (Kate Ward)
+# https://github.com/kward/shunit2
+#
+# shUnit2 is a xUnit based unit test framework for Bourne shell scripts. It is
+# based on the popular JUnit unit testing framework for Java.
+#
+# `expr` may be antiquated, but it is the only solution in some cases.
+# shellcheck disable=SC2003
+# Allow usage of legacy backticked `...` notation instead of $(...).
+# shellcheck disable=SC2006
+
+# Return if shunit2 already loaded.
+if test -n "${SHUNIT_VERSION:-}"; then
+ exit 0
+fi
+SHUNIT_VERSION='2.1.9pre'
+
+# Return values that scripts can use.
+SHUNIT_TRUE=0
+SHUNIT_FALSE=1
+SHUNIT_ERROR=2
+
+# Determine if `builtin` command exists.
+__SHUNIT_BUILTIN='builtin'
+# shellcheck disable=2039
+if ! ("${__SHUNIT_BUILTIN}" echo 123 >/dev/null 2>&1); then
+ __SHUNIT_BUILTIN=''
+fi
+
+# Determine some reasonable command defaults.
+__SHUNIT_CMD_ECHO_ESC='echo -e'
+# shellcheck disable=SC2039,SC3037
+if ${__SHUNIT_BUILTIN} [ "`echo -e test`" = '-e test' ]; then
+ __SHUNIT_CMD_ECHO_ESC='echo'
+fi
+
+# Commands a user can override if needed.
+__SHUNIT_CMD_TPUT='tput'
+SHUNIT_CMD_TPUT=${SHUNIT_CMD_TPUT:-${__SHUNIT_CMD_TPUT}}
+
+# Enable color output. Options are 'auto', 'always', or 'never'.
+SHUNIT_COLOR=${SHUNIT_COLOR:-auto}
+
+#
+# Internal constants.
+#
+
+__SHUNIT_MODE_SOURCED='sourced'
+__SHUNIT_MODE_STANDALONE='standalone'
+__SHUNIT_PARENT=${SHUNIT_PARENT:-$0}
+
+# User provided test prefix to display in front of the name of the test being
+# executed. Define by setting the SHUNIT_TEST_PREFIX variable.
+__SHUNIT_TEST_PREFIX=${SHUNIT_TEST_PREFIX:-}
+
+# ANSI colors.
+__SHUNIT_ANSI_NONE='\033[0m'
+__SHUNIT_ANSI_RED='\033[1;31m'
+__SHUNIT_ANSI_GREEN='\033[1;32m'
+__SHUNIT_ANSI_YELLOW='\033[1;33m'
+__SHUNIT_ANSI_CYAN='\033[1;36m'
+
+#
+# Internal variables.
+#
+
+# Variables.
+__shunit_lineno='' # Line number of executed test.
+__shunit_mode=${__SHUNIT_MODE_SOURCED} # Operating mode.
+__shunit_reportGenerated=${SHUNIT_FALSE} # Is report generated.
+__shunit_script='' # Filename of unittest script (standalone mode).
+__shunit_skip=${SHUNIT_FALSE} # Is skipping enabled.
+__shunit_suite='' # Suite of tests to execute.
+__shunit_clean=${SHUNIT_FALSE} # _shunit_cleanup() was already called.
+__shunit_suiteName='' # Text name of current test suite.
+__shunit_xmlSuiteName='' # XML-ready text name of current test suite.
+
+# JUnit XML variables.
+__shunit_junitXmlOutputFile='' # File to use for JUnit XML output in addition to stdout.
+__shunit_junitXmlTestCases='' # Test cases info in the JUnit XML format for output
+__shunit_junitXmlCurrentTestCaseErrors='' # Current test case error info in the JUnit XML format for output
+
+# ANSI colors (populated by _shunit_configureColor()).
+__shunit_ansi_none=''
+__shunit_ansi_red=''
+__shunit_ansi_green=''
+__shunit_ansi_yellow=''
+__shunit_ansi_cyan=''
+
+# Counts of tests.
+__shunit_testSuccess=${SHUNIT_TRUE}
+__shunit_testsTotal=0
+__shunit_testsPassed=0
+__shunit_testsFailed=0
+
+# Counts of asserts.
+__shunit_assertsTotal=0
+__shunit_assertsPassed=0
+__shunit_assertsFailed=0
+__shunit_assertsSkipped=0
+__shunit_assertsCurrentTest=0
+
+#
+# Internal functions.
+#
+
+# Logging.
+_shunit_warn() {
+ ${__SHUNIT_CMD_ECHO_ESC} "${__shunit_ansi_yellow}shunit2:WARN${__shunit_ansi_none} $*" >&2
+}
+_shunit_error() {
+ ${__SHUNIT_CMD_ECHO_ESC} "${__shunit_ansi_red}shunit2:ERROR${__shunit_ansi_none} $*" >&2
+}
+_shunit_fatal() {
+ ${__SHUNIT_CMD_ECHO_ESC} "${__shunit_ansi_red}shunit2:FATAL${__shunit_ansi_none} $*" >&2
+ exit ${SHUNIT_ERROR}
+}
+
+#
+# Macros.
+#
+
+# shellcheck disable=SC2016,SC2089
+_SHUNIT_LINENO_='eval __shunit_lineno=""; if ${__SHUNIT_BUILTIN} [ "${1:-}" = "--lineno" ] && ${__SHUNIT_BUILTIN} [ -n "${2:-}" ]; then __shunit_lineno="[${2}]"; shift 2; fi;'
+
+#
+# Setup.
+#
+
+# Specific shell checks.
+if ${__SHUNIT_BUILTIN} [ -n "${ZSH_VERSION:-}" ]; then
+ setopt |grep "^shwordsplit$" >/dev/null
+ if ${__SHUNIT_BUILTIN} [ $? -ne ${SHUNIT_TRUE} ]; then
+ _shunit_fatal 'zsh shwordsplit option is required for proper operation'
+ fi
+ if ${__SHUNIT_BUILTIN} [ -z "${SHUNIT_PARENT:-}" ]; then
+ _shunit_fatal "zsh does not pass \$0 through properly. please declare \
+\"SHUNIT_PARENT=\$0\" before calling shUnit2"
+ fi
+fi
+
+# Set the constants readonly.
+__shunit_constants=`set |grep '^__SHUNIT_' |cut -d= -f1`
+echo "${__shunit_constants}" |grep '^Binary file' >/dev/null && \
+ __shunit_constants=`set |grep -a '^__SHUNIT_' |cut -d= -f1`
+for __shunit_const in ${__shunit_constants}; do
+ if ${__SHUNIT_BUILTIN} [ -z "${ZSH_VERSION:-}" ]; then
+ readonly "${__shunit_const}"
+ else
+ case ${ZSH_VERSION} in
+ [123].*) readonly "${__shunit_const}" ;;
+ *)
+ # Declare readonly constants globally.
+ # shellcheck disable=SC2039,SC3045
+ readonly -g "${__shunit_const}"
+ esac
+ fi
+done
+unset __shunit_const __shunit_constants
+
+#-----------------------------------------------------------------------------
+# Assertion functions.
+#
+
+# Assert that two values are equal to one another.
+#
+# Args:
+# message: string: failure message [optional]
+# expected: string: expected value
+# actual: string: actual value
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+assertEquals() {
+ # shellcheck disable=SC2090
+ ${_SHUNIT_LINENO_}
+ if ${__SHUNIT_BUILTIN} [ $# -lt 2 -o $# -gt 3 ]; then
+ _shunit_error "assertEquals() requires two or three arguments; $# given"
+ _shunit_assertFail
+ return ${SHUNIT_ERROR}
+ fi
+ if _shunit_shouldSkip; then
+ return ${SHUNIT_TRUE}
+ fi
+
+ shunit_message_=${__shunit_lineno}
+ if ${__SHUNIT_BUILTIN} [ $# -eq 3 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+ shunit_expected_=$1
+ shunit_actual_=$2
+
+ shunit_return=${SHUNIT_TRUE}
+ if ${__SHUNIT_BUILTIN} [ "${shunit_expected_}" = "${shunit_actual_}" ]; then
+ _shunit_assertPass
+ else
+ failNotEquals "${shunit_message_}" "${shunit_expected_}" "${shunit_actual_}"
+ shunit_return=${SHUNIT_FALSE}
+ fi
+
+ unset shunit_message_ shunit_expected_ shunit_actual_
+ return ${shunit_return}
+}
+# shellcheck disable=SC2016,SC2034
+_ASSERT_EQUALS_='eval assertEquals --lineno "${LINENO:-}"'
+
+# Assert that two values are not equal to one another.
+#
+# Args:
+# message: string: failure message [optional]
+# expected: string: expected value
+# actual: string: actual value
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+assertNotEquals() {
+ # shellcheck disable=SC2090
+ ${_SHUNIT_LINENO_}
+ if ${__SHUNIT_BUILTIN} [ $# -lt 2 -o $# -gt 3 ]; then
+ _shunit_error "assertNotEquals() requires two or three arguments; $# given"
+ _shunit_assertFail
+ return ${SHUNIT_ERROR}
+ fi
+ if _shunit_shouldSkip; then
+ return ${SHUNIT_TRUE}
+ fi
+
+ shunit_message_=${__shunit_lineno}
+ if ${__SHUNIT_BUILTIN} [ $# -eq 3 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+ shunit_expected_=$1
+ shunit_actual_=$2
+
+ shunit_return=${SHUNIT_TRUE}
+ if ${__SHUNIT_BUILTIN} [ "${shunit_expected_}" != "${shunit_actual_}" ]; then
+ _shunit_assertPass
+ else
+ failSame "${shunit_message_}" "${shunit_expected_}" "${shunit_actual_}"
+ shunit_return=${SHUNIT_FALSE}
+ fi
+
+ unset shunit_message_ shunit_expected_ shunit_actual_
+ return ${shunit_return}
+}
+# shellcheck disable=SC2016,SC2034
+_ASSERT_NOT_EQUALS_='eval assertNotEquals --lineno "${LINENO:-}"'
+
+# Assert that a container contains a content.
+#
+# Args:
+# message: string: failure message [optional]
+# container: string: container to analyze
+# content: string: content to find
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+assertContains() {
+ # shellcheck disable=SC2090
+ ${_SHUNIT_LINENO_}
+ if ${__SHUNIT_BUILTIN} [ $# -lt 2 -o $# -gt 3 ]; then
+ _shunit_error "assertContains() requires two or three arguments; $# given"
+ _shunit_assertFail
+ return ${SHUNIT_ERROR}
+ fi
+ if _shunit_shouldSkip; then
+ return ${SHUNIT_TRUE}
+ fi
+
+ shunit_message_=${__shunit_lineno}
+ if ${__SHUNIT_BUILTIN} [ $# -eq 3 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+ shunit_container_=$1
+ shunit_content_=$2
+ shunit_return=${SHUNIT_TRUE}
+ if echo "${shunit_container_}" |grep -F -- "${shunit_content_}" >/dev/null; then
+ _shunit_assertPass
+ else
+ failNotFound "${shunit_message_}" "${shunit_content_}"
+ shunit_return=${SHUNIT_FALSE}
+ fi
+
+ unset shunit_message_ shunit_container_ shunit_content_
+ return ${shunit_return}
+}
+# shellcheck disable=SC2016,SC2034
+_ASSERT_CONTAINS_='eval assertContains --lineno "${LINENO:-}"'
+
+# Assert that a container does not contain a content.
+#
+# Args:
+# message: string: failure message [optional]
+# container: string: container to analyze
+# content: string: content to look for
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+assertNotContains() {
+ # shellcheck disable=SC2090
+ ${_SHUNIT_LINENO_}
+ if ${__SHUNIT_BUILTIN} [ $# -lt 2 -o $# -gt 3 ]; then
+ _shunit_error "assertNotContains() requires two or three arguments; $# given"
+ _shunit_assertFail
+ return ${SHUNIT_ERROR}
+ fi
+ if _shunit_shouldSkip; then
+ return ${SHUNIT_TRUE}
+ fi
+
+ shunit_message_=${__shunit_lineno}
+ if ${__SHUNIT_BUILTIN} [ $# -eq 3 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+ shunit_container_=$1
+ shunit_content_=$2
+
+ shunit_return=${SHUNIT_TRUE}
+ if echo "$shunit_container_" |grep -F -- "$shunit_content_" > /dev/null; then
+ failFound "${shunit_message_}" "${shunit_content_}"
+ shunit_return=${SHUNIT_FALSE}
+ else
+ _shunit_assertPass
+ fi
+
+ unset shunit_message_ shunit_container_ shunit_content_
+ return ${shunit_return}
+}
+# shellcheck disable=SC2016,SC2034
+_ASSERT_NOT_CONTAINS_='eval assertNotContains --lineno "${LINENO:-}"'
+
+# Assert that a value is null (i.e. an empty string).
+#
+# Args:
+# message: string: failure message [optional]
+# actual: string: actual value
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+assertNull() {
+ # shellcheck disable=SC2090
+ ${_SHUNIT_LINENO_}
+ if ${__SHUNIT_BUILTIN} [ $# -gt 2 ]; then
+ # Allowing 0 arguments as $1 might actually be null.
+ _shunit_error "assertNull() requires one or two arguments; $# given"
+ _shunit_assertFail
+ return ${SHUNIT_ERROR}
+ fi
+ if _shunit_shouldSkip; then
+ return ${SHUNIT_TRUE}
+ fi
+
+ shunit_message_=${__shunit_lineno}
+ if ${__SHUNIT_BUILTIN} [ $# -eq 2 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+
+ ${__SHUNIT_BUILTIN} test -z "${1:-}"
+ assertTrue "${shunit_message_}" $?
+ shunit_return=$?
+
+ unset shunit_message_
+ return ${shunit_return}
+}
+# shellcheck disable=SC2016,SC2034
+_ASSERT_NULL_='eval assertNull --lineno "${LINENO:-}"'
+
+# Assert that a value is not null (i.e. a non-empty string).
+#
+# Args:
+# message: string: failure message [optional]
+# actual: string: actual value
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+assertNotNull() {
+ # shellcheck disable=SC2090
+ ${_SHUNIT_LINENO_}
+ if ${__SHUNIT_BUILTIN} [ $# -gt 2 ]; then
+ # Allowing 0 arguments as $1 might actually be null.
+ _shunit_error "assertNotNull() requires one or two arguments; $# given"
+ _shunit_assertFail
+ return ${SHUNIT_ERROR}
+ fi
+ if _shunit_shouldSkip; then
+ return ${SHUNIT_TRUE}
+ fi
+
+ shunit_message_=${__shunit_lineno}
+ if ${__SHUNIT_BUILTIN} [ $# -eq 2 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+
+ ${__SHUNIT_BUILTIN} test -n "${1:-}"
+ assertTrue "${shunit_message_}" $?
+ shunit_return=$?
+
+ unset shunit_message_
+ return ${shunit_return}
+}
+# shellcheck disable=SC2016,SC2034
+_ASSERT_NOT_NULL_='eval assertNotNull --lineno "${LINENO:-}"'
+
+# Assert that two values are the same (i.e. equal to one another).
+#
+# Args:
+# message: string: failure message [optional]
+# expected: string: expected value
+# actual: string: actual value
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+assertSame() {
+ # shellcheck disable=SC2090
+ ${_SHUNIT_LINENO_}
+ if ${__SHUNIT_BUILTIN} [ $# -lt 2 -o $# -gt 3 ]; then
+ _shunit_error "assertSame() requires two or three arguments; $# given"
+ _shunit_assertFail
+ return ${SHUNIT_ERROR}
+ fi
+ if _shunit_shouldSkip; then
+ return ${SHUNIT_TRUE}
+ fi
+
+ shunit_message_=${__shunit_lineno}
+ if ${__SHUNIT_BUILTIN} [ $# -eq 3 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+ assertEquals "${shunit_message_}" "$1" "$2"
+ shunit_return=$?
+
+ unset shunit_message_
+ return ${shunit_return}
+}
+# shellcheck disable=SC2016,SC2034
+_ASSERT_SAME_='eval assertSame --lineno "${LINENO:-}"'
+
+# Assert that two values are not the same (i.e. not equal to one another).
+#
+# Args:
+# message: string: failure message [optional]
+# expected: string: expected value
+# actual: string: actual value
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+assertNotSame() {
+ # shellcheck disable=SC2090
+ ${_SHUNIT_LINENO_}
+ if ${__SHUNIT_BUILTIN} [ $# -lt 2 -o $# -gt 3 ]; then
+ _shunit_error "assertNotSame() requires two or three arguments; $# given"
+ _shunit_assertFail
+ return ${SHUNIT_ERROR}
+ fi
+ if _shunit_shouldSkip; then
+ return ${SHUNIT_TRUE}
+ fi
+
+ shunit_message_=${__shunit_lineno}
+ if ${__SHUNIT_BUILTIN} [ $# -eq 3 ]; then
+ shunit_message_="${shunit_message_:-}$1"
+ shift
+ fi
+ assertNotEquals "${shunit_message_}" "$1" "$2"
+ shunit_return=$?
+
+ unset shunit_message_
+ return ${shunit_return}
+}
+# shellcheck disable=SC2016,SC2034
+_ASSERT_NOT_SAME_='eval assertNotSame --lineno "${LINENO:-}"'
+
+# Assert that a value or shell test condition is true.
+#
+# In shell, a value of 0 is true and a non-zero value is false. Any integer
+# value passed can thereby be tested.
+#
+# Shell supports much more complicated tests though, and a means to support
+# them was needed. As such, this function tests that conditions are true or
+# false through evaluation rather than just looking for a true or false.
+#
+# The following test will succeed:
+# assertTrue 0
+# assertTrue "[ 34 -gt 23 ]"
+# The following test will fail with a message:
+# assertTrue 123
+# assertTrue "test failed" "[ -r '/non/existent/file' ]"
+#
+# Args:
+# message: string: failure message [optional]
+# condition: string: integer value or shell conditional statement
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+assertTrue() {
+ # shellcheck disable=SC2090
+ ${_SHUNIT_LINENO_}
+ if ${__SHUNIT_BUILTIN} [ $# -lt 1 -o $# -gt 2 ]; then
+ _shunit_error "assertTrue() takes one or two arguments; $# given"
+ _shunit_assertFail
+ return ${SHUNIT_ERROR}
+ fi
+ if _shunit_shouldSkip; then
+ return ${SHUNIT_TRUE}
+ fi
+
+ shunit_message_=${__shunit_lineno}
+ if ${__SHUNIT_BUILTIN} [ $# -eq 2 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+ shunit_condition_=$1
+
+ # See if condition is an integer, i.e. a return value.
+ shunit_return=${SHUNIT_TRUE}
+ if ${__SHUNIT_BUILTIN} [ -z "${shunit_condition_}" ]; then
+ # Null condition.
+ shunit_return=${SHUNIT_FALSE}
+ elif (expr \( "${shunit_condition_}" + '0' \) '=' "${shunit_condition_}" >/dev/null 2>&1)
+ then
+ # Possible return value. Treating 0 as true, and non-zero as false.
+ if ${__SHUNIT_BUILTIN} [ "${shunit_condition_}" -ne 0 ]; then
+ shunit_return=${SHUNIT_FALSE}
+ fi
+ else
+ # Hopefully... a condition.
+ if ! eval "${shunit_condition_}" >/dev/null 2>&1; then
+ shunit_return=${SHUNIT_FALSE}
+ fi
+ fi
+
+ # Record the test.
+ if ${__SHUNIT_BUILTIN} [ ${shunit_return} -eq ${SHUNIT_TRUE} ]; then
+ _shunit_assertPass
+ else
+ _shunit_assertFail "${shunit_message_}"
+ fi
+
+ unset shunit_message_ shunit_condition_
+ return ${shunit_return}
+}
+# shellcheck disable=SC2016,SC2034
+_ASSERT_TRUE_='eval assertTrue --lineno "${LINENO:-}"'
+
+# Assert that a value or shell test condition is false.
+#
+# In shell, a value of 0 is true and a non-zero value is false. Any integer
+# value passed can thereby be tested.
+#
+# Shell supports much more complicated tests though, and a means to support
+# them was needed. As such, this function tests that conditions are true or
+# false through evaluation rather than just looking for a true or false.
+#
+# The following test will succeed:
+# assertFalse 1
+# assertFalse "[ 'apples' = 'oranges' ]"
+# The following test will fail with a message:
+# assertFalse 0
+# assertFalse "test failed" "[ 1 -eq 1 -a 2 -eq 2 ]"
+#
+# Args:
+# message: string: failure message [optional]
+# condition: string: integer value or shell conditional statement
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+assertFalse() {
+ # shellcheck disable=SC2090
+ ${_SHUNIT_LINENO_}
+ if ${__SHUNIT_BUILTIN} [ $# -lt 1 -o $# -gt 2 ]; then
+ _shunit_error "assertFalse() requires one or two arguments; $# given"
+ _shunit_assertFail
+ return ${SHUNIT_ERROR}
+ fi
+ if _shunit_shouldSkip; then
+ return ${SHUNIT_TRUE}
+ fi
+
+ shunit_message_=${__shunit_lineno}
+ if ${__SHUNIT_BUILTIN} [ $# -eq 2 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+ shunit_condition_=$1
+
+ # See if condition is an integer, i.e. a return value.
+ shunit_return=${SHUNIT_TRUE}
+ if ${__SHUNIT_BUILTIN} [ -z "${shunit_condition_}" ]; then
+ # Null condition.
+ shunit_return=${SHUNIT_TRUE}
+ elif (expr \( "${shunit_condition_}" + '0' \) '=' "${shunit_condition_}" >/dev/null 2>&1); then
+ # Possible return value. Treating 0 as true, and non-zero as false.
+ if ${__SHUNIT_BUILTIN} [ "${shunit_condition_}" -eq 0 ]; then
+ shunit_return=${SHUNIT_FALSE}
+ fi
+ else
+ # Hopefully... a condition.
+ # shellcheck disable=SC2086
+ if eval ${shunit_condition_} >/dev/null 2>&1; then
+ shunit_return=${SHUNIT_FALSE}
+ fi
+ fi
+
+ # Record the test.
+ if ${__SHUNIT_BUILTIN} [ "${shunit_return}" -eq "${SHUNIT_TRUE}" ]; then
+ _shunit_assertPass
+ else
+ _shunit_assertFail "${shunit_message_}"
+ fi
+
+ unset shunit_message_ shunit_condition_
+ return "${shunit_return}"
+}
+# shellcheck disable=SC2016,SC2034
+_ASSERT_FALSE_='eval assertFalse --lineno "${LINENO:-}"'
+
+#-----------------------------------------------------------------------------
+# Failure functions.
+#
+
+# Records a test failure.
+#
+# Args:
+# message: string: failure message [optional]
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+fail() {
+ # shellcheck disable=SC2090
+ ${_SHUNIT_LINENO_}
+ if ${__SHUNIT_BUILTIN} [ $# -gt 1 ]; then
+ _shunit_error "fail() requires zero or one arguments; $# given"
+ return ${SHUNIT_ERROR}
+ fi
+ if _shunit_shouldSkip; then
+ return ${SHUNIT_TRUE}
+ fi
+
+ shunit_message_=${__shunit_lineno}
+ if ${__SHUNIT_BUILTIN} [ $# -eq 1 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+
+ _shunit_assertFail "${shunit_message_}"
+
+ unset shunit_message_
+ return ${SHUNIT_FALSE}
+}
+# shellcheck disable=SC2016,SC2034
+_FAIL_='eval fail --lineno "${LINENO:-}"'
+
+# Records a test failure, stating two values were not equal.
+#
+# Args:
+# message: string: failure message [optional]
+# expected: string: expected value
+# actual: string: actual value
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+failNotEquals() {
+ # shellcheck disable=SC2090
+ ${_SHUNIT_LINENO_}
+ if ${__SHUNIT_BUILTIN} [ $# -lt 2 -o $# -gt 3 ]; then
+ _shunit_error "failNotEquals() requires one or two arguments; $# given"
+ return ${SHUNIT_ERROR}
+ fi
+ if _shunit_shouldSkip; then
+ return ${SHUNIT_TRUE}
+ fi
+
+ shunit_message_=${__shunit_lineno}
+ if ${__SHUNIT_BUILTIN} [ $# -eq 3 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+ shunit_expected_=$1
+ shunit_actual_=$2
+
+ shunit_message_=${shunit_message_%% }
+ _shunit_assertFail "${shunit_message_:+${shunit_message_} }expected:<${shunit_expected_}> but was:<${shunit_actual_}>"
+
+ unset shunit_message_ shunit_expected_ shunit_actual_
+ return ${SHUNIT_FALSE}
+}
+# shellcheck disable=SC2016,SC2034
+_FAIL_NOT_EQUALS_='eval failNotEquals --lineno "${LINENO:-}"'
+
+# Records a test failure, stating a value was found.
+#
+# Args:
+# message: string: failure message [optional]
+# content: string: found value
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+failFound() {
+ # shellcheck disable=SC2090
+ ${_SHUNIT_LINENO_}
+ if ${__SHUNIT_BUILTIN} [ $# -lt 1 -o $# -gt 2 ]; then
+ _shunit_error "failFound() requires one or two arguments; $# given"
+ return ${SHUNIT_ERROR}
+ fi
+ if _shunit_shouldSkip; then
+ return ${SHUNIT_TRUE}
+ fi
+
+ shunit_message_=${__shunit_lineno}
+ if ${__SHUNIT_BUILTIN} [ $# -eq 2 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+ shunit_content_=$1
+
+ shunit_message_=${shunit_message_%% }
+ _shunit_assertFail "${shunit_message_:+${shunit_message_} }found:<${shunit_content_}>"
+
+ unset shunit_message_ shunit_content_
+ return ${SHUNIT_FALSE}
+}
+# shellcheck disable=SC2016,SC2034
+_FAIL_FOUND_='eval failFound --lineno "${LINENO:-}"'
+
+# Records a test failure, stating a content was not found.
+#
+# Args:
+# message: string: failure message [optional]
+# content: string: content not found
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+failNotFound() {
+ # shellcheck disable=SC2090
+ ${_SHUNIT_LINENO_}
+ if ${__SHUNIT_BUILTIN} [ $# -lt 1 -o $# -gt 2 ]; then
+ _shunit_error "failNotFound() requires one or two arguments; $# given"
+ return ${SHUNIT_ERROR}
+ fi
+ if _shunit_shouldSkip; then
+ return ${SHUNIT_TRUE}
+ fi
+
+ shunit_message_=${__shunit_lineno}
+ if ${__SHUNIT_BUILTIN} [ $# -eq 2 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+ shunit_content_=$1
+
+ shunit_message_=${shunit_message_%% }
+ _shunit_assertFail "${shunit_message_:+${shunit_message_} }not found:<${shunit_content_}>"
+
+ unset shunit_message_ shunit_content_
+ return ${SHUNIT_FALSE}
+}
+# shellcheck disable=SC2016,SC2034
+_FAIL_NOT_FOUND_='eval failNotFound --lineno "${LINENO:-}"'
+
+# Records a test failure, stating two values should have been the same.
+#
+# Args:
+# message: string: failure message [optional]
+# expected: string: expected value
+# actual: string: actual value
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+failSame() {
+ # shellcheck disable=SC2090
+ ${_SHUNIT_LINENO_}
+ if ${__SHUNIT_BUILTIN} [ $# -lt 2 -o $# -gt 3 ]; then
+ _shunit_error "failSame() requires two or three arguments; $# given"
+ return ${SHUNIT_ERROR}
+ fi
+ if _shunit_shouldSkip; then
+ return ${SHUNIT_TRUE}
+ fi
+
+ shunit_message_=${__shunit_lineno}
+ if ${__SHUNIT_BUILTIN} [ $# -eq 3 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+
+ shunit_message_=${shunit_message_%% }
+ _shunit_assertFail "${shunit_message_:+${shunit_message_} }expected not same"
+
+ unset shunit_message_
+ return ${SHUNIT_FALSE}
+}
+# shellcheck disable=SC2016,SC2034
+_FAIL_SAME_='eval failSame --lineno "${LINENO:-}"'
+
+# Records a test failure, stating two values were not equal.
+#
+# This is functionally equivalent to calling failNotEquals().
+#
+# Args:
+# message: string: failure message [optional]
+# expected: string: expected value
+# actual: string: actual value
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+failNotSame() {
+ # shellcheck disable=SC2090
+ ${_SHUNIT_LINENO_}
+ if ${__SHUNIT_BUILTIN} [ $# -lt 2 -o $# -gt 3 ]; then
+ _shunit_error "failNotSame() requires one or two arguments; $# given"
+ return ${SHUNIT_ERROR}
+ fi
+ if _shunit_shouldSkip; then
+ return ${SHUNIT_TRUE}
+ fi
+
+ shunit_message_=${__shunit_lineno}
+ if ${__SHUNIT_BUILTIN} [ $# -eq 3 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+ failNotEquals "${shunit_message_}" "$1" "$2"
+ shunit_return=$?
+
+ unset shunit_message_
+ return ${shunit_return}
+}
+# shellcheck disable=SC2016,SC2034
+_FAIL_NOT_SAME_='eval failNotSame --lineno "${LINENO:-}"'
+
+#-----------------------------------------------------------------------------
+# Skipping functions.
+#
+
+# Force remaining assert and fail functions to be "skipped".
+#
+# This function forces the remaining assert and fail functions to be "skipped",
+# i.e. they will have no effect. Each function skipped will be recorded so that
+# the total of asserts and fails will not be altered.
+#
+# Args:
+# message: string: message to provide to user [optional]
+startSkipping() {
+ if ${__SHUNIT_BUILTIN} [ $# -gt 0 ]; then _shunit_warn "[skipping] $*"; fi
+ __shunit_skip=${SHUNIT_TRUE}
+}
+
+# Resume the normal recording behavior of assert and fail calls.
+#
+# Args:
+# None
+endSkipping() { __shunit_skip=${SHUNIT_FALSE}; }
+
+# Returns the state of assert and fail call skipping.
+#
+# Args:
+# None
+# Returns:
+# boolean: (TRUE/FALSE constant)
+isSkipping() { return ${__shunit_skip}; }
+
+#-----------------------------------------------------------------------------
+# Suite functions.
+#
+
+# Stub. This function should contains all unit test calls to be made.
+#
+# DEPRECATED (as of 2.1.0)
+#
+# This function can be optionally overridden by the user in their test suite.
+#
+# If this function exists, it will be called when shunit2 is sourced. If it
+# does not exist, shunit2 will search the parent script for all functions
+# beginning with the word 'test', and they will be added dynamically to the
+# test suite.
+#
+# This function should be overridden by the user in their unit test suite.
+# Note: see _shunit_mktempFunc() for actual implementation
+#
+# Args:
+# None
+#suite() { :; } # DO NOT UNCOMMENT THIS FUNCTION
+
+# Adds a function name to the list of tests schedule for execution.
+#
+# This function should only be called from within the suite() function.
+#
+# Args:
+# function: string: name of a function to add to current unit test suite
+suite_addTest() {
+ shunit_func_=${1:-}
+
+ __shunit_suite="${__shunit_suite:+${__shunit_suite} }${shunit_func_}"
+ __shunit_testsTotal=`expr "${__shunit_testsTotal}" + 1`
+
+ unset shunit_func_
+}
+
+# Stub. This function will be called once before any tests are run.
+#
+# Common one-time environment preparation tasks shared by all tests can be
+# defined here.
+#
+# This function should be overridden by the user in their unit test suite.
+# Note: see _shunit_mktempFunc() for actual implementation
+#
+# Args:
+# None
+#oneTimeSetUp() { :; } # DO NOT UNCOMMENT THIS FUNCTION
+
+# Stub. This function will be called once after all tests are finished.
+#
+# Common one-time environment cleanup tasks shared by all tests can be defined
+# here.
+#
+# This function should be overridden by the user in their unit test suite.
+# Note: see _shunit_mktempFunc() for actual implementation
+#
+# Args:
+# None
+#oneTimeTearDown() { :; } # DO NOT UNCOMMENT THIS FUNCTION
+
+# Stub. This function will be called before each test is run.
+#
+# Common environment preparation tasks shared by all tests can be defined here.
+#
+# This function should be overridden by the user in their unit test suite.
+# Note: see _shunit_mktempFunc() for actual implementation
+#
+# Args:
+# None
+#setUp() { :; } # DO NOT UNCOMMENT THIS FUNCTION
+
+# Note: see _shunit_mktempFunc() for actual implementation
+# Stub. This function will be called after each test is run.
+#
+# Common environment cleanup tasks shared by all tests can be defined here.
+#
+# This function should be overridden by the user in their unit test suite.
+# Note: see _shunit_mktempFunc() for actual implementation
+#
+# Args:
+# None
+#tearDown() { :; } # DO NOT UNCOMMENT THIS FUNCTION
+
+#------------------------------------------------------------------------------
+# Internal shUnit2 functions.
+#
+
+# Create a temporary directory to store various run-time files in.
+#
+# This function is a cross-platform temporary directory creation tool. Not all
+# OSes have the `mktemp` function, so one is included here.
+#
+# Args:
+# None
+# Outputs:
+# string: the temporary directory that was created
+_shunit_mktempDir() {
+ # Try the standard `mktemp` function.
+ if ( exec mktemp -dqt shunit.XXXXXX 2>/dev/null ); then
+ return
+ fi
+
+ # The standard `mktemp` didn't work. Use our own.
+ # shellcheck disable=SC2039,SC3028
+ if ${__SHUNIT_BUILTIN} [ -r '/dev/urandom' -a -x '/usr/bin/od' ]; then
+ _shunit_random_=`/usr/bin/od -vAn -N4 -tx4 "${_shunit_file_}"
+#! /bin/sh
+exit ${SHUNIT_TRUE}
+EOF
+ command chmod +x "${_shunit_file_}"
+ done
+
+ unset _shunit_file_
+}
+
+# Final cleanup function to leave things as we found them.
+#
+# Besides removing the temporary directory, this function is in charge of the
+# final exit code of the unit test. The exit code is based on how the script
+# was ended (e.g. normal exit, or via Ctrl-C).
+#
+# Args:
+# name: string: name of the trap called (specified when trap defined)
+_shunit_cleanup() {
+ _shunit_name_=$1
+
+ _shunit_signal_=0
+ case "${_shunit_name_}" in
+ EXIT) ;;
+ INT) _shunit_signal_=130 ;; # 2+128
+ TERM) _shunit_signal_=143 ;; # 15+128
+ *)
+ _shunit_error "unrecognized trap value (${_shunit_name_})"
+ ;;
+ esac
+ if ${__SHUNIT_BUILTIN} [ "${_shunit_name_}" != 'EXIT' ]; then
+ _shunit_warn "trapped and now handling the (${_shunit_name_}) signal"
+ fi
+
+ # Do our work.
+ if ${__SHUNIT_BUILTIN} [ ${__shunit_clean} -eq ${SHUNIT_FALSE} ]; then
+ # Ensure tear downs are only called once.
+ __shunit_clean=${SHUNIT_TRUE}
+
+ tearDown || _shunit_warn 'tearDown() returned non-zero return code.'
+ oneTimeTearDown || \
+ _shunit_warn 'oneTimeTearDown() returned non-zero return code.'
+
+ command rm -fr "${__shunit_tmpDir}"
+ fi
+
+ if ${__SHUNIT_BUILTIN} [ "${_shunit_name_}" != 'EXIT' ]; then
+ # Handle all non-EXIT signals.
+ trap - 0 # Disable EXIT trap.
+ exit ${_shunit_signal_}
+ elif ${__SHUNIT_BUILTIN} [ ${__shunit_reportGenerated} -eq ${SHUNIT_FALSE} ]; then
+ _shunit_assertFail 'unknown failure encountered running a test'
+ _shunit_generateReport
+ exit ${SHUNIT_ERROR}
+ fi
+
+ unset _shunit_name_ _shunit_signal_
+}
+
+# configureColor based on user color preference.
+#
+# Args:
+# color: string: color mode (one of `always`, `auto`, or `never`).
+_shunit_configureColor() {
+ _shunit_color_=${SHUNIT_FALSE} # By default, no color.
+ case $1 in
+ 'always') _shunit_color_=${SHUNIT_TRUE} ;;
+ 'auto')
+ if ${__SHUNIT_BUILTIN} [ "`_shunit_colors`" -ge 8 ]; then
+ _shunit_color_=${SHUNIT_TRUE}
+ fi
+ ;;
+ 'never'|'none') ;; # Support 'none' to support legacy usage.
+ *) _shunit_fatal "unrecognized color option '$1'" ;;
+ esac
+
+ # shellcheck disable=SC2254
+ case ${_shunit_color_} in
+ ${SHUNIT_TRUE})
+ __shunit_ansi_none=${__SHUNIT_ANSI_NONE}
+ __shunit_ansi_red=${__SHUNIT_ANSI_RED}
+ __shunit_ansi_green=${__SHUNIT_ANSI_GREEN}
+ __shunit_ansi_yellow=${__SHUNIT_ANSI_YELLOW}
+ __shunit_ansi_cyan=${__SHUNIT_ANSI_CYAN}
+ ;;
+ ${SHUNIT_FALSE})
+ __shunit_ansi_none=''
+ __shunit_ansi_red=''
+ __shunit_ansi_green=''
+ __shunit_ansi_yellow=''
+ __shunit_ansi_cyan=''
+ ;;
+ esac
+
+ unset _shunit_color_ _shunit_tput_
+}
+
+# colors returns the number of supported colors for the TERM.
+_shunit_colors() {
+ if _shunit_tput_=`${SHUNIT_CMD_TPUT} colors 2>/dev/null`; then
+ echo "${_shunit_tput_}"
+ else
+ echo 16
+ fi
+ unset _shunit_tput_
+}
+
+# The actual running of the tests happens here.
+#
+# Args:
+# None
+_shunit_execSuite() {
+ for _shunit_test_ in ${__shunit_suite}; do
+ __shunit_testSuccess=${SHUNIT_TRUE}
+
+ # Reset per-test info
+ __shunit_assertsCurrentTest=0
+ __shunit_junitXmlCurrentTestCaseErrors=''
+
+ # Disable skipping.
+ endSkipping
+
+ # Execute the per-test setUp() function.
+ if ! setUp; then
+ _shunit_fatal "setUp() returned non-zero return code."
+ fi
+
+ # Execute the test.
+ echo "${__SHUNIT_TEST_PREFIX}${_shunit_test_}"
+ # shellcheck disable=SC2086
+ if ! eval ${_shunit_test_}; then
+ _shunit_error "${_shunit_test_}() returned non-zero return code."
+ __shunit_testSuccess=${SHUNIT_ERROR}
+ fi
+
+ # Execute the per-test tearDown() function.
+ if ! tearDown; then
+ _shunit_fatal "tearDown() returned non-zero return code."
+ fi
+
+ # Store current test case info in JUnit XML.
+ __shunit_junitXmlTestCases="${__shunit_junitXmlTestCases}
+ ${__shunit_junitXmlCurrentTestCaseErrors}
+ "
+
+ # Update stats.
+ if ${__SHUNIT_BUILTIN} [ ${__shunit_testSuccess} -eq ${SHUNIT_TRUE} ]; then
+ __shunit_testsPassed=`expr "${__shunit_testsPassed}" + 1`
+ else
+ __shunit_testsFailed=`expr "${__shunit_testsFailed}" + 1`
+ fi
+ done
+
+ unset _shunit_test_
+}
+
+# Generates the user friendly report with appropriate OK/FAILED message.
+#
+# Args:
+# None
+# Output:
+# string: the report of successful and failed tests, as well as totals.
+_shunit_generateReport() {
+ if ${__SHUNIT_BUILTIN} [ "${__shunit_reportGenerated}" -eq ${SHUNIT_TRUE} ]; then
+ return
+ fi
+
+ _shunit_ok_=${SHUNIT_TRUE}
+
+ # If no exit code was provided, determine an appropriate one.
+ if ${__SHUNIT_BUILTIN} [ "${__shunit_testsFailed}" -gt 0 -o ${__shunit_testSuccess} -eq ${SHUNIT_FALSE} ]; then
+ _shunit_ok_=${SHUNIT_FALSE}
+ fi
+
+ echo
+ _shunit_msg_="Ran ${__shunit_ansi_cyan}${__shunit_testsTotal}${__shunit_ansi_none}"
+ if ${__SHUNIT_BUILTIN} [ "${__shunit_testsTotal}" -eq 1 ]; then
+ ${__SHUNIT_CMD_ECHO_ESC} "${_shunit_msg_} test."
+ else
+ ${__SHUNIT_CMD_ECHO_ESC} "${_shunit_msg_} tests."
+ fi
+
+ if ${__SHUNIT_BUILTIN} [ -n "${__shunit_junitXmlOutputFile}" ]; then
+ echo "
+${__shunit_junitXmlTestCases}
+" > "${__shunit_junitXmlOutputFile}"
+ echo
+ echo "JUnit XML file ${__shunit_junitXmlOutputFile} was saved."
+ fi
+
+ if ${__SHUNIT_BUILTIN} [ ${_shunit_ok_} -eq ${SHUNIT_TRUE} ]; then
+ _shunit_msg_="${__shunit_ansi_green}OK${__shunit_ansi_none}"
+ if ${__SHUNIT_BUILTIN} [ "${__shunit_assertsSkipped}" -gt 0 ]; then
+ _shunit_msg_="${_shunit_msg_} (${__shunit_ansi_yellow}skipped=${__shunit_assertsSkipped}${__shunit_ansi_none})"
+ fi
+ else
+ _shunit_msg_="${__shunit_ansi_red}FAILED${__shunit_ansi_none}"
+ _shunit_msg_="${_shunit_msg_} (${__shunit_ansi_red}failures=${__shunit_assertsFailed}${__shunit_ansi_none}"
+ if ${__SHUNIT_BUILTIN} [ "${__shunit_assertsSkipped}" -gt 0 ]; then
+ _shunit_msg_="${_shunit_msg_},${__shunit_ansi_yellow}skipped=${__shunit_assertsSkipped}${__shunit_ansi_none}"
+ fi
+ _shunit_msg_="${_shunit_msg_})"
+ fi
+
+ echo
+ ${__SHUNIT_CMD_ECHO_ESC} "${_shunit_msg_}"
+ __shunit_reportGenerated=${SHUNIT_TRUE}
+
+ unset _shunit_msg_ _shunit_ok_
+}
+
+# Test for whether a function should be skipped.
+#
+# Args:
+# None
+# Returns:
+# boolean: whether the test should be skipped (TRUE/FALSE constant)
+_shunit_shouldSkip() {
+ if ${__SHUNIT_BUILTIN} test ${__shunit_skip} -eq ${SHUNIT_FALSE}; then
+ return ${SHUNIT_FALSE}
+ fi
+ _shunit_assertSkip
+}
+
+# Records a successful test.
+#
+# Args:
+# None
+_shunit_assertPass() {
+ __shunit_assertsPassed=`expr "${__shunit_assertsPassed}" + 1`
+ __shunit_assertsTotal=`expr "${__shunit_assertsTotal}" + 1`
+ __shunit_assertsCurrentTest=`expr "${__shunit_assertsCurrentTest}" + 1`
+}
+
+# Records a test failure.
+#
+# Args:
+# message: string: failure message to provide user
+_shunit_assertFail() {
+ __shunit_testSuccess=${SHUNIT_FALSE}
+ _shunit_incFailedCount
+
+ _shunit_xml_message_="`_shunit_escapeXmlData "$@"`"
+
+ __shunit_junitXmlCurrentTestCaseErrors="${__shunit_junitXmlCurrentTestCaseErrors}
+ "
+
+ if ${__SHUNIT_BUILTIN} [ $# -gt 0 ]; then
+ ${__SHUNIT_CMD_ECHO_ESC} "${__shunit_ansi_red}ASSERT:${__shunit_ansi_none}$*"
+ fi
+
+ unset _shunit_xml_message_
+}
+
+# Increment the count of failed asserts.
+#
+# Args:
+# none
+_shunit_incFailedCount() {
+ __shunit_assertsFailed=`expr "${__shunit_assertsFailed}" + 1`
+ __shunit_assertsTotal=`expr "${__shunit_assertsTotal}" + 1`
+ __shunit_assertsCurrentTest=`expr "${__shunit_assertsCurrentTest}" + 1`
+}
+
+# Records a skipped test.
+#
+# Args:
+# None
+_shunit_assertSkip() {
+ __shunit_assertsSkipped=`expr "${__shunit_assertsSkipped}" + 1`
+ __shunit_assertsTotal=`expr "${__shunit_assertsTotal}" + 1`
+ __shunit_assertsCurrentTest=`expr "${__shunit_assertsCurrentTest}" + 1`
+}
+
+# Dump the current test metrics.
+#
+# Args:
+# none
+_shunit_metrics() {
+ echo "< \
+total: ${__shunit_assertsTotal} \
+passed: ${__shunit_assertsPassed} \
+failed: ${__shunit_assertsFailed} \
+skipped: ${__shunit_assertsSkipped} \
+>"
+}
+
+# Prepare a script filename for sourcing.
+#
+# Args:
+# script: string: path to a script to source
+# Returns:
+# string: filename prefixed with ./ (if necessary)
+_shunit_prepForSourcing() {
+ _shunit_script_=$1
+ case "${_shunit_script_}" in
+ /*|./*) echo "${_shunit_script_}" ;;
+ *) echo "./${_shunit_script_}" ;;
+ esac
+ unset _shunit_script_
+}
+
+# Extract list of functions to run tests against.
+#
+# Args:
+# script: string: name of script to extract functions from
+# Returns:
+# string: of function names
+_shunit_extractTestFunctions() {
+ _shunit_script_=$1
+
+ # Extract the lines with test function names, strip of anything besides the
+ # function name, and output everything on a single line.
+ _shunit_regex_='^\s*((function test[A-Za-z0-9_-]*)|(test[A-Za-z0-9_-]* *\(\)))'
+ grep -E "${_shunit_regex_}" "${_shunit_script_}" \
+ |command sed 's/^[^A-Za-z0-9_-]*//;s/^function //;s/\([A-Za-z0-9_-]*\).*/\1/g' \
+ |xargs
+
+ unset _shunit_regex_ _shunit_script_
+}
+
+# Escape XML data.
+#
+# Args:
+# data: string: data to escape
+# Returns:
+# string: escaped data
+_shunit_escapeXmlData() {
+ # Required XML characters to escape are described here:
+ # http://www.w3.org/TR/REC-xml/#syntax
+ # https://www.liquid-technologies.com/Reference/Glossary/XML_EscapingData.html
+ echo "$*" \
+ |command sed 's/&/\&/g;s/\</g;s/>/\>/g;s/"/\"/g'";s/'/\'/g"
+}
+
+#------------------------------------------------------------------------------
+# Main.
+#
+
+# Determine the operating mode.
+if ${__SHUNIT_BUILTIN} [ $# -eq 0 -o "${1:-}" = '--' ]; then
+ __shunit_script=${__SHUNIT_PARENT}
+ __shunit_mode=${__SHUNIT_MODE_SOURCED}
+else
+ __shunit_script=$1
+ if ! ${__SHUNIT_BUILTIN} [ -r "${__shunit_script}" ]; then
+ _shunit_fatal "unable to read from ${__shunit_script}"
+ fi
+ __shunit_mode=${__SHUNIT_MODE_STANDALONE}
+fi
+
+# Create a temporary storage location.
+__shunit_tmpDir=`_shunit_mktempDir`
+
+# Provide a public temporary directory for unit test scripts.
+# TODO(kward): document this.
+SHUNIT_TMPDIR="${__shunit_tmpDir}/tmp"
+if ! command mkdir "${SHUNIT_TMPDIR}"; then
+ _shunit_fatal "error creating SHUNIT_TMPDIR '${SHUNIT_TMPDIR}'"
+fi
+
+# Configure traps to clean up after ourselves.
+trap '_shunit_cleanup EXIT' 0
+trap '_shunit_cleanup INT' 2
+trap '_shunit_cleanup TERM' 15
+
+# Create phantom functions to work around issues with Cygwin.
+_shunit_mktempFunc
+PATH="${__shunit_tmpDir}:${PATH}"
+
+# Make sure phantom functions are executable. This will bite if `/tmp` (or the
+# current `$TMPDIR`) points to a path on a partition that was mounted with the
+# 'noexec' option. The noexec command was created with `_shunit_mktempFunc()`.
+noexec 2>/dev/null || _shunit_fatal \
+ 'Please declare TMPDIR with path on partition with exec permission.'
+
+# We must manually source the tests in standalone mode.
+if ${__SHUNIT_BUILTIN} [ "${__shunit_mode}" = "${__SHUNIT_MODE_STANDALONE}" ]; then
+ # shellcheck disable=SC1090
+ ${__SHUNIT_BUILTIN} . "`_shunit_prepForSourcing \"${__shunit_script}\"`"
+fi
+
+# Configure default output coloring behavior.
+_shunit_configureColor "${SHUNIT_COLOR}"
+
+# Execute the oneTimeSetUp function (if it exists).
+if ! oneTimeSetUp; then
+ _shunit_fatal "oneTimeSetUp() returned non-zero return code."
+fi
+
+# Command line selected tests or suite selected tests
+if ${__SHUNIT_BUILTIN} [ "$#" -ge 2 ]; then
+ # Argument $1 is either the filename of tests or '--'; either way, skip it.
+ shift
+ # Remaining arguments ($2 .. $#) are assumed to be:
+ # - test function names.
+ # - configuration options, that is started with the `--` prefix.
+ # Interate through all remaining args in "$@" in a POSIX (likely portable) way.
+ # Helpful tip: https://unix.stackexchange.com/questions/314032/how-to-use-arguments-like-1-2-in-a-for-loop
+ for _shunit_arg_ do
+ case "${_shunit_arg_}" in
+ --output-junit-xml=*)
+ # It is a request for JUnit XML output.
+ __shunit_junitXmlOutputFile="${_shunit_arg_#--output-junit-xml=}"
+ ;;
+ --suite-name=*)
+ # It is a request for a custom suite name.
+ __shunit_suiteName="${_shunit_arg_#--suite-name=}"
+ ;;
+ --*)
+ _shunit_fatal "unrecognized option \"${_shunit_arg_}\""
+ ;;
+ *)
+ # It is the test name, process it in a usual way.
+ suite_addTest "${_shunit_arg_}"
+ ;;
+ esac
+ done
+ unset _shunit_arg_
+else
+ # Execute the suite function defined in the parent test script.
+ # DEPRECATED as of 2.1.0.
+ suite
+fi
+
+# If no tests or suite specified, dynamically build a list of functions.
+if ${__SHUNIT_BUILTIN} [ -z "${__shunit_suite}" ]; then
+ shunit_funcs_=`_shunit_extractTestFunctions "${__shunit_script}"`
+ for shunit_func_ in ${shunit_funcs_}; do
+ suite_addTest "${shunit_func_}"
+ done
+fi
+unset shunit_func_ shunit_funcs_
+
+# If suite name is not defined, dynamically generate it from the script name.
+if ${__SHUNIT_BUILTIN} [ -z "${__shunit_suiteName}" ]; then
+ __shunit_suiteName="${__shunit_script##*/}"
+fi
+
+# Prepare the suite name for XML output.
+__shunit_xmlSuiteName="`_shunit_escapeXmlData "${__shunit_suiteName}"`"
+
+# Execute the suite of unit tests.
+_shunit_execSuite
+
+# Execute the oneTimeTearDown function (if it exists).
+if ! oneTimeTearDown; then
+ _shunit_fatal "oneTimeTearDown() returned non-zero return code."
+fi
+
+# Generate a report summary.
+_shunit_generateReport
+
+# That's it folks.
+if ! ${__SHUNIT_BUILTIN} [ "${__shunit_testsFailed}" -eq 0 ]; then
+ return ${SHUNIT_FALSE}
+fi
diff --git a/src/errors.rs b/src/errors.rs
index 9198b43..9b3ae24 100644
--- a/src/errors.rs
+++ b/src/errors.rs
@@ -8,11 +8,11 @@ use snafu::prelude::*;
pub enum SyntaxError {
/// The first command in a path is not moveto.
#[snafu(display(
- "Invalid SVG path command '{command}' at index {index}, expected 'M' or 'm'"
+ "Invalid SVG path command '{character}' at index {index}, expected 'M' or 'm'"
))]
ExpectedMovetoCommand {
/// Command letter found
- command: char,
+ character: char,
/// Index of the command in the path
index: usize,
},
@@ -38,7 +38,7 @@ pub enum SyntaxError {
/// Index of the character in the path
index: usize,
/// Expected character
- expected: String,
+ expected: &'static str,
},
/// Invalid path ending.
@@ -47,7 +47,7 @@ pub enum SyntaxError {
/// Index of the end of the path
index: usize,
/// Expected token
- expected: String,
+ expected: &'static str,
},
/// Invalid SVG quaractic arc command flag argument.
diff --git a/src/lib.rs b/src/lib.rs
index 2d15fd7..52cb97a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,11 +1,12 @@
#![doc = include_str!("../README.md")]
#![warn(missing_docs)]
#![doc(test(attr(deny(warnings))))]
-#![no_std]
+#![cfg_attr(not(test), no_std)]
extern crate alloc;
use crate::alloc::{
string::{String, ToString},
+ vec,
vec::Vec,
};
@@ -13,7 +14,7 @@ use crate::alloc::{
::doc_comment::doctest!("../README.md");
#[cfg(test)]
-mod tests;
+pub(crate) mod tests;
mod errors;
pub use errors::SyntaxError;
@@ -142,6 +143,8 @@ impl SVGPathCommand {
/// according to the SVG Path v1.1 specification.
///
/// ```
+/// # // Don't test this in strict mode because it would fail
+/// # #[cfg(not(feature = "strict"))] {
/// use svg_path_cst::{svg_path_cst, SVGPathCSTNode, WSP};
///
/// let cst = svg_path_cst(b" \t\n\r \x0C");
@@ -189,6 +192,7 @@ impl SVGPathCommand {
/// _ => (),
/// }
/// }
+/// # }
/// ```
#[derive(Debug, PartialEq, Clone, Copy)]
#[repr(u8)]
@@ -461,17 +465,20 @@ impl<'a> Parser<'a> {
Some(self.path[self.index])
}
- fn check_unexpected_end(&mut self, expected: &str) -> Result<(), SyntaxError> {
+ fn check_unexpected_end(
+ &mut self,
+ expected: &'static str,
+ ) -> Result<(), SyntaxError> {
if self.peek().is_none() {
return Err(SyntaxError::UnexpectedEnding {
index: self.index - 1,
- expected: expected.to_string(),
+ expected,
});
}
Ok(())
}
- #[cfg_attr(feature = "tracing", tracing::instrument(level = "trace", skip(self)))]
+ #[cfg_attr(feature = "tracing", tracing::instrument(level = "trace", skip_all))]
fn parse_whitespaces(&mut self, nodes: &mut Vec) {
while let Some(next) = self.peek() {
match next {
@@ -522,7 +529,7 @@ impl<'a> Parser<'a> {
#[cfg_attr(
feature = "tracing",
- tracing::instrument(level = "trace", skip(self), err(Debug))
+ tracing::instrument(level = "trace", skip_all, err(Debug))
)]
fn parse_comma_wsp(
&mut self,
@@ -546,7 +553,7 @@ impl<'a> Parser<'a> {
} else {
return Err(SyntaxError::UnexpectedEnding {
index: self.index - 1,
- expected: "comma or whitespace".to_string(),
+ expected: "comma or whitespace",
});
}
@@ -555,7 +562,7 @@ impl<'a> Parser<'a> {
#[cfg_attr(
feature = "tracing",
- tracing::instrument(level = "trace", skip(self), err(Debug))
+ tracing::instrument(level = "trace", skip_all, err(Debug))
)]
fn parse_number(&mut self) -> Result {
let start = self.index;
@@ -615,7 +622,7 @@ impl<'a> Parser<'a> {
return Err(SyntaxError::InvalidCharacter {
character: next as char,
index: self.index - 1,
- expected: "number or command".to_string(),
+ expected: "number or command",
});
} else if !has_digit {
number.push(next);
@@ -654,7 +661,7 @@ impl<'a> Parser<'a> {
}
}
- #[cfg_attr(feature = "tracing", tracing::instrument(level = "trace", skip(self)))]
+ #[cfg_attr(feature = "tracing", tracing::instrument(level = "trace", skip_all))]
fn parse_sign(&mut self) -> Option {
match self.peek() {
Some(b'+') => {
@@ -677,7 +684,7 @@ impl<'a> Parser<'a> {
#[cfg_attr(
feature = "tracing",
- tracing::instrument(level = "trace", skip(self), err(Debug))
+ tracing::instrument(level = "trace", skip_all, err(Debug))
)]
fn parse_flag(&mut self, command: u8) -> Result {
match self.next() {
@@ -690,14 +697,14 @@ impl<'a> Parser<'a> {
}),
None => Err(SyntaxError::UnexpectedEnding {
index: self.index,
- expected: "flag (0 or 1)".to_string(),
+ expected: "flag (0 or 1)",
}),
}
}
#[cfg_attr(
feature = "tracing",
- tracing::instrument(level = "trace", skip(self), err(Debug))
+ tracing::instrument(level = "trace", skip_all, err(Debug))
)]
fn parse_coordinate(&mut self) -> Result {
let sign_node = self.parse_sign();
@@ -710,7 +717,7 @@ impl<'a> Parser<'a> {
else {
Err(SyntaxError::UnexpectedEnding {
index: self.index - 1,
- expected: "number".to_string(),
+ expected: "number",
})?
};
@@ -737,7 +744,7 @@ impl<'a> Parser<'a> {
#[cfg_attr(
feature = "tracing",
- tracing::instrument(level = "trace", skip(self), err(Debug))
+ tracing::instrument(level = "trace", skip_all, err(Debug))
)]
fn parse_coordinate_pair(
&mut self,
@@ -763,7 +770,7 @@ impl<'a> Parser<'a> {
#[cfg_attr(
feature = "tracing",
- tracing::instrument(level = "trace", skip(self), err(Debug))
+ tracing::instrument(level = "trace", skip_all, err(Debug))
)]
fn parse_two_operands_command(
&mut self,
@@ -808,7 +815,7 @@ impl<'a> Parser<'a> {
#[cfg_attr(
feature = "tracing",
- tracing::instrument(level = "trace", skip(self), err(Debug))
+ tracing::instrument(level = "trace", skip_all, err(Debug))
)]
fn parse_four_operands_command(
&mut self,
@@ -861,7 +868,7 @@ impl<'a> Parser<'a> {
#[cfg_attr(
feature = "tracing",
- tracing::instrument(level = "trace", skip(self), err(Debug))
+ tracing::instrument(level = "trace", skip_all, err(Debug))
)]
fn parse_curveto(
&mut self,
@@ -924,7 +931,7 @@ impl<'a> Parser<'a> {
#[cfg_attr(
feature = "tracing",
- tracing::instrument(level = "trace", skip(self), err(Debug))
+ tracing::instrument(level = "trace", skip_all, err(Debug))
)]
fn parse_arc(
&mut self,
@@ -1086,18 +1093,25 @@ impl<'a> Parser<'a> {
Ok(cst)
}
- #[cfg_attr(feature = "tracing", tracing::instrument(level = "trace", skip(self)))]
+ #[cfg_attr(feature = "tracing", tracing::instrument(level = "trace", skip_all))]
fn parse_closepath(&mut self, command: &'static SVGPathCommand) -> SVGPathCSTNode {
- let mut segment = new_segment(command, self.index - 1, false);
- segment.end = self.index;
- segment.chain_end = self.index;
- segment.cst.push(SVGPathCSTNode::Command(command));
- SVGPathCSTNode::Segment(segment)
+ let start = self.index - 1;
+ let end = self.index;
+ SVGPathCSTNode::Segment(SVGPathSegment {
+ command,
+ args: Vec::with_capacity(0),
+ cst: vec![SVGPathCSTNode::Command(command); 1],
+ start,
+ end,
+ chained: false,
+ chain_start: start,
+ chain_end: end,
+ })
}
#[cfg_attr(
feature = "tracing",
- tracing::instrument(level = "trace", skip(self), err(Debug))
+ tracing::instrument(level = "trace", skip_all, err(Debug))
)]
fn parse_horizontal_or_vertical(
&mut self,
@@ -1152,179 +1166,177 @@ impl<'a> Parser<'a> {
#[cfg_attr(
feature = "tracing",
- tracing::instrument(level = "trace", skip(self), err(Debug))
- )]
- fn parse_drawto(
- &mut self,
- command: u8,
- nodes: &mut Vec,
- ) -> Result<(), SyntaxError> {
- match command {
- b'm' => {
- nodes.extend(
- self.parse_two_operands_command(&SVGPathCommand::MovetoLower)?,
- );
- Ok(())
- }
- b'M' => {
- nodes.extend(
- self.parse_two_operands_command(&SVGPathCommand::MovetoUpper)?,
- );
- Ok(())
- }
- b'l' => {
- nodes.extend(
- self.parse_two_operands_command(&SVGPathCommand::LinetoLower)?,
- );
- Ok(())
- }
- b'L' => {
- nodes.extend(
- self.parse_two_operands_command(&SVGPathCommand::LinetoUpper)?,
- );
- Ok(())
- }
- b'h' => {
- nodes.extend(
- self.parse_horizontal_or_vertical(
- &SVGPathCommand::HorizontalLower,
- )?,
- );
- Ok(())
- }
- b'H' => {
- nodes.extend(
- self.parse_horizontal_or_vertical(
- &SVGPathCommand::HorizontalUpper,
- )?,
- );
- Ok(())
- }
- b'v' => {
- nodes.extend(
- self.parse_horizontal_or_vertical(&SVGPathCommand::VerticalLower)?,
- );
- Ok(())
- }
- b'V' => {
- nodes.extend(
- self.parse_horizontal_or_vertical(&SVGPathCommand::VerticalUpper)?,
- );
- Ok(())
- }
- b'z' => {
- nodes.push(self.parse_closepath(&SVGPathCommand::ClosepathLower));
- Ok(())
- }
- b'Z' => {
- nodes.push(self.parse_closepath(&SVGPathCommand::ClosepathUpper));
- Ok(())
- }
- b'c' => {
- nodes.extend(self.parse_curveto(&SVGPathCommand::CurvetoLower)?);
- Ok(())
- }
- b'C' => {
- nodes.extend(self.parse_curveto(&SVGPathCommand::CurvetoUpper)?);
- Ok(())
- }
- b'q' => {
- nodes.extend(
- self.parse_four_operands_command(&SVGPathCommand::QuadraticLower)?,
- );
- Ok(())
- }
- b'Q' => {
- nodes.extend(
- self.parse_four_operands_command(&SVGPathCommand::QuadraticUpper)?,
- );
- Ok(())
- }
- b's' => {
- nodes.extend(self.parse_four_operands_command(
- &SVGPathCommand::SmoothCurvetoLower,
- )?);
- Ok(())
- }
- b'S' => {
- nodes.extend(self.parse_four_operands_command(
- &SVGPathCommand::SmoothCurvetoUpper,
- )?);
- Ok(())
- }
- b'a' => {
- nodes.extend(self.parse_arc(&SVGPathCommand::ArcLower)?);
- Ok(())
- }
- b'A' => {
- nodes.extend(self.parse_arc(&SVGPathCommand::ArcUpper)?);
- Ok(())
- }
- b't' => {
- nodes.extend(self.parse_two_operands_command(
- &SVGPathCommand::SmoothQuadraticLower,
- )?);
- Ok(())
- }
- b'T' => {
- nodes.extend(self.parse_two_operands_command(
- &SVGPathCommand::SmoothQuadraticUpper,
- )?);
- Ok(())
- }
- _ => Err(SyntaxError::InvalidCharacter {
- character: command as char,
- index: self.index - 1,
- expected: "command".to_string(),
- }),
- }
- }
-
- #[cfg_attr(
- feature = "tracing",
- tracing::instrument(level = "trace", skip(self), err(Debug))
+ tracing::instrument(level = "trace", skip_all, err(Debug))
)]
pub fn parse(&mut self) -> Result, SyntaxError> {
if self.path.is_empty() {
#[cfg(feature = "tracing")]
tracing::trace!("Empty SVG path");
+
+ #[cfg(feature = "strict")]
+ return Err(SyntaxError::UnexpectedEnding {
+ expected: "moveto command",
+ index: 0,
+ });
+
+ #[cfg(not(feature = "strict"))]
return Ok(Vec::new());
}
+
if self.path == b"none" {
#[cfg(feature = "tracing")]
tracing::trace!("SVG path with 'none' value");
+
+ #[cfg(feature = "strict")]
+ return Err(SyntaxError::ExpectedMovetoCommand {
+ character: 'n',
+ index: 0,
+ });
+
+ #[cfg(not(feature = "strict"))]
return Ok(Vec::from([SVGPathCSTNode::None]));
}
- let mut cst = Vec::new();
- self.parse_whitespaces(&mut cst);
+ let mut nodes = Vec::with_capacity(self.path.len() / 4);
+ self.parse_whitespaces(&mut nodes);
let next = self.next().unwrap_or(b' ');
match next {
b'm' | b'M' => {
- for node in self.parse_two_operands_command(match next {
+ nodes.extend(self.parse_two_operands_command(match next {
b'm' => &SVGPathCommand::MovetoLower,
_ => &SVGPathCommand::MovetoUpper,
- })? {
- cst.push(node);
- }
- self.parse_whitespaces(&mut cst);
+ })?);
+ self.parse_whitespaces(&mut nodes);
while let Some(next) = self.next() {
- self.parse_drawto(next, &mut cst)?;
- self.parse_whitespaces(&mut cst);
+ match next {
+ b'm' => {
+ nodes.extend(self.parse_two_operands_command(
+ &SVGPathCommand::MovetoLower,
+ )?);
+ }
+ b'M' => {
+ nodes.extend(self.parse_two_operands_command(
+ &SVGPathCommand::MovetoUpper,
+ )?);
+ }
+ b'l' => {
+ nodes.extend(self.parse_two_operands_command(
+ &SVGPathCommand::LinetoLower,
+ )?);
+ }
+ b'L' => {
+ nodes.extend(self.parse_two_operands_command(
+ &SVGPathCommand::LinetoUpper,
+ )?);
+ }
+ b'h' => {
+ nodes.extend(self.parse_horizontal_or_vertical(
+ &SVGPathCommand::HorizontalLower,
+ )?);
+ }
+ b'H' => {
+ nodes.extend(self.parse_horizontal_or_vertical(
+ &SVGPathCommand::HorizontalUpper,
+ )?);
+ }
+ b'v' => {
+ nodes.extend(self.parse_horizontal_or_vertical(
+ &SVGPathCommand::VerticalLower,
+ )?);
+ }
+ b'V' => {
+ nodes.extend(self.parse_horizontal_or_vertical(
+ &SVGPathCommand::VerticalUpper,
+ )?);
+ }
+ b'z' => {
+ nodes.push(
+ self.parse_closepath(&SVGPathCommand::ClosepathLower),
+ );
+ }
+ b'Z' => {
+ nodes.push(
+ self.parse_closepath(&SVGPathCommand::ClosepathUpper),
+ );
+ }
+ b'c' => {
+ nodes.extend(
+ self.parse_curveto(&SVGPathCommand::CurvetoLower)?,
+ );
+ }
+ b'C' => {
+ nodes.extend(
+ self.parse_curveto(&SVGPathCommand::CurvetoUpper)?,
+ );
+ }
+ b'q' => {
+ nodes.extend(self.parse_four_operands_command(
+ &SVGPathCommand::QuadraticLower,
+ )?);
+ }
+ b'Q' => {
+ nodes.extend(self.parse_four_operands_command(
+ &SVGPathCommand::QuadraticUpper,
+ )?);
+ }
+ b's' => {
+ nodes.extend(self.parse_four_operands_command(
+ &SVGPathCommand::SmoothCurvetoLower,
+ )?);
+ }
+ b'S' => {
+ nodes.extend(self.parse_four_operands_command(
+ &SVGPathCommand::SmoothCurvetoUpper,
+ )?);
+ }
+ b'a' => {
+ nodes.extend(self.parse_arc(&SVGPathCommand::ArcLower)?);
+ }
+ b'A' => {
+ nodes.extend(self.parse_arc(&SVGPathCommand::ArcUpper)?);
+ }
+ b't' => {
+ nodes.extend(self.parse_two_operands_command(
+ &SVGPathCommand::SmoothQuadraticLower,
+ )?);
+ }
+ b'T' => {
+ nodes.extend(self.parse_two_operands_command(
+ &SVGPathCommand::SmoothQuadraticUpper,
+ )?);
+ }
+ _ => {
+ return Err(SyntaxError::InvalidCharacter {
+ character: next as char,
+ index: self.index - 1,
+ expected: "command",
+ })
+ }
+ }
+ self.parse_whitespaces(&mut nodes);
}
}
- b' ' => (),
+ b' ' => {
+ #[cfg(all(feature = "tracing", feature = "strict"))]
+ tracing::trace!("Empty SVG path");
+
+ #[cfg(feature = "strict")]
+ return Err(SyntaxError::ExpectedMovetoCommand {
+ character: self.path[0] as char,
+ index: 0,
+ });
+ }
_ => {
#[cfg(feature = "tracing")]
tracing::trace!("Expected moveto command, found '{}'", next as char);
return Err(SyntaxError::ExpectedMovetoCommand {
- command: next as char,
+ character: next as char,
index: self.index - 1,
});
}
}
- Ok(cst)
+ Ok(nodes)
}
}
@@ -1343,7 +1355,7 @@ impl<'a> Parser<'a> {
/// Err(SVGPathSyntaxError::InvalidCharacter {
/// character: '!',
/// index: 6,
-/// expected: "number or command".to_string(),
+/// expected: "number or command",
/// })
/// );
///
@@ -1355,7 +1367,7 @@ impl<'a> Parser<'a> {
#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace", skip_all))]
pub fn svg_path_cst(path: &[u8]) -> Result, SyntaxError> {
#[cfg(feature = "tracing")]
- tracing::info!("Parsing SVG path: {:?}", path);
+ tracing::trace!("{:?}", &path.iter().map(|&c| c as char).collect::());
let mut parser = Parser::new(path);
parser.parse()
}
diff --git a/src/main.rs b/src/main.rs
index 89579cf..f97933e 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -2,8 +2,16 @@ use svg_path_cst::svg_path_cst;
fn main() {
let args: Vec = std::env::args().collect();
- let data = args[args.len() - 1].as_bytes();
- match svg_path_cst(data) {
+ if args.contains(&String::from("--help"))
+ || args.contains(&String::from("-h"))
+ || args.len() < 2
+ {
+ eprintln!("Usage: svg-path-cst \"\"");
+ std::process::exit(1);
+ }
+
+ let svg_path = args[args.len() - 1].as_bytes();
+ match svg_path_cst(svg_path) {
Ok(result) => {
#[allow(clippy::print_stdout)]
{
@@ -11,7 +19,7 @@ fn main() {
}
}
Err(e) => {
- eprintln!("{e:?}");
+ eprintln!("Error: {e}");
std::process::exit(1);
}
}
diff --git a/src/tests/helpers.rs b/src/tests/helpers.rs
new file mode 100644
index 0000000..99631c8
--- /dev/null
+++ b/src/tests/helpers.rs
@@ -0,0 +1,17 @@
+use crate::{svg_path_cst, SyntaxError};
+
+#[cfg_attr(feature = "strict", allow(dead_code))]
+pub(crate) fn assert_svg_path_cst(path: &[u8], expected: Vec) {
+ assert_eq!(svg_path_cst(path), Ok(expected));
+}
+
+#[cfg_attr(feature = "strict", allow(dead_code))]
+pub(crate) fn assert_svg_path_cst_fmt(path: &[u8], expected: &str) {
+ let cst = svg_path_cst(path);
+ assert!(cst.is_ok());
+ assert_eq!(format!("{:?}", cst.unwrap()), expected);
+}
+
+pub(crate) fn assert_svg_path_cst_err(path: &[u8], expected: SyntaxError) {
+ assert_eq!(svg_path_cst(path), Err(expected));
+}
diff --git a/src/tests/integration.sh b/src/tests/integration.sh
new file mode 100644
index 0000000..ae42658
--- /dev/null
+++ b/src/tests/integration.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+
+: '
+ Integration tests for svg-path-cst binary.
+
+ Environment variables:
+
+ - DEBUG: If not empty, all executed commands will be shown in the output.
+'
+
+testSimpleicons() {
+ output="$(./target/debug/svg-path-cst "$(cat fuzz/corpus/simpleicons.txt)")"
+ assertContains "$output" "[Segment(SVGPathSegment { command: MovetoUpper, args: [12.0, 0.0]"
+ assertContains "$output" "chain_end: 449 })]"
+}
+
+testElsevier() {
+ output="$(./target/debug/svg-path-cst "$(cat fuzz/corpus/elsevier.txt)")"
+ assertContains "$output" "[Segment(SVGPathSegment { command: MovetoUpper"
+ assertContains "$output" "chain_end: 80118 })]"
+}
+
+testFailsOnInvalidInput() {
+ output="$(./target/debug/svg-path-cst "invalid" 2>&1)"
+ exitCode=$?
+ assertEquals "$exitCode" 1
+ assertContains "$output" "Error: Invalid SVG path command 'i' at index 0, expected 'M' or 'm'"
+}
+
+testHelp() {
+ output="$(./target/debug/svg-path-cst --help 2>&1)"
+ exitCode=$?
+ assertEquals "$exitCode" 1
+ assertContains "$output" "Usage: svg-path-cst \"\""
+}
+
+testNoArgs() {
+ output="$(./target/debug/svg-path-cst 2>&1)"
+ exitCode=$?
+ assertEquals "$exitCode" 1
+ assertContains "$output" "Usage: svg-path-cst \"\""
+}
+
+testAdditionalArgsAreIgnored() {
+ output="$(./target/debug/svg-path-cst foo bar baz "$(cat fuzz/corpus/simpleicons.txt)" 2>&1)"
+ assertContains "$output" "[Segment(SVGPathSegment { command: MovetoUpper, args: [12.0, 0.0]"
+ assertContains "$output" "chain_end: 449 })]"
+}
+
+prepare() {
+ cargo build
+
+ set -e
+ if [ -n "$DEBUG" ]; then
+ set -x
+ fi
+
+ if [ ! -f "shunit2" ]; then
+ curl -sSL https://raw.githubusercontent.com/kward/shunit2/master/shunit2 \
+ -o shunit2
+ fi
+}
+
+prepare && . ./shunit2
diff --git a/src/tests/mod.rs b/src/tests/mod.rs
new file mode 100644
index 0000000..a03e89c
--- /dev/null
+++ b/src/tests/mod.rs
@@ -0,0 +1,7 @@
+pub(in crate::tests) mod helpers;
+
+#[cfg(not(feature = "strict"))]
+mod no_features;
+
+#[cfg(feature = "strict")]
+mod strict;
diff --git a/src/tests.rs b/src/tests/no_features.rs
similarity index 85%
rename from src/tests.rs
rename to src/tests/no_features.rs
index d8d9ff7..1d7c4ed 100644
--- a/src/tests.rs
+++ b/src/tests/no_features.rs
@@ -1,47 +1,27 @@
-extern crate alloc;
-use crate::alloc::{format, string::ToString, vec, vec::Vec};
-
-use crate::errors::SyntaxError;
-use crate::{svg_path_cst, SVGPathCSTNode, SVGPathCommand, SVGPathSegment, Sign, WSP};
-
-macro_rules! assert_svg_path_cst {
- ($path:expr, $expected:expr) => {
- assert_eq!(svg_path_cst($path), Ok($expected));
- };
-}
-
-macro_rules! assert_svg_path_cst_fmt {
- ($path:expr, $expected:expr) => {
- let cst = svg_path_cst($path);
- assert!(cst.is_ok());
- assert_eq!(format!("{:?}", cst.unwrap()), $expected);
- };
-}
-
-macro_rules! assert_svg_path_cst_err {
- ($path:expr, $expected:expr) => {
- assert_eq!(svg_path_cst($path), Err($expected));
- };
-}
+use crate::tests::helpers::*;
+use crate::{
+ svg_path_cst, SVGPathCSTNode, SVGPathCommand, SVGPathSegment, Sign, SyntaxError,
+ WSP,
+};
#[test]
-fn test_empty() {
- assert_svg_path_cst!(b"", Vec::with_capacity(0));
+fn empty() {
+ assert_svg_path_cst(b"", Vec::new());
}
#[test]
-fn test_none() {
- assert_svg_path_cst!(b"none", vec![SVGPathCSTNode::None]);
+fn none() {
+ assert_svg_path_cst(b"none", vec![SVGPathCSTNode::None]);
}
#[test]
-fn test_none_fmt() {
- assert_svg_path_cst_fmt!(b"none", "[None]");
+fn none_fmt() {
+ assert_svg_path_cst_fmt(b"none", "[None]");
}
#[test]
-fn test_whitespaces() {
- assert_svg_path_cst!(
+fn whitespaces() {
+ assert_svg_path_cst(
b" \t\n\r \x0C",
vec![
SVGPathCSTNode::Whitespace {
@@ -74,13 +54,13 @@ fn test_whitespaces() {
start: 5,
end: 6,
},
- ]
+ ],
);
}
#[test]
-fn test_whitespaces_fmt() {
- assert_svg_path_cst_fmt!(
+fn whitespaces_fmt() {
+ assert_svg_path_cst_fmt(
b" \t\n\r \x0C",
concat!(
"[Whitespace { wsp: Space, start: 0, end: 1 },",
@@ -89,92 +69,92 @@ fn test_whitespaces_fmt() {
" Whitespace { wsp: CarriageReturn, start: 3, end: 4 },",
" Whitespace { wsp: Space, start: 4, end: 5 },",
" Whitespace { wsp: FormFeed, start: 5, end: 6 }]",
- )
+ ),
);
}
#[test]
fn invalid_character() {
- let expected = "number or command".to_string();
+ let expected = "number or command";
- assert_svg_path_cst_err!(
+ assert_svg_path_cst_err(
b"m0 0 !l10 10",
SyntaxError::InvalidCharacter {
character: '!',
index: 5,
- expected: expected.clone(),
- }
+ expected,
+ },
);
- assert_svg_path_cst_err!(
+ assert_svg_path_cst_err(
b"m0 0 l!10 10",
SyntaxError::InvalidCharacter {
character: '!',
index: 6,
- expected: expected.clone(),
- }
+ expected,
+ },
);
- assert_svg_path_cst_err!(
+ assert_svg_path_cst_err(
b"m0 ! 0",
SyntaxError::InvalidCharacter {
character: '!',
index: 3,
- expected: expected.clone(),
- }
+ expected,
+ },
);
- assert_svg_path_cst_err!(
+ assert_svg_path_cst_err(
b"m0 \t\n 0!",
SyntaxError::InvalidCharacter {
character: '!',
index: 7,
- expected: expected.clone(),
- }
+ expected,
+ },
);
}
#[test]
fn invalid_moveto_at_start() {
- assert_svg_path_cst_err!(
+ assert_svg_path_cst_err(
b"A 10 10",
SyntaxError::ExpectedMovetoCommand {
- command: 'A',
+ character: 'A',
index: 0,
- }
+ },
);
}
#[test]
fn invalid_moveto_after_wsp() {
- assert_svg_path_cst_err!(
+ assert_svg_path_cst_err(
b" \t\n\r \x0C A 10 10",
SyntaxError::ExpectedMovetoCommand {
- command: 'A',
+ character: 'A',
index: 7,
- }
+ },
);
}
#[test]
fn invalid_end_in_moveto() {
- assert_svg_path_cst_err!(
+ assert_svg_path_cst_err(
b"m 10",
SyntaxError::UnexpectedEnding {
index: 3,
- expected: "comma or whitespace".to_string(),
- }
+ expected: "comma or whitespace",
+ },
);
}
#[test]
fn invalid_moveto_args() {
- assert_svg_path_cst_err!(
+ assert_svg_path_cst_err(
b"m 1 2 3",
SyntaxError::UnexpectedEnding {
index: 6,
- expected: "comma or whitespace".to_string(),
- }
+ expected: "comma or whitespace",
+ },
);
}
@@ -194,7 +174,7 @@ fn basic_moveto() {
end: 2,
},
SVGPathCSTNode::Number {
- raw_number: "10".to_string(),
+ raw_number: "10".into(),
value: 10.0,
start: 2,
end: 4,
@@ -204,7 +184,7 @@ fn basic_moveto() {
start: 4,
},
SVGPathCSTNode::Number {
- raw_number: "10".to_string(),
+ raw_number: "10".into(),
value: 10.0,
start: 5,
end: 7,
@@ -221,7 +201,7 @@ fn basic_moveto() {
#[test]
fn basic_moveto_fmt() {
- assert_svg_path_cst_fmt!(
+ assert_svg_path_cst_fmt(
b"m 10-10",
concat!(
"[Segment(SVGPathSegment {",
@@ -236,13 +216,13 @@ fn basic_moveto_fmt() {
" start: 0, end: 7, chained: false,",
" chain_start: 0, chain_end: 7",
" })]",
- )
+ ),
);
}
#[test]
fn moveto_whitespaces() {
- assert_svg_path_cst!(
+ assert_svg_path_cst(
b" M \t10\r 10 ",
vec![
SVGPathCSTNode::Whitespace {
@@ -266,7 +246,7 @@ fn moveto_whitespaces() {
end: 4,
},
SVGPathCSTNode::Number {
- raw_number: "10".to_string(),
+ raw_number: "10".into(),
value: 10.0,
start: 4,
end: 6,
@@ -282,7 +262,7 @@ fn moveto_whitespaces() {
end: 8,
},
SVGPathCSTNode::Number {
- raw_number: "10".to_string(),
+ raw_number: "10".into(),
value: 10.0,
start: 8,
end: 10,
@@ -299,13 +279,13 @@ fn moveto_whitespaces() {
start: 10,
end: 11,
},
- ]
+ ],
);
}
#[test]
fn chained_moveto() {
- assert_svg_path_cst!(
+ assert_svg_path_cst(
b"M 10 10 20 20",
vec![
SVGPathCSTNode::Segment(SVGPathSegment {
@@ -319,7 +299,7 @@ fn chained_moveto() {
end: 2,
},
SVGPathCSTNode::Number {
- raw_number: "10".to_string(),
+ raw_number: "10".into(),
value: 10.0,
start: 2,
end: 4,
@@ -330,7 +310,7 @@ fn chained_moveto() {
end: 5,
},
SVGPathCSTNode::Number {
- raw_number: "10".to_string(),
+ raw_number: "10".into(),
value: 10.0,
start: 5,
end: 7,
@@ -352,7 +332,7 @@ fn chained_moveto() {
args: vec![20.0, 20.0],
cst: vec![
SVGPathCSTNode::Number {
- raw_number: "20".to_string(),
+ raw_number: "20".into(),
value: 20.0,
start: 8,
end: 10,
@@ -363,7 +343,7 @@ fn chained_moveto() {
end: 11,
},
SVGPathCSTNode::Number {
- raw_number: "20".to_string(),
+ raw_number: "20".into(),
value: 20.0,
start: 11,
end: 13,
@@ -375,13 +355,13 @@ fn chained_moveto() {
chain_start: 0,
chain_end: 13,
}),
- ]
+ ],
);
}
#[test]
fn moveto_and_moveto_drawto() {
- assert_svg_path_cst!(
+ assert_svg_path_cst(
b"M10 10 M5 -4.6",
vec![
SVGPathCSTNode::Segment(SVGPathSegment {
@@ -390,7 +370,7 @@ fn moveto_and_moveto_drawto() {
cst: vec![
SVGPathCSTNode::Command(&SVGPathCommand::MovetoUpper),
SVGPathCSTNode::Number {
- raw_number: "10".to_string(),
+ raw_number: "10".into(),
value: 10.0,
start: 1,
end: 3,
@@ -401,7 +381,7 @@ fn moveto_and_moveto_drawto() {
end: 4,
},
SVGPathCSTNode::Number {
- raw_number: "10".to_string(),
+ raw_number: "10".into(),
value: 10.0,
start: 4,
end: 6,
@@ -424,7 +404,7 @@ fn moveto_and_moveto_drawto() {
cst: vec![
SVGPathCSTNode::Command(&SVGPathCommand::MovetoUpper),
SVGPathCSTNode::Number {
- raw_number: "5".to_string(),
+ raw_number: "5".into(),
value: 5.0,
start: 8,
end: 9,
@@ -439,7 +419,7 @@ fn moveto_and_moveto_drawto() {
start: 10,
},
SVGPathCSTNode::Number {
- raw_number: "4.6".to_string(),
+ raw_number: "4.6".into(),
value: 4.6,
start: 11,
end: 14,
@@ -451,13 +431,13 @@ fn moveto_and_moveto_drawto() {
chain_start: 7,
chain_end: 14,
}),
- ]
+ ],
);
}
#[test]
fn horizontal_and_vertical() {
- assert_svg_path_cst!(
+ assert_svg_path_cst(
b"m1 2h6v-3 5",
vec![
SVGPathCSTNode::Segment(SVGPathSegment {
@@ -466,7 +446,7 @@ fn horizontal_and_vertical() {
cst: vec![
SVGPathCSTNode::Command(&SVGPathCommand::MovetoLower),
SVGPathCSTNode::Number {
- raw_number: "1".to_string(),
+ raw_number: "1".into(),
value: 1.0,
start: 1,
end: 2,
@@ -477,7 +457,7 @@ fn horizontal_and_vertical() {
end: 3,
},
SVGPathCSTNode::Number {
- raw_number: "2".to_string(),
+ raw_number: "2".into(),
value: 2.0,
start: 3,
end: 4,
@@ -495,7 +475,7 @@ fn horizontal_and_vertical() {
cst: vec![
SVGPathCSTNode::Command(&SVGPathCommand::HorizontalLower),
SVGPathCSTNode::Number {
- raw_number: "6".to_string(),
+ raw_number: "6".into(),
value: 6.0,
start: 5,
end: 6,
@@ -517,7 +497,7 @@ fn horizontal_and_vertical() {
start: 7,
},
SVGPathCSTNode::Number {
- raw_number: "3".to_string(),
+ raw_number: "3".into(),
value: 3.0,
start: 8,
end: 9,
@@ -538,24 +518,24 @@ fn horizontal_and_vertical() {
command: &SVGPathCommand::VerticalLower,
args: vec![5.0],
cst: vec![SVGPathCSTNode::Number {
- raw_number: "5".to_string(),
+ raw_number: "5".into(),
value: 5.0,
start: 10,
end: 11,
- },],
+ }],
start: 10,
end: 11,
chained: true,
chain_start: 6,
chain_end: 11,
}),
- ]
+ ],
);
}
#[test]
fn moveto_curveto() {
- assert_svg_path_cst!(
+ assert_svg_path_cst(
b"m0 0c100,100 250,100 250,200 200,200 500,200 500,400",
vec![
SVGPathCSTNode::Segment(SVGPathSegment {
@@ -564,7 +544,7 @@ fn moveto_curveto() {
cst: vec![
SVGPathCSTNode::Command(&SVGPathCommand::MovetoLower),
SVGPathCSTNode::Number {
- raw_number: "0".to_string(),
+ raw_number: "0".into(),
value: 0.0,
start: 1,
end: 2,
@@ -575,7 +555,7 @@ fn moveto_curveto() {
end: 3,
},
SVGPathCSTNode::Number {
- raw_number: "0".to_string(),
+ raw_number: "0".into(),
value: 0.0,
start: 3,
end: 4,
@@ -593,14 +573,14 @@ fn moveto_curveto() {
cst: vec![
SVGPathCSTNode::Command(&SVGPathCommand::CurvetoLower),
SVGPathCSTNode::Number {
- raw_number: "100".to_string(),
+ raw_number: "100".into(),
value: 100.0,
start: 5,
end: 8,
},
SVGPathCSTNode::Comma { start: 8 },
SVGPathCSTNode::Number {
- raw_number: "100".to_string(),
+ raw_number: "100".into(),
value: 100.0,
start: 9,
end: 12,
@@ -611,14 +591,14 @@ fn moveto_curveto() {
end: 13,
},
SVGPathCSTNode::Number {
- raw_number: "250".to_string(),
+ raw_number: "250".into(),
value: 250.0,
start: 13,
end: 16,
},
SVGPathCSTNode::Comma { start: 16 },
SVGPathCSTNode::Number {
- raw_number: "100".to_string(),
+ raw_number: "100".into(),
value: 100.0,
start: 17,
end: 20,
@@ -629,14 +609,14 @@ fn moveto_curveto() {
end: 21,
},
SVGPathCSTNode::Number {
- raw_number: "250".to_string(),
+ raw_number: "250".into(),
value: 250.0,
start: 21,
end: 24,
},
SVGPathCSTNode::Comma { start: 24 },
SVGPathCSTNode::Number {
- raw_number: "200".to_string(),
+ raw_number: "200".into(),
value: 200.0,
start: 25,
end: 28,
@@ -658,14 +638,14 @@ fn moveto_curveto() {
args: vec![200.0, 200.0, 500.0, 200.0, 500.0, 400.0],
cst: vec![
SVGPathCSTNode::Number {
- raw_number: "200".to_string(),
+ raw_number: "200".into(),
value: 200.0,
start: 29,
end: 32,
},
SVGPathCSTNode::Comma { start: 32 },
SVGPathCSTNode::Number {
- raw_number: "200".to_string(),
+ raw_number: "200".into(),
value: 200.0,
start: 33,
end: 36,
@@ -676,14 +656,14 @@ fn moveto_curveto() {
end: 37,
},
SVGPathCSTNode::Number {
- raw_number: "500".to_string(),
+ raw_number: "500".into(),
value: 500.0,
start: 37,
end: 40,
},
SVGPathCSTNode::Comma { start: 40 },
SVGPathCSTNode::Number {
- raw_number: "200".to_string(),
+ raw_number: "200".into(),
value: 200.0,
start: 41,
end: 44,
@@ -694,14 +674,14 @@ fn moveto_curveto() {
end: 45,
},
SVGPathCSTNode::Number {
- raw_number: "500".to_string(),
+ raw_number: "500".into(),
value: 500.0,
start: 45,
end: 48,
},
SVGPathCSTNode::Comma { start: 48 },
SVGPathCSTNode::Number {
- raw_number: "400".to_string(),
+ raw_number: "400".into(),
value: 400.0,
start: 49,
end: 52,
@@ -713,13 +693,13 @@ fn moveto_curveto() {
chain_start: 4,
chain_end: 52,
}),
- ]
+ ],
);
}
#[test]
fn moveto_smooth_curveto() {
- assert_svg_path_cst!(
+ assert_svg_path_cst(
b"m0 0s100,100 250,200 150 150 300 300",
vec![
SVGPathCSTNode::Segment(SVGPathSegment {
@@ -728,7 +708,7 @@ fn moveto_smooth_curveto() {
cst: vec![
SVGPathCSTNode::Command(&SVGPathCommand::MovetoLower),
SVGPathCSTNode::Number {
- raw_number: "0".to_string(),
+ raw_number: "0".into(),
value: 0.0,
start: 1,
end: 2,
@@ -739,7 +719,7 @@ fn moveto_smooth_curveto() {
end: 3,
},
SVGPathCSTNode::Number {
- raw_number: "0".to_string(),
+ raw_number: "0".into(),
value: 0.0,
start: 3,
end: 4,
@@ -757,14 +737,14 @@ fn moveto_smooth_curveto() {
cst: vec![
SVGPathCSTNode::Command(&SVGPathCommand::SmoothCurvetoLower),
SVGPathCSTNode::Number {
- raw_number: "100".to_string(),
+ raw_number: "100".into(),
value: 100.0,
start: 5,
end: 8,
},
SVGPathCSTNode::Comma { start: 8 },
SVGPathCSTNode::Number {
- raw_number: "100".to_string(),
+ raw_number: "100".into(),
value: 100.0,
start: 9,
end: 12,
@@ -775,14 +755,14 @@ fn moveto_smooth_curveto() {
end: 13,
},
SVGPathCSTNode::Number {
- raw_number: "250".to_string(),
+ raw_number: "250".into(),
value: 250.0,
start: 13,
end: 16,
},
SVGPathCSTNode::Comma { start: 16 },
SVGPathCSTNode::Number {
- raw_number: "200".to_string(),
+ raw_number: "200".into(),
value: 200.0,
start: 17,
end: 20,
@@ -804,7 +784,7 @@ fn moveto_smooth_curveto() {
args: vec![150.0, 150.0, 300.0, 300.0],
cst: vec![
SVGPathCSTNode::Number {
- raw_number: "150".to_string(),
+ raw_number: "150".into(),
value: 150.0,
start: 21,
end: 24,
@@ -815,7 +795,7 @@ fn moveto_smooth_curveto() {
end: 25,
},
SVGPathCSTNode::Number {
- raw_number: "150".to_string(),
+ raw_number: "150".into(),
value: 150.0,
start: 25,
end: 28,
@@ -826,7 +806,7 @@ fn moveto_smooth_curveto() {
end: 29,
},
SVGPathCSTNode::Number {
- raw_number: "300".to_string(),
+ raw_number: "300".into(),
value: 300.0,
start: 29,
end: 32,
@@ -837,7 +817,7 @@ fn moveto_smooth_curveto() {
end: 33,
},
SVGPathCSTNode::Number {
- raw_number: "300".to_string(),
+ raw_number: "300".into(),
value: 300.0,
start: 33,
end: 36,
@@ -849,13 +829,13 @@ fn moveto_smooth_curveto() {
chain_start: 4,
chain_end: 36,
}),
- ]
+ ],
);
}
#[test]
fn moveto_arc() {
- assert_svg_path_cst!(
+ assert_svg_path_cst(
b"m0 0a100,100 0 0 1 250,200 150 150 0 0 0 300 300",
vec![
SVGPathCSTNode::Segment(SVGPathSegment {
@@ -864,7 +844,7 @@ fn moveto_arc() {
cst: vec![
SVGPathCSTNode::Command(&SVGPathCommand::MovetoLower),
SVGPathCSTNode::Number {
- raw_number: "0".to_string(),
+ raw_number: "0".into(),
value: 0.0,
start: 1,
end: 2,
@@ -875,7 +855,7 @@ fn moveto_arc() {
end: 3,
},
SVGPathCSTNode::Number {
- raw_number: "0".to_string(),
+ raw_number: "0".into(),
value: 0.0,
start: 3,
end: 4,
@@ -893,14 +873,14 @@ fn moveto_arc() {
cst: vec![
SVGPathCSTNode::Command(&SVGPathCommand::ArcLower),
SVGPathCSTNode::Number {
- raw_number: "100".to_string(),
+ raw_number: "100".into(),
value: 100.0,
start: 5,
end: 8,
},
SVGPathCSTNode::Comma { start: 8 },
SVGPathCSTNode::Number {
- raw_number: "100".to_string(),
+ raw_number: "100".into(),
value: 100.0,
start: 9,
end: 12,
@@ -911,7 +891,7 @@ fn moveto_arc() {
end: 13,
},
SVGPathCSTNode::Number {
- raw_number: "0".to_string(),
+ raw_number: "0".into(),
value: 0.0,
start: 13,
end: 14,
@@ -922,7 +902,7 @@ fn moveto_arc() {
end: 15,
},
SVGPathCSTNode::Number {
- raw_number: "0".to_string(),
+ raw_number: "0".into(),
value: 0.0,
start: 15,
end: 16,
@@ -933,7 +913,7 @@ fn moveto_arc() {
end: 17,
},
SVGPathCSTNode::Number {
- raw_number: "1".to_string(),
+ raw_number: "1".into(),
value: 1.0,
start: 17,
end: 18,
@@ -944,14 +924,14 @@ fn moveto_arc() {
end: 19,
},
SVGPathCSTNode::Number {
- raw_number: "250".to_string(),
+ raw_number: "250".into(),
value: 250.0,
start: 19,
end: 22,
},
SVGPathCSTNode::Comma { start: 22 },
SVGPathCSTNode::Number {
- raw_number: "200".to_string(),
+ raw_number: "200".into(),
value: 200.0,
start: 23,
end: 26,
@@ -973,7 +953,7 @@ fn moveto_arc() {
args: vec![150.0, 150.0, 0.0, 0.0, 0.0, 300.0, 300.0],
cst: vec![
SVGPathCSTNode::Number {
- raw_number: "150".to_string(),
+ raw_number: "150".into(),
value: 150.0,
start: 27,
end: 30,
@@ -984,7 +964,7 @@ fn moveto_arc() {
end: 31,
},
SVGPathCSTNode::Number {
- raw_number: "150".to_string(),
+ raw_number: "150".into(),
value: 150.0,
start: 31,
end: 34,
@@ -995,7 +975,7 @@ fn moveto_arc() {
end: 35,
},
SVGPathCSTNode::Number {
- raw_number: "0".to_string(),
+ raw_number: "0".into(),
value: 0.0,
start: 35,
end: 36,
@@ -1006,7 +986,7 @@ fn moveto_arc() {
end: 37,
},
SVGPathCSTNode::Number {
- raw_number: "0".to_string(),
+ raw_number: "0".into(),
value: 0.0,
start: 37,
end: 38,
@@ -1017,7 +997,7 @@ fn moveto_arc() {
end: 39,
},
SVGPathCSTNode::Number {
- raw_number: "0".to_string(),
+ raw_number: "0".into(),
value: 0.0,
start: 39,
end: 40,
@@ -1028,7 +1008,7 @@ fn moveto_arc() {
end: 41,
},
SVGPathCSTNode::Number {
- raw_number: "300".to_string(),
+ raw_number: "300".into(),
value: 300.0,
start: 41,
end: 44,
@@ -1039,7 +1019,7 @@ fn moveto_arc() {
end: 45,
},
SVGPathCSTNode::Number {
- raw_number: "300".to_string(),
+ raw_number: "300".into(),
value: 300.0,
start: 45,
end: 48,
@@ -1051,25 +1031,25 @@ fn moveto_arc() {
chain_start: 4,
chain_end: 48,
}),
- ]
+ ],
);
}
#[test]
fn invalid_arc_flag() {
- assert_svg_path_cst_err!(
+ assert_svg_path_cst_err(
b"m0 0a100,100 0 2 1 250,200",
SyntaxError::InvalidArcFlag {
index: 15,
character: '2',
command: 'a',
- }
+ },
);
}
#[test]
fn moveto_quadratic() {
- assert_svg_path_cst!(
+ assert_svg_path_cst(
b"m0 0q100,100 250,200 150 150 300 300",
vec![
SVGPathCSTNode::Segment(SVGPathSegment {
@@ -1078,7 +1058,7 @@ fn moveto_quadratic() {
cst: vec![
SVGPathCSTNode::Command(&SVGPathCommand::MovetoLower),
SVGPathCSTNode::Number {
- raw_number: "0".to_string(),
+ raw_number: "0".into(),
value: 0.0,
start: 1,
end: 2,
@@ -1089,7 +1069,7 @@ fn moveto_quadratic() {
end: 3,
},
SVGPathCSTNode::Number {
- raw_number: "0".to_string(),
+ raw_number: "0".into(),
value: 0.0,
start: 3,
end: 4,
@@ -1107,14 +1087,14 @@ fn moveto_quadratic() {
cst: vec![
SVGPathCSTNode::Command(&SVGPathCommand::QuadraticLower),
SVGPathCSTNode::Number {
- raw_number: "100".to_string(),
+ raw_number: "100".into(),
value: 100.0,
start: 5,
end: 8,
},
SVGPathCSTNode::Comma { start: 8 },
SVGPathCSTNode::Number {
- raw_number: "100".to_string(),
+ raw_number: "100".into(),
value: 100.0,
start: 9,
end: 12,
@@ -1125,14 +1105,14 @@ fn moveto_quadratic() {
end: 13,
},
SVGPathCSTNode::Number {
- raw_number: "250".to_string(),
+ raw_number: "250".into(),
value: 250.0,
start: 13,
end: 16,
},
SVGPathCSTNode::Comma { start: 16 },
SVGPathCSTNode::Number {
- raw_number: "200".to_string(),
+ raw_number: "200".into(),
value: 200.0,
start: 17,
end: 20,
@@ -1154,7 +1134,7 @@ fn moveto_quadratic() {
args: vec![150.0, 150.0, 300.0, 300.0],
cst: vec![
SVGPathCSTNode::Number {
- raw_number: "150".to_string(),
+ raw_number: "150".into(),
value: 150.0,
start: 21,
end: 24,
@@ -1165,7 +1145,7 @@ fn moveto_quadratic() {
end: 25,
},
SVGPathCSTNode::Number {
- raw_number: "150".to_string(),
+ raw_number: "150".into(),
value: 150.0,
start: 25,
end: 28,
@@ -1176,7 +1156,7 @@ fn moveto_quadratic() {
end: 29,
},
SVGPathCSTNode::Number {
- raw_number: "300".to_string(),
+ raw_number: "300".into(),
value: 300.0,
start: 29,
end: 32,
@@ -1187,7 +1167,7 @@ fn moveto_quadratic() {
end: 33,
},
SVGPathCSTNode::Number {
- raw_number: "300".to_string(),
+ raw_number: "300".into(),
value: 300.0,
start: 33,
end: 36,
@@ -1199,13 +1179,13 @@ fn moveto_quadratic() {
chain_start: 4,
chain_end: 36,
}),
- ]
+ ],
);
}
#[test]
fn moveto_smooth_quadratic() {
- assert_svg_path_cst!(
+ assert_svg_path_cst(
b"m0 0t100,100 250,200",
vec![
SVGPathCSTNode::Segment(SVGPathSegment {
@@ -1214,7 +1194,7 @@ fn moveto_smooth_quadratic() {
cst: vec![
SVGPathCSTNode::Command(&SVGPathCommand::MovetoLower),
SVGPathCSTNode::Number {
- raw_number: "0".to_string(),
+ raw_number: "0".into(),
value: 0.0,
start: 1,
end: 2,
@@ -1225,7 +1205,7 @@ fn moveto_smooth_quadratic() {
end: 3,
},
SVGPathCSTNode::Number {
- raw_number: "0".to_string(),
+ raw_number: "0".into(),
value: 0.0,
start: 3,
end: 4,
@@ -1243,14 +1223,14 @@ fn moveto_smooth_quadratic() {
cst: vec![
SVGPathCSTNode::Command(&SVGPathCommand::SmoothQuadraticLower),
SVGPathCSTNode::Number {
- raw_number: "100".to_string(),
+ raw_number: "100".into(),
value: 100.0,
start: 5,
end: 8,
},
SVGPathCSTNode::Comma { start: 8 },
SVGPathCSTNode::Number {
- raw_number: "100".to_string(),
+ raw_number: "100".into(),
value: 100.0,
start: 9,
end: 12,
@@ -1272,14 +1252,14 @@ fn moveto_smooth_quadratic() {
args: vec![250.0, 200.0],
cst: vec![
SVGPathCSTNode::Number {
- raw_number: "250".to_string(),
+ raw_number: "250".into(),
value: 250.0,
start: 13,
end: 16,
},
SVGPathCSTNode::Comma { start: 16 },
SVGPathCSTNode::Number {
- raw_number: "200".to_string(),
+ raw_number: "200".into(),
value: 200.0,
start: 17,
end: 20,
@@ -1291,25 +1271,25 @@ fn moveto_smooth_quadratic() {
chain_start: 4,
chain_end: 20,
}),
- ]
+ ],
);
}
#[test]
fn invalid_multiple_commas() {
- assert_svg_path_cst_err!(
+ assert_svg_path_cst_err(
b"m0 0,,100,100",
SyntaxError::InvalidNumber {
- number: ",".to_string(),
+ number: ",".into(),
start: 4,
end: 5,
- }
+ },
);
}
#[test]
fn arc_with_flags_together() {
- assert_svg_path_cst!(
+ assert_svg_path_cst(
b"m0 0a1.862 1.862 0 00-.248.033",
vec![
SVGPathCSTNode::Segment(SVGPathSegment {
@@ -1318,7 +1298,7 @@ fn arc_with_flags_together() {
cst: vec![
SVGPathCSTNode::Command(&SVGPathCommand::MovetoLower),
SVGPathCSTNode::Number {
- raw_number: "0".to_string(),
+ raw_number: "0".into(),
value: 0.0,
start: 1,
end: 2,
@@ -1329,7 +1309,7 @@ fn arc_with_flags_together() {
end: 3,
},
SVGPathCSTNode::Number {
- raw_number: "0".to_string(),
+ raw_number: "0".into(),
value: 0.0,
start: 3,
end: 4,
@@ -1347,7 +1327,7 @@ fn arc_with_flags_together() {
cst: vec![
SVGPathCSTNode::Command(&SVGPathCommand::ArcLower),
SVGPathCSTNode::Number {
- raw_number: "1.862".to_string(),
+ raw_number: "1.862".into(),
value: 1.862,
start: 5,
end: 10,
@@ -1358,7 +1338,7 @@ fn arc_with_flags_together() {
end: 11,
},
SVGPathCSTNode::Number {
- raw_number: "1.862".to_string(),
+ raw_number: "1.862".into(),
value: 1.862,
start: 11,
end: 16,
@@ -1369,7 +1349,7 @@ fn arc_with_flags_together() {
end: 17,
},
SVGPathCSTNode::Number {
- raw_number: "0".to_string(),
+ raw_number: "0".into(),
value: 0.0,
start: 17,
end: 18,
@@ -1380,13 +1360,13 @@ fn arc_with_flags_together() {
end: 19,
},
SVGPathCSTNode::Number {
- raw_number: "0".to_string(),
+ raw_number: "0".into(),
value: 0.0,
start: 19,
end: 20,
},
SVGPathCSTNode::Number {
- raw_number: "0".to_string(),
+ raw_number: "0".into(),
value: 0.0,
start: 20,
end: 21,
@@ -1396,13 +1376,13 @@ fn arc_with_flags_together() {
start: 21,
},
SVGPathCSTNode::Number {
- raw_number: ".248".to_string(),
+ raw_number: ".248".into(),
value: 0.248,
start: 22,
end: 26,
},
SVGPathCSTNode::Number {
- raw_number: ".033".to_string(),
+ raw_number: ".033".into(),
value: 0.033,
start: 26,
end: 30,
@@ -1414,25 +1394,38 @@ fn arc_with_flags_together() {
chain_start: 4,
chain_end: 30,
}),
- ]
+ ],
);
}
#[test]
fn invalid_utf8_length_1() {
- assert_svg_path_cst_err!(
+ assert_svg_path_cst_err(
b"m0 0\xE1", // \xE1 is á
SyntaxError::InvalidCharacter {
character: 'á',
index: 4,
- expected: "number or command".to_string()
+ expected: "number or command",
+ },
+ );
+}
+
+#[test]
+fn invalid_path_ending() {
+ // https://github.com/simple-icons/simple-icons/pull/11053
+ assert_svg_path_cst_err(
+ b"m2.249 8.801a2.6 2.6 0 0 1-2.6 2.6 2.6 2.6 0 0 1-2.6-2.6 2.6 2.6 0 0 1 2.6-2.6 2.6 2.6 0 0 1 2.6 2.6z55",
+ SyntaxError::InvalidCharacter {
+ character: '5',
+ index: 101,
+ expected: "command",
}
);
}
#[test]
fn simple_icons_icon_path() {
- let cst = svg_path_cst(include_bytes!("../fuzz/corpus/simpleicons.txt"));
+ let cst = svg_path_cst(include_bytes!("../../fuzz/corpus/simpleicons.txt"));
assert!(cst.is_ok());
assert_eq!(cst.unwrap().len(), 46);
}
diff --git a/src/tests/strict.rs b/src/tests/strict.rs
new file mode 100644
index 0000000..8b02cf5
--- /dev/null
+++ b/src/tests/strict.rs
@@ -0,0 +1,35 @@
+use crate::tests::helpers::*;
+use crate::SyntaxError;
+
+#[test]
+fn empty() {
+ assert_svg_path_cst_err(
+ b"",
+ SyntaxError::UnexpectedEnding {
+ expected: "moveto command",
+ index: 0,
+ },
+ );
+}
+
+#[test]
+fn none() {
+ assert_svg_path_cst_err(
+ b"none",
+ SyntaxError::ExpectedMovetoCommand {
+ character: 'n',
+ index: 0,
+ },
+ );
+}
+
+#[test]
+fn whitespaces() {
+ assert_svg_path_cst_err(
+ b"\t\n\r \x0C",
+ SyntaxError::ExpectedMovetoCommand {
+ character: '\t',
+ index: 0,
+ },
+ );
+}