Compare commits
150 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
320b5f8568 | ||
|
|
99b2af6c99 | ||
|
|
6e71acdb7d | ||
|
|
8406d37991 | ||
|
|
64bb597908 | ||
|
|
068864ea87 | ||
|
|
03a3ddbd63 | ||
|
|
122adee23b | ||
|
|
556afecbb8 | ||
|
|
e4407cbdd1 | ||
|
|
f57d60dab0 | ||
|
|
0aa3939a75 | ||
|
|
52cb81e75e | ||
|
|
945121202d | ||
|
|
f4e0dddd9d | ||
|
|
6b62176fd0 | ||
|
|
44483b4d54 | ||
|
|
48d3de77fe | ||
|
|
680b176501 | ||
|
|
dc0338e978 | ||
|
|
ff3e0a50af | ||
|
|
03c8c07fe0 | ||
|
|
3a6fc5b669 | ||
|
|
d258cdb839 | ||
|
|
aa5629354e | ||
|
|
efc4a04829 | ||
|
|
dd611ea64a | ||
|
|
4bd5f3bec7 | ||
|
|
c2b3509b6a | ||
|
|
7f3f5fb889 | ||
|
|
e0fbf17226 | ||
|
|
4e18cbafba | ||
|
|
46c36d7f3e | ||
|
|
c46a935cfc | ||
|
|
f50415cb32 | ||
|
|
4f1a151e97 | ||
|
|
c8e3fdba51 | ||
|
|
4b3fc20c62 | ||
|
|
3131f8ac64 | ||
|
|
60a4835590 | ||
|
|
172d72aa46 | ||
|
|
b4fcc6500b | ||
|
|
ddb6f31562 | ||
|
|
dc080b30fc | ||
|
|
9901e17437 | ||
|
|
ea000894f0 | ||
|
|
e7742b529a | ||
|
|
8eba0c4923 | ||
|
|
e0c0070a13 | ||
|
|
65ce116998 | ||
|
|
e348e7d4e3 | ||
|
|
492090470c | ||
|
|
3ec900c8df | ||
|
|
d0a008ed22 | ||
|
|
f2292f1c07 | ||
|
|
44392cfcca | ||
|
|
110630d230 | ||
|
|
ebe12d96c1 | ||
|
|
24c8ac8e21 | ||
|
|
259ad6e242 | ||
|
|
dd1f7c7777 | ||
|
|
c1b471208d | ||
|
|
606bab9e6d | ||
|
|
0edf5620a2 | ||
|
|
cdf87641c5 | ||
|
|
eb2995dd3b | ||
|
|
cd6a64c015 | ||
|
|
a4a83d047d | ||
|
|
a4414369ce | ||
|
|
83e4b72307 | ||
|
|
34b3e4fa7b | ||
|
|
c0e879dc1e | ||
|
|
fa31b001f4 | ||
|
|
0897061ff6 | ||
|
|
28a3e1bc7b | ||
|
|
3fd3d20722 | ||
|
|
90735586b5 | ||
|
|
78befc7665 | ||
|
|
ef549d3b19 | ||
|
|
777c756a7f | ||
|
|
037caf369c | ||
|
|
54085b5833 | ||
|
|
2bfa8e59e7 | ||
|
|
5d31db39a4 | ||
|
|
adcd0de7e4 | ||
|
|
c2f9789a64 | ||
|
|
579cbb5d11 | ||
|
|
cad2be43bf | ||
|
|
a0a4f0eb90 | ||
|
|
9f4f8e79ce | ||
|
|
77e0dbb42e | ||
|
|
eff5cdbf40 | ||
|
|
eef3571299 | ||
|
|
f227d8405e | ||
|
|
9520e5814b | ||
|
|
28ad4fd046 | ||
|
|
7626a69fa1 | ||
|
|
121c0ce516 | ||
|
|
5a64db98fe | ||
|
|
abfae9c6c0 | ||
|
|
5272e2f1b4 | ||
|
|
90d4b11922 | ||
|
|
d552ef6569 | ||
|
|
f050e9b6a8 | ||
|
|
a5e108bc37 | ||
|
|
58290515b5 | ||
|
|
423f65046e | ||
|
|
badeaf8246 | ||
|
|
d38100581c | ||
|
|
f4eff5ca56 | ||
|
|
5b02c21ebf | ||
|
|
5f1668702a | ||
|
|
1faaeeebf1 | ||
|
|
20a7c89084 | ||
|
|
e83417b243 | ||
|
|
36b80dc093 | ||
|
|
1812b1a56e | ||
|
|
1a70b3d2c0 | ||
|
|
abf066701e | ||
|
|
4984ea4179 | ||
|
|
3cb251ea6c | ||
|
|
4bfea41291 | ||
|
|
99376515ef | ||
|
|
23f4ba4205 | ||
|
|
55ad136283 | ||
|
|
c717541099 | ||
|
|
c2e921c2dc | ||
|
|
e499169f0e | ||
|
|
84c088df67 | ||
|
|
f210f95f99 | ||
|
|
17b81c7c72 | ||
|
|
2911fce7cc | ||
|
|
e622d9fa6b | ||
|
|
8186fbb8b3 | ||
|
|
68ccff74fa | ||
|
|
9a13cb72c6 | ||
|
|
65abaa332f | ||
|
|
67e5829fd9 | ||
|
|
995b41e697 | ||
|
|
eb51bdfe2f | ||
|
|
bbb9ec637a | ||
|
|
dc012b49f5 | ||
|
|
13863a68f7 | ||
|
|
2962f76c81 | ||
|
|
b9b3ef6e74 | ||
|
|
310ab2eab2 | ||
|
|
53320070da | ||
|
|
2d5593681f | ||
|
|
b3f97dbb40 | ||
|
|
a48d76321e |
@@ -192,6 +192,54 @@ spec:
|
|||||||
]
|
]
|
||||||
- name: docker-image
|
- name: docker-image
|
||||||
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
||||||
|
- name: run-image-wasm
|
||||||
|
taskRef:
|
||||||
|
name: run-docker-image
|
||||||
|
workspaces:
|
||||||
|
- name: source
|
||||||
|
workspace: git-source
|
||||||
|
- name: cargo-cache
|
||||||
|
workspace: cargo-cache
|
||||||
|
runAfter:
|
||||||
|
- run-image-all
|
||||||
|
params:
|
||||||
|
- name: args
|
||||||
|
value:
|
||||||
|
[
|
||||||
|
"--target",
|
||||||
|
"wasm32-unknown-unknown",
|
||||||
|
"--profile",
|
||||||
|
"wasm",
|
||||||
|
"--bin",
|
||||||
|
"wasm",
|
||||||
|
"--no-default-features",
|
||||||
|
"--features",
|
||||||
|
"wasm",
|
||||||
|
]
|
||||||
|
- name: docker-image
|
||||||
|
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
||||||
|
- name: run-image-wasm-test
|
||||||
|
taskRef:
|
||||||
|
name: run-docker-image
|
||||||
|
workspaces:
|
||||||
|
- name: source
|
||||||
|
workspace: git-source
|
||||||
|
- name: cargo-cache
|
||||||
|
workspace: cargo-cache
|
||||||
|
runAfter:
|
||||||
|
- run-image-wasm
|
||||||
|
params:
|
||||||
|
- name: args
|
||||||
|
value:
|
||||||
|
[
|
||||||
|
"--bin",
|
||||||
|
"wasm_test",
|
||||||
|
"--no-default-features",
|
||||||
|
"--features",
|
||||||
|
"wasm_test",
|
||||||
|
]
|
||||||
|
- name: docker-image
|
||||||
|
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
||||||
finally:
|
finally:
|
||||||
- name: report-success
|
- name: report-success
|
||||||
when:
|
when:
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ spec:
|
|||||||
[
|
[
|
||||||
--no-default-features,
|
--no-default-features,
|
||||||
--features,
|
--features,
|
||||||
compare,
|
"compare,wasm_test",
|
||||||
--no-fail-fast,
|
--no-fail-fast,
|
||||||
--lib,
|
--lib,
|
||||||
--test,
|
--test,
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ spec:
|
|||||||
- name: command
|
- name: command
|
||||||
value: ["cargo", "fix"]
|
value: ["cargo", "fix"]
|
||||||
- name: args
|
- name: args
|
||||||
value: ["--allow-dirty"]
|
value: ["--all-targets", "--all-features", "--allow-dirty"]
|
||||||
- name: docker-image
|
- name: docker-image
|
||||||
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
||||||
- name: commit-changes
|
- name: commit-changes
|
||||||
|
|||||||
25
Cargo.toml
25
Cargo.toml
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "organic"
|
name = "organic"
|
||||||
version = "0.1.13"
|
version = "0.1.15"
|
||||||
authors = ["Tom Alexander <tom@fizz.buzz>"]
|
authors = ["Tom Alexander <tom@fizz.buzz>"]
|
||||||
description = "An org-mode parser."
|
description = "An org-mode parser."
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
@@ -39,17 +39,33 @@ path = "src/lib.rs"
|
|||||||
path = "src/bin_foreign_document_test.rs"
|
path = "src/bin_foreign_document_test.rs"
|
||||||
required-features = ["foreign_document_test"]
|
required-features = ["foreign_document_test"]
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "wasm"
|
||||||
|
path = "src/bin_wasm.rs"
|
||||||
|
required-features = ["wasm"]
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
# This bin exists for development purposes only. The real target of this crate is the library.
|
||||||
|
name = "wasm_test"
|
||||||
|
path = "src/bin_wasm_test.rs"
|
||||||
|
required-features = ["wasm_test"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
futures = { version = "0.3.28", optional = true }
|
futures = { version = "0.3.28", optional = true }
|
||||||
|
gloo-utils = "0.2.0"
|
||||||
nom = "7.1.1"
|
nom = "7.1.1"
|
||||||
opentelemetry = { version = "0.20.0", optional = true, default-features = false, features = ["trace", "rt-tokio"] }
|
opentelemetry = { version = "0.20.0", optional = true, default-features = false, features = ["trace", "rt-tokio"] }
|
||||||
opentelemetry-otlp = { version = "0.13.0", optional = true }
|
opentelemetry-otlp = { version = "0.13.0", optional = true }
|
||||||
opentelemetry-semantic-conventions = { version = "0.12.0", optional = true }
|
opentelemetry-semantic-conventions = { version = "0.12.0", optional = true }
|
||||||
|
serde = { version = "1.0.193", optional = true, features = ["derive"] }
|
||||||
|
serde-wasm-bindgen = { version = "0.6.3", optional = true }
|
||||||
|
serde_json = { version = "1.0.108", optional = true }
|
||||||
tokio = { version = "1.30.0", optional = true, default-features = false, features = ["rt", "rt-multi-thread"] }
|
tokio = { version = "1.30.0", optional = true, default-features = false, features = ["rt", "rt-multi-thread"] }
|
||||||
tracing = { version = "0.1.37", optional = true }
|
tracing = { version = "0.1.37", optional = true }
|
||||||
tracing-opentelemetry = { version = "0.20.0", optional = true }
|
tracing-opentelemetry = { version = "0.20.0", optional = true }
|
||||||
tracing-subscriber = { version = "0.3.17", optional = true, features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3.17", optional = true, features = ["env-filter"] }
|
||||||
walkdir = { version = "2.3.3", optional = true }
|
walkdir = { version = "2.3.3", optional = true }
|
||||||
|
wasm-bindgen = { version = "0.2.89", optional = true }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
walkdir = "2.3.3"
|
walkdir = "2.3.3"
|
||||||
@@ -60,6 +76,8 @@ compare = ["tokio/process", "tokio/macros"]
|
|||||||
foreign_document_test = ["compare", "dep:futures", "tokio/sync", "dep:walkdir", "tokio/process"]
|
foreign_document_test = ["compare", "dep:futures", "tokio/sync", "dep:walkdir", "tokio/process"]
|
||||||
tracing = ["dep:opentelemetry", "dep:opentelemetry-otlp", "dep:opentelemetry-semantic-conventions", "dep:tokio", "dep:tracing", "dep:tracing-opentelemetry", "dep:tracing-subscriber"]
|
tracing = ["dep:opentelemetry", "dep:opentelemetry-otlp", "dep:opentelemetry-semantic-conventions", "dep:tokio", "dep:tracing", "dep:tracing-opentelemetry", "dep:tracing-subscriber"]
|
||||||
event_count = []
|
event_count = []
|
||||||
|
wasm = ["dep:serde", "dep:wasm-bindgen", "dep:serde-wasm-bindgen"]
|
||||||
|
wasm_test = ["wasm", "dep:serde_json", "tokio/process", "tokio/macros"]
|
||||||
|
|
||||||
# Optimized build for any sort of release.
|
# Optimized build for any sort of release.
|
||||||
[profile.release-lto]
|
[profile.release-lto]
|
||||||
@@ -79,3 +97,8 @@ strip = "symbols"
|
|||||||
inherits = "release"
|
inherits = "release"
|
||||||
lto = true
|
lto = true
|
||||||
debug = true
|
debug = true
|
||||||
|
|
||||||
|
[profile.wasm]
|
||||||
|
inherits = "release"
|
||||||
|
lto = true
|
||||||
|
strip = true
|
||||||
|
|||||||
17
Makefile
17
Makefile
@@ -7,6 +7,7 @@ MAKEFLAGS += --no-builtin-rules
|
|||||||
TESTJOBS := 4
|
TESTJOBS := 4
|
||||||
OS:=$(shell uname -s)
|
OS:=$(shell uname -s)
|
||||||
RELEASEFLAGS :=
|
RELEASEFLAGS :=
|
||||||
|
WASMTARGET := bundler # or web
|
||||||
|
|
||||||
ifeq ($(OS),Linux)
|
ifeq ($(OS),Linux)
|
||||||
TESTJOBS:=$(shell nproc)
|
TESTJOBS:=$(shell nproc)
|
||||||
@@ -29,6 +30,11 @@ build:
|
|||||||
release:
|
release:
|
||||||
> cargo build --release $(RELEASEFLAGS)
|
> cargo build --release $(RELEASEFLAGS)
|
||||||
|
|
||||||
|
.PHONY: wasm
|
||||||
|
wasm:
|
||||||
|
> cargo build --target=wasm32-unknown-unknown --profile wasm --bin wasm --features wasm
|
||||||
|
> wasm-bindgen --target $(WASMTARGET) --out-dir target/wasm32-unknown-unknown/js target/wasm32-unknown-unknown/wasm/wasm.wasm
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
> cargo clean
|
> cargo clean
|
||||||
@@ -49,11 +55,20 @@ clippy:
|
|||||||
test:
|
test:
|
||||||
> cargo test --no-default-features --features compare --no-fail-fast --lib --test test_loader -- --test-threads $(TESTJOBS)
|
> cargo test --no-default-features --features compare --no-fail-fast --lib --test test_loader -- --test-threads $(TESTJOBS)
|
||||||
|
|
||||||
|
.PHONY: doc
|
||||||
|
doc:
|
||||||
|
> cargo doc --no-deps --open --lib --release --all-features
|
||||||
|
|
||||||
.PHONY: dockertest
|
.PHONY: dockertest
|
||||||
dockertest:
|
dockertest:
|
||||||
> $(MAKE) -C docker/organic_test
|
> $(MAKE) -C docker/organic_test
|
||||||
> docker run --init --rm -i -t --read-only -v "$$(readlink -f ./):/source:ro" --mount type=tmpfs,destination=/tmp --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source organic-test --no-default-features --features compare --no-fail-fast --lib --test test_loader -- --test-threads $(TESTJOBS)
|
> docker run --init --rm -i -t --read-only -v "$$(readlink -f ./):/source:ro" --mount type=tmpfs,destination=/tmp --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source organic-test --no-default-features --features compare --no-fail-fast --lib --test test_loader -- --test-threads $(TESTJOBS)
|
||||||
|
|
||||||
|
.PHONY: dockerwasmtest
|
||||||
|
dockerwasmtest:
|
||||||
|
> $(MAKE) -C docker/organic_test
|
||||||
|
> docker run --init --rm -i -t --read-only -v "$$(readlink -f ./):/source:ro" --mount type=tmpfs,destination=/tmp --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source organic-test --no-default-features --features compare,wasm_test --no-fail-fast --lib --test test_loader autogen_wasm_ -- --test-threads $(TESTJOBS)
|
||||||
|
|
||||||
.PHONY: buildtest
|
.PHONY: buildtest
|
||||||
buildtest:
|
buildtest:
|
||||||
> cargo build --no-default-features
|
> cargo build --no-default-features
|
||||||
@@ -62,6 +77,8 @@ buildtest:
|
|||||||
> cargo build --no-default-features --features compare,tracing
|
> cargo build --no-default-features --features compare,tracing
|
||||||
> cargo build --no-default-features --features compare,foreign_document_test
|
> cargo build --no-default-features --features compare,foreign_document_test
|
||||||
> cargo build --no-default-features --features compare,tracing,foreign_document_test
|
> cargo build --no-default-features --features compare,tracing,foreign_document_test
|
||||||
|
> cargo build --target wasm32-unknown-unknown --profile wasm --bin wasm --no-default-features --features wasm
|
||||||
|
> cargo build --bin wasm_test --no-default-features --features wasm_test
|
||||||
|
|
||||||
.PHONY: foreign_document_test
|
.PHONY: foreign_document_test
|
||||||
foreign_document_test:
|
foreign_document_test:
|
||||||
|
|||||||
@@ -10,17 +10,16 @@ Currently, Organic parses most documents the same as the official org-mode parse
|
|||||||
|
|
||||||
### Project Goals
|
### Project Goals
|
||||||
- We aim to provide perfect parity with the emacs org-mode parser. In that regard, any document that parses differently between Emacs and Organic is considered a bug.
|
- We aim to provide perfect parity with the emacs org-mode parser. In that regard, any document that parses differently between Emacs and Organic is considered a bug.
|
||||||
- The parser should have minimal dependencies. This should reduce effort w.r.t.: security audits, legal compliance, portability.
|
- The parser should have minimal dependencies.
|
||||||
- The parser should be usable everywhere. In the interest of getting org-mode used in as many places as possible, this parser should be usable by everyone everywhere. This means:
|
- The parser should be usable everywhere. In the interest of getting org used in as many places as possible, this parser should be usable by everyone everywhere. This means:
|
||||||
- It must have a permissive license.
|
- It must have a permissive license.
|
||||||
- We will investigate compiling to WASM. This is an important goal of the project and will definitely happen, but only after the parser has a more stable API.
|
- It compiles to both natively and to wasm.
|
||||||
- We will investigate compiling to a C library for native linking to other code. This is more of a maybe-goal for the project.
|
- We will investigate compiling to a C library for native linking to other code. This is more of a maybe-goal for the project.
|
||||||
### Project Non-Goals
|
### Project Non-Goals
|
||||||
- This project will not include an elisp engine since that would drastically increase the complexity of the code. Any features requiring an elisp engine will not be implemented (for example, Emacs supports embedded eval expressions in documents but this parser will never support that).
|
- This project will not include an elisp engine since that would drastically increase the complexity of the code. Any features requiring an elisp engine will not be implemented (for example, Emacs supports embedded eval expressions in documents but this parser will never support that).
|
||||||
- This project is exclusively an org-mode **parser**. This limits its scope to roughly the output of `(org-element-parse-buffer)`. It will not render org-mode documents in other formats like HTML or LaTeX.
|
- This project is exclusively an org-mode **parser**. This limits its scope to roughly the output of `(org-element-parse-buffer)`. It will not render org-mode documents in other formats like HTML or LaTeX.
|
||||||
### Project Maybe-Goals
|
### Project Maybe-Goals
|
||||||
- table.el support. Currently we support org-mode tables but org-mode also allows table.el tables. So far, their use in org-mode documents seems rather uncommon so this is a low-priority feature.
|
- table.el support. Currently we support org-mode tables but org-mode also allows table.el tables. So far, their use in org-mode documents seems rather uncommon so this is a low-priority feature.
|
||||||
- Document editing support. I do not anticipate any advanced editing features to make editing ergonomic, but it should be relatively easy to be able to parse an org-mode document and serialize it back into org-mode. This would enable cool features to be built on top of the library like auto-formatters. To accomplish this feature, We'd have to capture all of the various separators and whitespace that we are currently simply throwing away. This would add many additional fields to the parsed structs and it would add more noise to the parsers themselves, so I do not want to approach this feature until the parser is more complete since it would make modifications and refactoring more difficult.
|
|
||||||
### Supported Versions
|
### Supported Versions
|
||||||
This project targets the version of Emacs and Org-mode that are built into the [organic-test docker image](docker/organic_test/Dockerfile). This is newer than the version of Org-mode that shipped with Emacs 29.1. The parser itself does not depend on Emacs or Org-mode though, so this only matters for development purposes when running the automated tests that compare against upstream Org-mode.
|
This project targets the version of Emacs and Org-mode that are built into the [organic-test docker image](docker/organic_test/Dockerfile). This is newer than the version of Org-mode that shipped with Emacs 29.1. The parser itself does not depend on Emacs or Org-mode though, so this only matters for development purposes when running the automated tests that compare against upstream Org-mode.
|
||||||
|
|
||||||
|
|||||||
@@ -2,5 +2,6 @@ FROM rustlang/rust:nightly-alpine3.17
|
|||||||
|
|
||||||
RUN apk add --no-cache musl-dev
|
RUN apk add --no-cache musl-dev
|
||||||
RUN cargo install --locked --no-default-features --features ci-autoclean cargo-cache
|
RUN cargo install --locked --no-default-features --features ci-autoclean cargo-cache
|
||||||
|
RUN rustup target add wasm32-unknown-unknown
|
||||||
|
|
||||||
ENTRYPOINT ["cargo", "build"]
|
ENTRYPOINT ["cargo", "build"]
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ endif
|
|||||||
# NOTE: This target will write to folders underneath the git-root
|
# NOTE: This target will write to folders underneath the git-root
|
||||||
.PHONY: run
|
.PHONY: run
|
||||||
run: build
|
run: build
|
||||||
docker run --rm --init --read-only --mount type=tmpfs,destination=/tmp -v "$$(readlink -f ../../):/source:ro" --workdir=/source --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target $(IMAGE_NAME)
|
docker run --rm --init -t --read-only --mount type=tmpfs,destination=/tmp -v "$$(readlink -f ../../):/source:ro" --workdir=/source --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target $(IMAGE_NAME)
|
||||||
|
|
||||||
.PHONY: shell
|
.PHONY: shell
|
||||||
shell: build
|
shell: build
|
||||||
|
|||||||
76
scripts/build_all_feature_flag_combinations.bash
Executable file
76
scripts/build_all_feature_flag_combinations.bash
Executable file
@@ -0,0 +1,76 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# Time running a single parse without invoking a compare with emacs.
|
||||||
|
set -euo pipefail
|
||||||
|
IFS=$'\n\t'
|
||||||
|
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||||
|
|
||||||
|
: ${PROFILE:="debug"}
|
||||||
|
|
||||||
|
############## Setup #########################
|
||||||
|
|
||||||
|
function cleanup {
|
||||||
|
for f in "${folders[@]}"; do
|
||||||
|
log "Deleting $f"
|
||||||
|
rm -rf "$f"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
folders=()
|
||||||
|
for sig in EXIT INT QUIT HUP TERM; do
|
||||||
|
trap "set +e; cleanup" "$sig"
|
||||||
|
done
|
||||||
|
|
||||||
|
function die {
|
||||||
|
local status_code="$1"
|
||||||
|
shift
|
||||||
|
(>&2 echo "${@}")
|
||||||
|
exit "$status_code"
|
||||||
|
}
|
||||||
|
|
||||||
|
function log {
|
||||||
|
(>&2 echo "${@}")
|
||||||
|
}
|
||||||
|
|
||||||
|
############## Program #########################
|
||||||
|
|
||||||
|
function main {
|
||||||
|
if [ "$#" -gt 0 ]; then
|
||||||
|
export CARGO_TARGET_DIR="$1"
|
||||||
|
else
|
||||||
|
local work_directory=$(mktemp -d -t 'organic.XXXXXX')
|
||||||
|
folders+=("$work_directory")
|
||||||
|
export CARGO_TARGET_DIR="$work_directory"
|
||||||
|
fi
|
||||||
|
local features=(compare foreign_document_test tracing event_count wasm wasm_test)
|
||||||
|
ENABLED_FEATURES= for_each_combination "${features[@]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function for_each_combination {
|
||||||
|
local additional_flags=()
|
||||||
|
if [ "$PROFILE" = "dev" ] || [ "$PROFILE" = "debug" ]; then
|
||||||
|
PROFILE="debug"
|
||||||
|
else
|
||||||
|
additional_flags+=(--profile "$PROFILE")
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
local flag=$1
|
||||||
|
shift
|
||||||
|
|
||||||
|
if [ "$#" -gt 0 ]; then
|
||||||
|
ENABLED_FEATURES="$ENABLED_FEATURES" for_each_combination "${@}"
|
||||||
|
elif [ -z "$ENABLED_FEATURES" ]; then
|
||||||
|
(cd "$DIR/../" && printf "\n\n\n========== no features ==========\n\n\n" && set -x && cargo build "${additional_flags[@]}" --no-default-features)
|
||||||
|
else
|
||||||
|
(cd "$DIR/../" && printf "\n\n\n========== %s ==========\n\n\n" "${ENABLED_FEATURES:1}" && set -x && cargo build "${additional_flags[@]}" --no-default-features --features "${ENABLED_FEATURES:1}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
ENABLED_FEATURES="$ENABLED_FEATURES,$flag"
|
||||||
|
if [ "$#" -gt 0 ]; then
|
||||||
|
ENABLED_FEATURES="$ENABLED_FEATURES" for_each_combination "${@}"
|
||||||
|
else
|
||||||
|
(cd "$DIR/../" && printf "\n\n\n========== %s ==========\n\n\n" "${ENABLED_FEATURES:1}" && set -x && cargo build "${additional_flags[@]}" --no-default-features --features "${ENABLED_FEATURES:1}")
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
main "${@}"
|
||||||
111
scripts/run_docker_wasm_compare.bash
Executable file
111
scripts/run_docker_wasm_compare.bash
Executable file
@@ -0,0 +1,111 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
set -euo pipefail
|
||||||
|
IFS=$'\n\t'
|
||||||
|
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||||
|
|
||||||
|
: ${SHELL:="NO"} # or YES to launch a shell instead of running the test
|
||||||
|
: ${TRACE:="NO"} # or YES to send traces to jaeger
|
||||||
|
: ${BACKTRACE:="NO"} # or YES to print a rust backtrace when panicking
|
||||||
|
: ${NO_COLOR:=""} # Set to anything to disable color output
|
||||||
|
: ${PROFILE:="debug"}
|
||||||
|
|
||||||
|
REALPATH=$(command -v uu-realpath || command -v realpath)
|
||||||
|
MAKE=$(command -v gmake || command -v make)
|
||||||
|
|
||||||
|
############## Setup #########################
|
||||||
|
|
||||||
|
function die {
|
||||||
|
local status_code="$1"
|
||||||
|
shift
|
||||||
|
(>&2 echo "${@}")
|
||||||
|
exit "$status_code"
|
||||||
|
}
|
||||||
|
|
||||||
|
function log {
|
||||||
|
(>&2 echo "${@}")
|
||||||
|
}
|
||||||
|
|
||||||
|
############## Program #########################
|
||||||
|
|
||||||
|
function main {
|
||||||
|
build_container
|
||||||
|
launch_container "${@}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function build_container {
|
||||||
|
$MAKE -C "$DIR/../docker/organic_test"
|
||||||
|
}
|
||||||
|
|
||||||
|
function launch_container {
|
||||||
|
local additional_flags=()
|
||||||
|
local features=(wasm_test)
|
||||||
|
|
||||||
|
if [ "$NO_COLOR" != "" ]; then
|
||||||
|
additional_flags+=(--env "NO_COLOR=$NO_COLOR")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$TRACE" = "YES" ]; then
|
||||||
|
# We use the host network so it can talk to jaeger hosted at 127.0.0.1
|
||||||
|
additional_flags+=(--network=host --env RUST_LOG=debug)
|
||||||
|
features+=(tracing)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$SHELL" != "YES" ]; then
|
||||||
|
additional_flags+=(--read-only)
|
||||||
|
else
|
||||||
|
additional_flags+=(-t)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$BACKTRACE" = "YES" ]; then
|
||||||
|
additional_flags+=(--env RUST_BACKTRACE=full)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$SHELL" = "YES" ]; then
|
||||||
|
exec docker run "${additional_flags[@]}" --init --rm -i --mount type=tmpfs,destination=/tmp -v "/:/input:ro" -v "$($REALPATH "$DIR/../"):/source:ro" --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source --entrypoint "" organic-test /bin/sh
|
||||||
|
fi
|
||||||
|
|
||||||
|
local features_joined
|
||||||
|
features_joined=$(IFS=","; echo "${features[*]}")
|
||||||
|
|
||||||
|
local build_flags=()
|
||||||
|
if [ "$PROFILE" = "dev" ] || [ "$PROFILE" = "debug" ]; then
|
||||||
|
PROFILE="debug"
|
||||||
|
else
|
||||||
|
build_flags+=(--profile "$PROFILE")
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
if [ $# -gt 0 ]; then
|
||||||
|
# If we passed in args, we need to forward them along
|
||||||
|
for path in "${@}"; do
|
||||||
|
local full_path
|
||||||
|
full_path=$($REALPATH "$path")
|
||||||
|
init_script=$(cat <<EOF
|
||||||
|
set -euo pipefail
|
||||||
|
IFS=\$'\n\t'
|
||||||
|
|
||||||
|
cargo build --bin wasm_test --no-default-features --features "$features_joined" ${build_flags[@]}
|
||||||
|
exec /target/${PROFILE}/wasm_test "/input${full_path}"
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
docker run "${additional_flags[@]}" --init --rm -i --mount type=tmpfs,destination=/tmp -v "/:/input:ro" -v "$($REALPATH "$DIR/../"):/source:ro" --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source --entrypoint "" organic-test sh -c "$init_script"
|
||||||
|
done
|
||||||
|
else
|
||||||
|
local current_directory init_script
|
||||||
|
current_directory=$(pwd)
|
||||||
|
init_script=$(cat <<EOF
|
||||||
|
set -euo pipefail
|
||||||
|
IFS=\$'\n\t'
|
||||||
|
|
||||||
|
cargo build --bin wasm_test --no-default-features --features "$features_joined" ${build_flags[@]}
|
||||||
|
cd /input${current_directory}
|
||||||
|
exec /target/${PROFILE}/wasm_test
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
docker run "${additional_flags[@]}" --init --rm -i --mount type=tmpfs,destination=/tmp -v "/:/input:ro" -v "$($REALPATH "$DIR/../"):/source:ro" --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source --entrypoint "" organic-test sh -c "$init_script"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
main "${@}"
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#![feature(exit_status_error)]
|
||||||
#![feature(round_char_boundary)]
|
#![feature(round_char_boundary)]
|
||||||
#![feature(exact_size_is_empty)]
|
#![feature(exact_size_is_empty)]
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
|||||||
10
src/bin_wasm.rs
Normal file
10
src/bin_wasm.rs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
use wasm_bindgen::prelude::wasm_bindgen;
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn parse_org(org_contents: &str) -> wasm_bindgen::JsValue {
|
||||||
|
organic::wasm_cli::parse_org(org_contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
62
src/bin_wasm_test.rs
Normal file
62
src/bin_wasm_test.rs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
#![feature(exact_size_is_empty)]
|
||||||
|
#![feature(exit_status_error)]
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
use organic::wasm_test::wasm_run_anonymous_compare;
|
||||||
|
use organic::wasm_test::wasm_run_compare_on_file;
|
||||||
|
|
||||||
|
#[cfg(feature = "tracing")]
|
||||||
|
use crate::init_tracing::init_telemetry;
|
||||||
|
#[cfg(feature = "tracing")]
|
||||||
|
use crate::init_tracing::shutdown_telemetry;
|
||||||
|
#[cfg(feature = "tracing")]
|
||||||
|
mod init_tracing;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "tracing"))]
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let rt = tokio::runtime::Runtime::new()?;
|
||||||
|
rt.block_on(async {
|
||||||
|
let main_body_result = main_body().await;
|
||||||
|
main_body_result
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "tracing")]
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let rt = tokio::runtime::Runtime::new()?;
|
||||||
|
rt.block_on(async {
|
||||||
|
init_telemetry()?;
|
||||||
|
let main_body_result = main_body().await;
|
||||||
|
shutdown_telemetry()?;
|
||||||
|
main_body_result
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
async fn main_body() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let args = std::env::args().skip(1);
|
||||||
|
if args.is_empty() {
|
||||||
|
let org_contents = read_stdin_to_string()?;
|
||||||
|
if wasm_run_anonymous_compare(org_contents).await? {
|
||||||
|
} else {
|
||||||
|
Err("Diff results do not match.")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
for arg in args {
|
||||||
|
if wasm_run_compare_on_file(arg).await? {
|
||||||
|
} else {
|
||||||
|
Err("Diff results do not match.")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_stdin_to_string() -> Result<String, Box<dyn std::error::Error>> {
|
||||||
|
let mut stdin_contents = String::new();
|
||||||
|
std::io::stdin()
|
||||||
|
.lock()
|
||||||
|
.read_to_string(&mut stdin_contents)?;
|
||||||
|
Ok(stdin_contents)
|
||||||
|
}
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use crate::compare::diff::compare_document;
|
use crate::compare::diff::compare_document;
|
||||||
use crate::compare::diff::DiffResult;
|
|
||||||
use crate::compare::parse::emacs_parse_anonymous_org_document;
|
|
||||||
use crate::compare::parse::emacs_parse_file_org_document;
|
|
||||||
use crate::compare::parse::get_emacs_version;
|
|
||||||
use crate::compare::parse::get_org_mode_version;
|
|
||||||
use crate::compare::sexp::sexp;
|
|
||||||
use crate::context::GlobalSettings;
|
use crate::context::GlobalSettings;
|
||||||
use crate::context::LocalFileAccessInterface;
|
use crate::context::LocalFileAccessInterface;
|
||||||
use crate::parser::parse_file_with_settings;
|
use crate::parser::parse_file_with_settings;
|
||||||
use crate::parser::parse_with_settings;
|
use crate::parser::parse_with_settings;
|
||||||
|
use crate::util::cli::emacs_parse_anonymous_org_document;
|
||||||
|
use crate::util::cli::emacs_parse_file_org_document;
|
||||||
|
use crate::util::cli::print_versions;
|
||||||
|
use crate::util::elisp::sexp;
|
||||||
|
use crate::util::terminal::foreground_color;
|
||||||
|
use crate::util::terminal::reset_color;
|
||||||
|
|
||||||
pub async fn run_anonymous_compare<P: AsRef<str>>(
|
pub async fn run_anonymous_compare<P: AsRef<str>>(
|
||||||
org_contents: P,
|
org_contents: P,
|
||||||
@@ -68,8 +68,8 @@ pub async fn run_anonymous_compare_with_settings<'g, 's, P: AsRef<str>>(
|
|||||||
} else if !silent {
|
} else if !silent {
|
||||||
println!(
|
println!(
|
||||||
"{color}Entire document passes.{reset}",
|
"{color}Entire document passes.{reset}",
|
||||||
color = DiffResult::foreground_color(0, 255, 0),
|
color = foreground_color(0, 255, 0),
|
||||||
reset = DiffResult::reset_color(),
|
reset = reset_color(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,19 +121,10 @@ pub async fn run_compare_on_file_with_settings<'g, 's, P: AsRef<Path>>(
|
|||||||
} else if !silent {
|
} else if !silent {
|
||||||
println!(
|
println!(
|
||||||
"{color}Entire document passes.{reset}",
|
"{color}Entire document passes.{reset}",
|
||||||
color = DiffResult::foreground_color(0, 255, 0),
|
color = foreground_color(0, 255, 0),
|
||||||
reset = DiffResult::reset_color(),
|
reset = reset_color(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn print_versions() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
eprintln!("Using emacs version: {}", get_emacs_version().await?.trim());
|
|
||||||
eprintln!(
|
|
||||||
"Using org-mode version: {}",
|
|
||||||
get_org_mode_version().await?.trim()
|
|
||||||
);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ use super::diff::artificial_owned_diff_scope;
|
|||||||
use super::diff::compare_ast_node;
|
use super::diff::compare_ast_node;
|
||||||
use super::diff::DiffEntry;
|
use super::diff::DiffEntry;
|
||||||
use super::diff::DiffStatus;
|
use super::diff::DiffStatus;
|
||||||
use super::sexp::unquote;
|
|
||||||
use super::sexp::Token;
|
|
||||||
use super::util::get_property;
|
use super::util::get_property;
|
||||||
use super::util::get_property_numeric;
|
use super::util::get_property_numeric;
|
||||||
use super::util::get_property_quoted_string;
|
use super::util::get_property_quoted_string;
|
||||||
@@ -20,6 +18,8 @@ use crate::types::CharOffsetInLine;
|
|||||||
use crate::types::LineNumber;
|
use crate::types::LineNumber;
|
||||||
use crate::types::RetainLabels;
|
use crate::types::RetainLabels;
|
||||||
use crate::types::SwitchNumberLines;
|
use crate::types::SwitchNumberLines;
|
||||||
|
use crate::util::elisp::unquote;
|
||||||
|
use crate::util::elisp::Token;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum EmacsField<'s> {
|
pub(crate) enum EmacsField<'s> {
|
||||||
|
|||||||
@@ -16,10 +16,6 @@ use super::compare_field::compare_property_retain_labels;
|
|||||||
use super::compare_field::compare_property_set_of_quoted_string;
|
use super::compare_field::compare_property_set_of_quoted_string;
|
||||||
use super::compare_field::compare_property_single_ast_node;
|
use super::compare_field::compare_property_single_ast_node;
|
||||||
use super::compare_field::compare_property_unquoted_atom;
|
use super::compare_field::compare_property_unquoted_atom;
|
||||||
use super::elisp_fact::ElispFact;
|
|
||||||
use super::elisp_fact::GetElispFact;
|
|
||||||
use super::sexp::unquote;
|
|
||||||
use super::sexp::Token;
|
|
||||||
use super::util::affiliated_keywords_names;
|
use super::util::affiliated_keywords_names;
|
||||||
use super::util::assert_no_children;
|
use super::util::assert_no_children;
|
||||||
use super::util::compare_additional_properties;
|
use super::util::compare_additional_properties;
|
||||||
@@ -109,6 +105,12 @@ use crate::types::Verbatim;
|
|||||||
use crate::types::VerseBlock;
|
use crate::types::VerseBlock;
|
||||||
use crate::types::WarningDelayType;
|
use crate::types::WarningDelayType;
|
||||||
use crate::types::Year;
|
use crate::types::Year;
|
||||||
|
use crate::util::elisp::unquote;
|
||||||
|
use crate::util::elisp::Token;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::util::elisp_fact::GetElispFact;
|
||||||
|
use crate::util::terminal::foreground_color;
|
||||||
|
use crate::util::terminal::reset_color;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum DiffEntry<'b, 's> {
|
pub enum DiffEntry<'b, 's> {
|
||||||
@@ -200,21 +202,21 @@ impl<'b, 's> DiffResult<'b, 's> {
|
|||||||
if self.has_bad_children() {
|
if self.has_bad_children() {
|
||||||
format!(
|
format!(
|
||||||
"{color}BADCHILD{reset}",
|
"{color}BADCHILD{reset}",
|
||||||
color = DiffResult::foreground_color(255, 255, 0),
|
color = foreground_color(255, 255, 0),
|
||||||
reset = DiffResult::reset_color(),
|
reset = reset_color(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
format!(
|
format!(
|
||||||
"{color}GOOD{reset}",
|
"{color}GOOD{reset}",
|
||||||
color = DiffResult::foreground_color(0, 255, 0),
|
color = foreground_color(0, 255, 0),
|
||||||
reset = DiffResult::reset_color(),
|
reset = reset_color(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DiffStatus::Bad => format!(
|
DiffStatus::Bad => format!(
|
||||||
"{color}BAD{reset}",
|
"{color}BAD{reset}",
|
||||||
color = DiffResult::foreground_color(255, 0, 0),
|
color = foreground_color(255, 0, 0),
|
||||||
reset = DiffResult::reset_color(),
|
reset = reset_color(),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -239,45 +241,6 @@ impl<'b, 's> DiffResult<'b, 's> {
|
|||||||
.iter()
|
.iter()
|
||||||
.any(|child| child.is_immediately_bad() || child.has_bad_children())
|
.any(|child| child.is_immediately_bad() || child.has_bad_children())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn foreground_color(red: u8, green: u8, blue: u8) -> String {
|
|
||||||
if DiffResult::should_use_color() {
|
|
||||||
format!(
|
|
||||||
"\x1b[38;2;{red};{green};{blue}m",
|
|
||||||
red = red,
|
|
||||||
green = green,
|
|
||||||
blue = blue
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
String::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub(crate) fn background_color(red: u8, green: u8, blue: u8) -> String {
|
|
||||||
if DiffResult::should_use_color() {
|
|
||||||
format!(
|
|
||||||
"\x1b[48;2;{red};{green};{blue}m",
|
|
||||||
red = red,
|
|
||||||
green = green,
|
|
||||||
blue = blue
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
String::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn reset_color() -> &'static str {
|
|
||||||
if DiffResult::should_use_color() {
|
|
||||||
"\x1b[0m"
|
|
||||||
} else {
|
|
||||||
""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn should_use_color() -> bool {
|
|
||||||
!std::env::var("NO_COLOR").is_ok_and(|val| !val.is_empty())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'b, 's> DiffLayer<'b, 's> {
|
impl<'b, 's> DiffLayer<'b, 's> {
|
||||||
@@ -295,14 +258,14 @@ impl<'b, 's> DiffLayer<'b, 's> {
|
|||||||
let status_text = if self.has_bad_children() {
|
let status_text = if self.has_bad_children() {
|
||||||
format!(
|
format!(
|
||||||
"{color}BADCHILD{reset}",
|
"{color}BADCHILD{reset}",
|
||||||
color = DiffResult::foreground_color(255, 255, 0),
|
color = foreground_color(255, 255, 0),
|
||||||
reset = DiffResult::reset_color(),
|
reset = reset_color(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
format!(
|
format!(
|
||||||
"{color}GOOD{reset}",
|
"{color}GOOD{reset}",
|
||||||
color = DiffResult::foreground_color(0, 255, 0),
|
color = foreground_color(0, 255, 0),
|
||||||
reset = DiffResult::reset_color(),
|
reset = reset_color(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
println!(
|
println!(
|
||||||
|
|||||||
@@ -2,10 +2,7 @@
|
|||||||
mod compare;
|
mod compare;
|
||||||
mod compare_field;
|
mod compare_field;
|
||||||
mod diff;
|
mod diff;
|
||||||
mod elisp_fact;
|
|
||||||
mod macros;
|
mod macros;
|
||||||
mod parse;
|
|
||||||
mod sexp;
|
|
||||||
mod util;
|
mod util;
|
||||||
pub use compare::run_anonymous_compare;
|
pub use compare::run_anonymous_compare;
|
||||||
pub use compare::run_anonymous_compare_with_settings;
|
pub use compare::run_anonymous_compare_with_settings;
|
||||||
|
|||||||
@@ -8,14 +8,15 @@ use super::compare_field::compare_property_quoted_string;
|
|||||||
use super::compare_field::ComparePropertiesResult;
|
use super::compare_field::ComparePropertiesResult;
|
||||||
use super::diff::DiffEntry;
|
use super::diff::DiffEntry;
|
||||||
use super::diff::DiffStatus;
|
use super::diff::DiffStatus;
|
||||||
use super::elisp_fact::GetElispFact;
|
|
||||||
use super::sexp::Token;
|
|
||||||
use crate::compare::diff::compare_ast_node;
|
use crate::compare::diff::compare_ast_node;
|
||||||
use crate::compare::sexp::unquote;
|
|
||||||
use crate::types::AffiliatedKeywordValue;
|
use crate::types::AffiliatedKeywordValue;
|
||||||
use crate::types::AstNode;
|
use crate::types::AstNode;
|
||||||
use crate::types::GetAffiliatedKeywords;
|
use crate::types::GetAffiliatedKeywords;
|
||||||
use crate::types::StandardProperties;
|
use crate::types::StandardProperties;
|
||||||
|
use crate::util::elisp::get_emacs_standard_properties;
|
||||||
|
use crate::util::elisp::unquote;
|
||||||
|
use crate::util::elisp::Token;
|
||||||
|
use crate::util::elisp_fact::GetElispFact;
|
||||||
|
|
||||||
/// Check if the child string slice is a slice of the parent string slice.
|
/// Check if the child string slice is a slice of the parent string slice.
|
||||||
fn is_slice_of(parent: &str, child: &str) -> bool {
|
fn is_slice_of(parent: &str, child: &str) -> bool {
|
||||||
@@ -145,80 +146,6 @@ fn assert_post_blank<'b, 's, S: StandardProperties<'s> + ?Sized>(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
struct EmacsStandardProperties {
|
|
||||||
begin: Option<usize>,
|
|
||||||
#[allow(dead_code)]
|
|
||||||
post_affiliated: Option<usize>,
|
|
||||||
#[allow(dead_code)]
|
|
||||||
contents_begin: Option<usize>,
|
|
||||||
#[allow(dead_code)]
|
|
||||||
contents_end: Option<usize>,
|
|
||||||
end: Option<usize>,
|
|
||||||
#[allow(dead_code)]
|
|
||||||
post_blank: Option<usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_emacs_standard_properties(
|
|
||||||
emacs: &Token<'_>,
|
|
||||||
) -> Result<EmacsStandardProperties, Box<dyn std::error::Error>> {
|
|
||||||
let children = emacs.as_list()?;
|
|
||||||
let attributes_child = children.get(1).ok_or("Should have an attributes child.")?;
|
|
||||||
let attributes_map = attributes_child.as_map()?;
|
|
||||||
let standard_properties = attributes_map.get(":standard-properties");
|
|
||||||
Ok(if standard_properties.is_some() {
|
|
||||||
let mut std_props = standard_properties
|
|
||||||
.expect("if statement proves its Some")
|
|
||||||
.as_vector()?
|
|
||||||
.iter();
|
|
||||||
let begin = maybe_token_to_usize(std_props.next())?;
|
|
||||||
let post_affiliated = maybe_token_to_usize(std_props.next())?;
|
|
||||||
let contents_begin = maybe_token_to_usize(std_props.next())?;
|
|
||||||
let contents_end = maybe_token_to_usize(std_props.next())?;
|
|
||||||
let end = maybe_token_to_usize(std_props.next())?;
|
|
||||||
let post_blank = maybe_token_to_usize(std_props.next())?;
|
|
||||||
EmacsStandardProperties {
|
|
||||||
begin,
|
|
||||||
post_affiliated,
|
|
||||||
contents_begin,
|
|
||||||
contents_end,
|
|
||||||
end,
|
|
||||||
post_blank,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let begin = maybe_token_to_usize(attributes_map.get(":begin").copied())?;
|
|
||||||
let end = maybe_token_to_usize(attributes_map.get(":end").copied())?;
|
|
||||||
let contents_begin = maybe_token_to_usize(attributes_map.get(":contents-begin").copied())?;
|
|
||||||
let contents_end = maybe_token_to_usize(attributes_map.get(":contents-end").copied())?;
|
|
||||||
let post_blank = maybe_token_to_usize(attributes_map.get(":post-blank").copied())?;
|
|
||||||
let post_affiliated =
|
|
||||||
maybe_token_to_usize(attributes_map.get(":post-affiliated").copied())?;
|
|
||||||
EmacsStandardProperties {
|
|
||||||
begin,
|
|
||||||
post_affiliated,
|
|
||||||
contents_begin,
|
|
||||||
contents_end,
|
|
||||||
end,
|
|
||||||
post_blank,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn maybe_token_to_usize(
|
|
||||||
token: Option<&Token<'_>>,
|
|
||||||
) -> Result<Option<usize>, Box<dyn std::error::Error>> {
|
|
||||||
Ok(token
|
|
||||||
.map(|token| token.as_atom())
|
|
||||||
.map_or(Ok(None), |r| r.map(Some))?
|
|
||||||
.and_then(|val| {
|
|
||||||
if val == "nil" {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(val.parse::<usize>())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.map_or(Ok(None), |r| r.map(Some))?)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a named property from the emacs token.
|
/// Get a named property from the emacs token.
|
||||||
///
|
///
|
||||||
/// Returns Ok(None) if value is nil or absent.
|
/// Returns Ok(None) if value is nil or absent.
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ pub(crate) fn record_event(event_type: EventType, input: OrgSource<'_>) {
|
|||||||
*db.entry(key).or_insert(0) += 1;
|
*db.entry(key).or_insert(0) += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn report(original_document: &str) {
|
pub(crate) fn report(original_document: &str) {
|
||||||
let mut db = GLOBAL_DATA.lock().unwrap();
|
let mut db = GLOBAL_DATA.lock().unwrap();
|
||||||
let db = db.get_or_insert_with(HashMap::new);
|
let db = db.get_or_insert_with(HashMap::new);
|
||||||
let mut results: Vec<_> = db.iter().collect();
|
let mut results: Vec<_> = db.iter().collect();
|
||||||
|
|||||||
@@ -2,5 +2,5 @@ mod database;
|
|||||||
mod event_type;
|
mod event_type;
|
||||||
|
|
||||||
pub(crate) use database::record_event;
|
pub(crate) use database::record_event;
|
||||||
pub use database::report;
|
pub(crate) use database::report;
|
||||||
pub(crate) use event_type::EventType;
|
pub(crate) use event_type::EventType;
|
||||||
|
|||||||
13
src/lib.rs
13
src/lib.rs
@@ -3,6 +3,8 @@
|
|||||||
#![feature(path_file_prefix)]
|
#![feature(path_file_prefix)]
|
||||||
#![feature(is_sorted)]
|
#![feature(is_sorted)]
|
||||||
#![feature(test)]
|
#![feature(test)]
|
||||||
|
#![feature(iter_intersperse)]
|
||||||
|
#![feature(exact_size_is_empty)]
|
||||||
// TODO: #![warn(missing_docs)]
|
// TODO: #![warn(missing_docs)]
|
||||||
#![allow(clippy::bool_assert_comparison)] // Sometimes you want the long form because its easier to see at a glance.
|
#![allow(clippy::bool_assert_comparison)] // Sometimes you want the long form because its easier to see at a glance.
|
||||||
|
|
||||||
@@ -10,11 +12,20 @@ extern crate test;
|
|||||||
|
|
||||||
#[cfg(feature = "compare")]
|
#[cfg(feature = "compare")]
|
||||||
pub mod compare;
|
pub mod compare;
|
||||||
|
pub mod parse_cli;
|
||||||
|
#[cfg(any(feature = "compare", feature = "wasm", feature = "wasm_test"))]
|
||||||
|
mod util;
|
||||||
|
#[cfg(any(feature = "wasm", feature = "wasm_test"))]
|
||||||
|
mod wasm;
|
||||||
|
#[cfg(any(feature = "wasm", feature = "wasm_test"))]
|
||||||
|
pub mod wasm_cli;
|
||||||
|
#[cfg(feature = "wasm_test")]
|
||||||
|
pub mod wasm_test;
|
||||||
|
|
||||||
mod context;
|
mod context;
|
||||||
mod error;
|
mod error;
|
||||||
#[cfg(feature = "event_count")]
|
#[cfg(feature = "event_count")]
|
||||||
pub mod event_count;
|
mod event_count;
|
||||||
mod iter;
|
mod iter;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|||||||
62
src/main.rs
62
src/main.rs
@@ -1,12 +1,4 @@
|
|||||||
#![feature(round_char_boundary)]
|
use organic::parse_cli::main_body;
|
||||||
#![feature(exact_size_is_empty)]
|
|
||||||
use std::io::Read;
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use ::organic::parser::parse;
|
|
||||||
use organic::parser::parse_with_settings;
|
|
||||||
use organic::settings::GlobalSettings;
|
|
||||||
use organic::settings::LocalFileAccessInterface;
|
|
||||||
|
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
use crate::init_tracing::init_telemetry;
|
use crate::init_tracing::init_telemetry;
|
||||||
@@ -30,55 +22,3 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
main_body_result
|
main_body_result
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
||||||
fn main_body() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let args = std::env::args().skip(1);
|
|
||||||
if args.is_empty() {
|
|
||||||
let org_contents = read_stdin_to_string()?;
|
|
||||||
run_anonymous_parse(org_contents)
|
|
||||||
} else {
|
|
||||||
for arg in args {
|
|
||||||
run_parse_on_file(arg)?
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_stdin_to_string() -> Result<String, Box<dyn std::error::Error>> {
|
|
||||||
let mut stdin_contents = String::new();
|
|
||||||
std::io::stdin()
|
|
||||||
.lock()
|
|
||||||
.read_to_string(&mut stdin_contents)?;
|
|
||||||
Ok(stdin_contents)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_anonymous_parse<P: AsRef<str>>(org_contents: P) -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let org_contents = org_contents.as_ref();
|
|
||||||
let rust_parsed = parse(org_contents)?;
|
|
||||||
println!("{:#?}", rust_parsed);
|
|
||||||
#[cfg(feature = "event_count")]
|
|
||||||
organic::event_count::report(org_contents);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_parse_on_file<P: AsRef<Path>>(org_path: P) -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let org_path = org_path.as_ref();
|
|
||||||
let parent_directory = org_path
|
|
||||||
.parent()
|
|
||||||
.ok_or("Should be contained inside a directory.")?;
|
|
||||||
let org_contents = std::fs::read_to_string(org_path)?;
|
|
||||||
let org_contents = org_contents.as_str();
|
|
||||||
let file_access_interface = LocalFileAccessInterface {
|
|
||||||
working_directory: Some(parent_directory.to_path_buf()),
|
|
||||||
};
|
|
||||||
let global_settings = GlobalSettings {
|
|
||||||
file_access: &file_access_interface,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
let rust_parsed = parse_with_settings(org_contents, &global_settings)?;
|
|
||||||
println!("{:#?}", rust_parsed);
|
|
||||||
#[cfg(feature = "event_count")]
|
|
||||||
organic::event_count::report(org_contents);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|||||||
59
src/parse_cli/mod.rs
Normal file
59
src/parse_cli/mod.rs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
use std::io::Read;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use crate::parser::parse;
|
||||||
|
use crate::parser::parse_with_settings;
|
||||||
|
use crate::settings::GlobalSettings;
|
||||||
|
use crate::settings::LocalFileAccessInterface;
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
pub fn main_body() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let args = std::env::args().skip(1);
|
||||||
|
if args.is_empty() {
|
||||||
|
let org_contents = read_stdin_to_string()?;
|
||||||
|
run_anonymous_parse(org_contents)
|
||||||
|
} else {
|
||||||
|
for arg in args {
|
||||||
|
run_parse_on_file(arg)?
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_stdin_to_string() -> Result<String, Box<dyn std::error::Error>> {
|
||||||
|
let mut stdin_contents = String::new();
|
||||||
|
std::io::stdin()
|
||||||
|
.lock()
|
||||||
|
.read_to_string(&mut stdin_contents)?;
|
||||||
|
Ok(stdin_contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_anonymous_parse<P: AsRef<str>>(org_contents: P) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let org_contents = org_contents.as_ref();
|
||||||
|
let rust_parsed = parse(org_contents)?;
|
||||||
|
println!("{:#?}", rust_parsed);
|
||||||
|
#[cfg(feature = "event_count")]
|
||||||
|
crate::event_count::report(org_contents);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_parse_on_file<P: AsRef<Path>>(org_path: P) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let org_path = org_path.as_ref();
|
||||||
|
let parent_directory = org_path
|
||||||
|
.parent()
|
||||||
|
.ok_or("Should be contained inside a directory.")?;
|
||||||
|
let org_contents = std::fs::read_to_string(org_path)?;
|
||||||
|
let org_contents = org_contents.as_str();
|
||||||
|
let file_access_interface = LocalFileAccessInterface {
|
||||||
|
working_directory: Some(parent_directory.to_path_buf()),
|
||||||
|
};
|
||||||
|
let global_settings = GlobalSettings {
|
||||||
|
file_access: &file_access_interface,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let rust_parsed = parse_with_settings(org_contents, &global_settings)?;
|
||||||
|
println!("{:#?}", rust_parsed);
|
||||||
|
#[cfg(feature = "event_count")]
|
||||||
|
crate::event_count::report(org_contents);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -332,19 +332,19 @@ pub type HourInner = u8;
|
|||||||
pub type MinuteInner = u8;
|
pub type MinuteInner = u8;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Year(YearInner);
|
pub struct Year(pub YearInner);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Month(MonthInner);
|
pub struct Month(pub MonthInner);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct DayOfMonth(DayOfMonthInner);
|
pub struct DayOfMonth(pub DayOfMonthInner);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Hour(HourInner);
|
pub struct Hour(pub HourInner);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Minute(MinuteInner);
|
pub struct Minute(pub MinuteInner);
|
||||||
|
|
||||||
impl Year {
|
impl Year {
|
||||||
// TODO: Make a real error type instead of a boxed any error.
|
// TODO: Make a real error type instead of a boxed any error.
|
||||||
|
|||||||
@@ -2,28 +2,53 @@ use std::path::Path;
|
|||||||
|
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
use crate::context::HeadlineLevelFilter;
|
|
||||||
use crate::settings::GlobalSettings;
|
use crate::settings::GlobalSettings;
|
||||||
|
use crate::settings::HeadlineLevelFilter;
|
||||||
|
|
||||||
/// Generate elisp to configure org-mode parsing settings
|
pub async fn print_versions() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
///
|
eprintln!("Using emacs version: {}", get_emacs_version().await?.trim());
|
||||||
/// Currently only org-list-allow-alphabetical is supported.
|
eprintln!(
|
||||||
fn global_settings_elisp(global_settings: &GlobalSettings) -> String {
|
"Using org-mode version: {}",
|
||||||
// This string concatenation is wildly inefficient but its only called in tests 🤷.
|
get_org_mode_version().await?.trim()
|
||||||
let mut ret = "".to_owned();
|
);
|
||||||
if global_settings.list_allow_alphabetical {
|
Ok(())
|
||||||
ret += "(setq org-list-allow-alphabetical t)\n"
|
|
||||||
}
|
}
|
||||||
if global_settings.tab_width != crate::settings::DEFAULT_TAB_WIDTH {
|
|
||||||
ret += format!("(setq-default tab-width {})", global_settings.tab_width).as_str();
|
pub(crate) async fn get_emacs_version() -> Result<String, Box<dyn std::error::Error>> {
|
||||||
|
let elisp_script = r#"(progn
|
||||||
|
(message "%s" (version))
|
||||||
|
)"#;
|
||||||
|
let mut cmd = Command::new("emacs");
|
||||||
|
let cmd = cmd
|
||||||
|
.arg("-q")
|
||||||
|
.arg("--no-site-file")
|
||||||
|
.arg("--no-splash")
|
||||||
|
.arg("--batch")
|
||||||
|
.arg("--eval")
|
||||||
|
.arg(elisp_script);
|
||||||
|
|
||||||
|
let out = cmd.output().await?;
|
||||||
|
out.status.exit_ok()?;
|
||||||
|
Ok(String::from_utf8(out.stderr)?)
|
||||||
}
|
}
|
||||||
if global_settings.odd_levels_only != HeadlineLevelFilter::default() {
|
|
||||||
ret += match global_settings.odd_levels_only {
|
pub(crate) async fn get_org_mode_version() -> Result<String, Box<dyn std::error::Error>> {
|
||||||
HeadlineLevelFilter::Odd => "(setq org-odd-levels-only t)\n",
|
let elisp_script = r#"(progn
|
||||||
HeadlineLevelFilter::OddEven => "(setq org-odd-levels-only nil)\n",
|
(org-mode)
|
||||||
};
|
(message "%s" (org-version nil t nil))
|
||||||
}
|
)"#;
|
||||||
ret
|
let mut cmd = Command::new("emacs");
|
||||||
|
let cmd = cmd
|
||||||
|
.arg("-q")
|
||||||
|
.arg("--no-site-file")
|
||||||
|
.arg("--no-splash")
|
||||||
|
.arg("--batch")
|
||||||
|
.arg("--eval")
|
||||||
|
.arg(elisp_script);
|
||||||
|
|
||||||
|
let out = cmd.output().await?;
|
||||||
|
out.status.exit_ok()?;
|
||||||
|
Ok(String::from_utf8(out.stderr)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn emacs_parse_anonymous_org_document<'g, 's, C>(
|
pub(crate) async fn emacs_parse_anonymous_org_document<'g, 's, C>(
|
||||||
@@ -144,39 +169,23 @@ where
|
|||||||
output
|
output
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_emacs_version() -> Result<String, Box<dyn std::error::Error>> {
|
/// Generate elisp to configure org-mode parsing settings
|
||||||
let elisp_script = r#"(progn
|
///
|
||||||
(message "%s" (version))
|
/// Currently only org-list-allow-alphabetical is supported.
|
||||||
)"#;
|
fn global_settings_elisp(global_settings: &GlobalSettings) -> String {
|
||||||
let mut cmd = Command::new("emacs");
|
// This string concatenation is wildly inefficient but its only called in tests 🤷.
|
||||||
let cmd = cmd
|
let mut ret = "".to_owned();
|
||||||
.arg("-q")
|
if global_settings.list_allow_alphabetical {
|
||||||
.arg("--no-site-file")
|
ret += "(setq org-list-allow-alphabetical t)\n"
|
||||||
.arg("--no-splash")
|
|
||||||
.arg("--batch")
|
|
||||||
.arg("--eval")
|
|
||||||
.arg(elisp_script);
|
|
||||||
|
|
||||||
let out = cmd.output().await?;
|
|
||||||
out.status.exit_ok()?;
|
|
||||||
Ok(String::from_utf8(out.stderr)?)
|
|
||||||
}
|
}
|
||||||
|
if global_settings.tab_width != crate::settings::DEFAULT_TAB_WIDTH {
|
||||||
pub async fn get_org_mode_version() -> Result<String, Box<dyn std::error::Error>> {
|
ret += format!("(setq-default tab-width {})", global_settings.tab_width).as_str();
|
||||||
let elisp_script = r#"(progn
|
}
|
||||||
(org-mode)
|
if global_settings.odd_levels_only != HeadlineLevelFilter::default() {
|
||||||
(message "%s" (org-version nil t nil))
|
ret += match global_settings.odd_levels_only {
|
||||||
)"#;
|
HeadlineLevelFilter::Odd => "(setq org-odd-levels-only t)\n",
|
||||||
let mut cmd = Command::new("emacs");
|
HeadlineLevelFilter::OddEven => "(setq org-odd-levels-only nil)\n",
|
||||||
let cmd = cmd
|
};
|
||||||
.arg("-q")
|
}
|
||||||
.arg("--no-site-file")
|
ret
|
||||||
.arg("--no-splash")
|
|
||||||
.arg("--batch")
|
|
||||||
.arg("--eval")
|
|
||||||
.arg(elisp_script);
|
|
||||||
|
|
||||||
let out = cmd.output().await?;
|
|
||||||
out.status.exit_ok()?;
|
|
||||||
Ok(String::from_utf8(out.stderr)?)
|
|
||||||
}
|
}
|
||||||
14
src/util/elisp/mod.rs
Normal file
14
src/util/elisp/mod.rs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
mod sexp;
|
||||||
|
mod util;
|
||||||
|
|
||||||
|
pub use sexp::sexp;
|
||||||
|
pub(crate) use sexp::unquote;
|
||||||
|
#[cfg(feature = "wasm_test")]
|
||||||
|
pub(crate) use sexp::TextWithProperties;
|
||||||
|
pub use sexp::Token;
|
||||||
|
#[cfg(feature = "compare")]
|
||||||
|
pub(crate) use util::get_emacs_standard_properties;
|
||||||
|
#[cfg(feature = "wasm_test")]
|
||||||
|
pub(crate) use util::maybe_token_to_usize;
|
||||||
|
#[cfg(feature = "wasm_test")]
|
||||||
|
pub(crate) use util::EmacsStandardProperties;
|
||||||
@@ -61,6 +61,7 @@ impl<'s> Token<'s> {
|
|||||||
}?)
|
}?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "compare")]
|
||||||
pub(crate) fn as_text<'p>(
|
pub(crate) fn as_text<'p>(
|
||||||
&'p self,
|
&'p self,
|
||||||
) -> Result<&'p TextWithProperties<'s>, Box<dyn std::error::Error>> {
|
) -> Result<&'p TextWithProperties<'s>, Box<dyn std::error::Error>> {
|
||||||
76
src/util/elisp/util.rs
Normal file
76
src/util/elisp/util.rs
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
use super::Token;
|
||||||
|
|
||||||
|
pub(crate) fn maybe_token_to_usize(
|
||||||
|
token: Option<&Token<'_>>,
|
||||||
|
) -> Result<Option<usize>, Box<dyn std::error::Error>> {
|
||||||
|
Ok(token
|
||||||
|
.map(|token| token.as_atom())
|
||||||
|
.map_or(Ok(None), |r| r.map(Some))?
|
||||||
|
.and_then(|val| {
|
||||||
|
if val == "nil" {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(val.parse::<usize>())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map_or(Ok(None), |r| r.map(Some))?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct EmacsStandardProperties {
|
||||||
|
pub(crate) begin: Option<usize>,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) post_affiliated: Option<usize>,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) contents_begin: Option<usize>,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) contents_end: Option<usize>,
|
||||||
|
pub(crate) end: Option<usize>,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) post_blank: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "compare")]
|
||||||
|
pub(crate) fn get_emacs_standard_properties(
|
||||||
|
emacs: &Token<'_>,
|
||||||
|
) -> Result<EmacsStandardProperties, Box<dyn std::error::Error>> {
|
||||||
|
let children = emacs.as_list()?;
|
||||||
|
let attributes_child = children.get(1).ok_or("Should have an attributes child.")?;
|
||||||
|
let attributes_map = attributes_child.as_map()?;
|
||||||
|
let standard_properties = attributes_map.get(":standard-properties");
|
||||||
|
Ok(if standard_properties.is_some() {
|
||||||
|
let mut std_props = standard_properties
|
||||||
|
.expect("if statement proves its Some")
|
||||||
|
.as_vector()?
|
||||||
|
.iter();
|
||||||
|
let begin = maybe_token_to_usize(std_props.next())?;
|
||||||
|
let post_affiliated = maybe_token_to_usize(std_props.next())?;
|
||||||
|
let contents_begin = maybe_token_to_usize(std_props.next())?;
|
||||||
|
let contents_end = maybe_token_to_usize(std_props.next())?;
|
||||||
|
let end = maybe_token_to_usize(std_props.next())?;
|
||||||
|
let post_blank = maybe_token_to_usize(std_props.next())?;
|
||||||
|
EmacsStandardProperties {
|
||||||
|
begin,
|
||||||
|
post_affiliated,
|
||||||
|
contents_begin,
|
||||||
|
contents_end,
|
||||||
|
end,
|
||||||
|
post_blank,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let begin = maybe_token_to_usize(attributes_map.get(":begin").copied())?;
|
||||||
|
let end = maybe_token_to_usize(attributes_map.get(":end").copied())?;
|
||||||
|
let contents_begin = maybe_token_to_usize(attributes_map.get(":contents-begin").copied())?;
|
||||||
|
let contents_end = maybe_token_to_usize(attributes_map.get(":contents-end").copied())?;
|
||||||
|
let post_blank = maybe_token_to_usize(attributes_map.get(":post-blank").copied())?;
|
||||||
|
let post_affiliated =
|
||||||
|
maybe_token_to_usize(attributes_map.get(":post-affiliated").copied())?;
|
||||||
|
EmacsStandardProperties {
|
||||||
|
begin,
|
||||||
|
post_affiliated,
|
||||||
|
contents_begin,
|
||||||
|
contents_end,
|
||||||
|
end,
|
||||||
|
post_blank,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
8
src/util/mod.rs
Normal file
8
src/util/mod.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#[cfg(any(feature = "compare", feature = "wasm_test"))]
|
||||||
|
pub mod cli;
|
||||||
|
#[cfg(any(feature = "compare", feature = "wasm_test"))]
|
||||||
|
pub mod elisp;
|
||||||
|
#[cfg(any(feature = "compare", feature = "wasm", feature = "wasm_test"))]
|
||||||
|
pub mod elisp_fact;
|
||||||
|
#[cfg(any(feature = "compare", feature = "wasm_test"))]
|
||||||
|
pub mod terminal;
|
||||||
42
src/util/terminal.rs
Normal file
42
src/util/terminal.rs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
fn should_use_color() -> bool {
|
||||||
|
!std::env::var("NO_COLOR").is_ok_and(|val| !val.is_empty())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn foreground_color(red: u8, green: u8, blue: u8) -> Cow<'static, str> {
|
||||||
|
if should_use_color() {
|
||||||
|
format!(
|
||||||
|
"\x1b[38;2;{red};{green};{blue}m",
|
||||||
|
red = red,
|
||||||
|
green = green,
|
||||||
|
blue = blue
|
||||||
|
)
|
||||||
|
.into()
|
||||||
|
} else {
|
||||||
|
Cow::from("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) fn background_color(red: u8, green: u8, blue: u8) -> Cow<'static, str> {
|
||||||
|
if should_use_color() {
|
||||||
|
format!(
|
||||||
|
"\x1b[48;2;{red};{green};{blue}m",
|
||||||
|
red = red,
|
||||||
|
green = green,
|
||||||
|
blue = blue
|
||||||
|
)
|
||||||
|
.into()
|
||||||
|
} else {
|
||||||
|
Cow::from("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn reset_color() -> &'static str {
|
||||||
|
if should_use_color() {
|
||||||
|
"\x1b[0m"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
95
src/wasm/additional_property.rs
Normal file
95
src/wasm/additional_property.rs
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use super::WasmAstNode;
|
||||||
|
use crate::types::AffiliatedKeywordValue;
|
||||||
|
use crate::types::AffiliatedKeywords;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum AdditionalPropertyValue {
|
||||||
|
SingleString(String),
|
||||||
|
ListOfStrings(Vec<String>),
|
||||||
|
OptionalPair {
|
||||||
|
optval: Option<String>,
|
||||||
|
val: String,
|
||||||
|
},
|
||||||
|
ObjectTree {
|
||||||
|
#[serde(rename = "object-tree")]
|
||||||
|
object_tree: Vec<(Option<Vec<WasmAstNode>>, Vec<WasmAstNode>)>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Default)]
|
||||||
|
pub struct AdditionalProperties {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) properties: HashMap<String, AdditionalPropertyValue>,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
AdditionalProperties,
|
||||||
|
AffiliatedKeywords<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{
|
||||||
|
let mut additional_properties = AdditionalProperties::default();
|
||||||
|
for (name, val) in original.keywords.iter() {
|
||||||
|
let converted_val = match val {
|
||||||
|
AffiliatedKeywordValue::SingleString(val) => {
|
||||||
|
AdditionalPropertyValue::SingleString((*val).to_owned())
|
||||||
|
}
|
||||||
|
AffiliatedKeywordValue::ListOfStrings(val) => {
|
||||||
|
AdditionalPropertyValue::ListOfStrings(
|
||||||
|
val.iter().map(|s| (*s).to_owned()).collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
AffiliatedKeywordValue::OptionalPair { optval, val } => {
|
||||||
|
AdditionalPropertyValue::OptionalPair {
|
||||||
|
optval: optval.map(|s| (*s).to_owned()),
|
||||||
|
val: (*val).to_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AffiliatedKeywordValue::ObjectTree(val) => {
|
||||||
|
let mut ret = Vec::with_capacity(val.len());
|
||||||
|
|
||||||
|
for (optval, value) in val {
|
||||||
|
let converted_optval = if let Some(optval) = optval {
|
||||||
|
Some(
|
||||||
|
optval
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let converted_value = value
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
ret.push((converted_optval, converted_value));
|
||||||
|
}
|
||||||
|
|
||||||
|
AdditionalPropertyValue::ObjectTree { object_tree: ret }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
additional_properties
|
||||||
|
.properties
|
||||||
|
.insert(name.clone(), converted_val);
|
||||||
|
}
|
||||||
|
Ok(additional_properties)
|
||||||
|
}
|
||||||
|
);
|
||||||
54
src/wasm/angle_link.rs
Normal file
54
src/wasm/angle_link.rs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::AngleLink;
|
||||||
|
use crate::types::LinkType;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(tag = "format")]
|
||||||
|
#[serde(rename = "angle")]
|
||||||
|
pub struct WasmAngleLink {
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub(crate) link_type: String,
|
||||||
|
pub(crate) path: String,
|
||||||
|
#[serde(rename = "raw-link")]
|
||||||
|
pub(crate) raw_link: String,
|
||||||
|
pub(crate) application: Option<String>,
|
||||||
|
#[serde(rename = "search-option")]
|
||||||
|
pub(crate) search_option: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmAngleLink,
|
||||||
|
AngleLink<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::AngleLink(original) },
|
||||||
|
{ "link".into() },
|
||||||
|
{
|
||||||
|
Ok((
|
||||||
|
Vec::new(),
|
||||||
|
WasmAngleLink {
|
||||||
|
link_type: match &original.link_type {
|
||||||
|
LinkType::File => "file".to_owned(),
|
||||||
|
LinkType::Protocol(protocol) => protocol.clone().into_owned(),
|
||||||
|
LinkType::Id => "id".to_owned(),
|
||||||
|
LinkType::CustomId => "custom-id".to_owned(),
|
||||||
|
LinkType::CodeRef => "coderef".to_owned(),
|
||||||
|
LinkType::Fuzzy => "fuzzy".to_owned(),
|
||||||
|
},
|
||||||
|
path: original.get_path().into_owned(),
|
||||||
|
raw_link: original.raw_link.to_owned(),
|
||||||
|
application: original.application.map(|c| c.to_owned()),
|
||||||
|
search_option: original.get_search_option().map(Cow::into_owned),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
142
src/wasm/ast_node.rs
Normal file
142
src/wasm/ast_node.rs
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::angle_link::WasmAngleLink;
|
||||||
|
use super::babel_call::WasmBabelCall;
|
||||||
|
use super::bold::WasmBold;
|
||||||
|
use super::center_block::WasmCenterBlock;
|
||||||
|
use super::citation::WasmCitation;
|
||||||
|
use super::citation_reference::WasmCitationReference;
|
||||||
|
use super::clock::WasmClock;
|
||||||
|
use super::code::WasmCode;
|
||||||
|
use super::comment::WasmComment;
|
||||||
|
use super::comment_block::WasmCommentBlock;
|
||||||
|
use super::diary_sexp::WasmDiarySexp;
|
||||||
|
use super::document::WasmDocument;
|
||||||
|
use super::drawer::WasmDrawer;
|
||||||
|
use super::dynamic_block::WasmDynamicBlock;
|
||||||
|
use super::entity::WasmEntity;
|
||||||
|
use super::example_block::WasmExampleBlock;
|
||||||
|
use super::export_block::WasmExportBlock;
|
||||||
|
use super::export_snippet::WasmExportSnippet;
|
||||||
|
use super::fixed_width_area::WasmFixedWidthArea;
|
||||||
|
use super::footnote_definition::WasmFootnoteDefinition;
|
||||||
|
use super::footnote_reference::WasmFootnoteReference;
|
||||||
|
use super::headline::WasmHeadline;
|
||||||
|
use super::horizontal_rule::WasmHorizontalRule;
|
||||||
|
use super::inline_babel_call::WasmInlineBabelCall;
|
||||||
|
use super::inline_source_block::WasmInlineSourceBlock;
|
||||||
|
use super::italic::WasmItalic;
|
||||||
|
use super::keyword::WasmKeyword;
|
||||||
|
use super::latex_environment::WasmLatexEnvironment;
|
||||||
|
use super::latex_fragment::WasmLatexFragment;
|
||||||
|
use super::line_break::WasmLineBreak;
|
||||||
|
use super::node_property::WasmNodeProperty;
|
||||||
|
use super::org_macro::WasmOrgMacro;
|
||||||
|
use super::paragraph::WasmParagraph;
|
||||||
|
use super::plain_link::WasmPlainLink;
|
||||||
|
use super::plain_list::WasmPlainList;
|
||||||
|
use super::plain_list_item::WasmPlainListItem;
|
||||||
|
use super::plain_text::WasmPlainText;
|
||||||
|
use super::planning::WasmPlanning;
|
||||||
|
use super::property_drawer::WasmPropertyDrawer;
|
||||||
|
use super::quote_block::WasmQuoteBlock;
|
||||||
|
use super::radio_link::WasmRadioLink;
|
||||||
|
use super::radio_target::WasmRadioTarget;
|
||||||
|
use super::regular_link::WasmRegularLink;
|
||||||
|
use super::section::WasmSection;
|
||||||
|
use super::special_block::WasmSpecialBlock;
|
||||||
|
use super::src_block::WasmSrcBlock;
|
||||||
|
use super::statistics_cookie::WasmStatisticsCookie;
|
||||||
|
use super::strike_through::WasmStrikeThrough;
|
||||||
|
use super::subscript::WasmSubscript;
|
||||||
|
use super::superscript::WasmSuperscript;
|
||||||
|
use super::table::WasmTable;
|
||||||
|
use super::table_cell::WasmTableCell;
|
||||||
|
use super::table_row::WasmTableRow;
|
||||||
|
use super::target::WasmTarget;
|
||||||
|
use super::timestamp::WasmTimestamp;
|
||||||
|
use super::underline::WasmUnderline;
|
||||||
|
use super::verbatim::WasmVerbatim;
|
||||||
|
use super::verse_block::WasmVerseBlock;
|
||||||
|
use super::WasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmAstNodeWrapper<I> {
|
||||||
|
#[serde(rename = "ast-node")]
|
||||||
|
pub(crate) ast_node: String,
|
||||||
|
#[serde(rename = "standard-properties")]
|
||||||
|
pub(crate) standard_properties: WasmStandardProperties,
|
||||||
|
#[serde(rename = "children")]
|
||||||
|
pub(crate) children: Vec<WasmAstNode>,
|
||||||
|
#[serde(rename = "properties")]
|
||||||
|
pub(crate) properties: I,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum WasmAstNode {
|
||||||
|
// Document Nodes
|
||||||
|
Document(WasmAstNodeWrapper<WasmDocument>),
|
||||||
|
Headline(WasmAstNodeWrapper<WasmHeadline>),
|
||||||
|
Section(WasmAstNodeWrapper<WasmSection>),
|
||||||
|
// Elements
|
||||||
|
Paragraph(WasmAstNodeWrapper<WasmParagraph>),
|
||||||
|
PlainList(WasmAstNodeWrapper<WasmPlainList>),
|
||||||
|
PlainListItem(WasmAstNodeWrapper<WasmPlainListItem>),
|
||||||
|
CenterBlock(WasmAstNodeWrapper<WasmCenterBlock>),
|
||||||
|
QuoteBlock(WasmAstNodeWrapper<WasmQuoteBlock>),
|
||||||
|
SpecialBlock(WasmAstNodeWrapper<WasmSpecialBlock>),
|
||||||
|
DynamicBlock(WasmAstNodeWrapper<WasmDynamicBlock>),
|
||||||
|
FootnoteDefinition(WasmAstNodeWrapper<WasmFootnoteDefinition>),
|
||||||
|
Comment(WasmAstNodeWrapper<WasmComment>),
|
||||||
|
Drawer(WasmAstNodeWrapper<WasmDrawer>),
|
||||||
|
PropertyDrawer(WasmAstNodeWrapper<WasmPropertyDrawer>),
|
||||||
|
NodeProperty(WasmAstNodeWrapper<WasmNodeProperty>),
|
||||||
|
Table(WasmAstNodeWrapper<WasmTable>),
|
||||||
|
TableRow(WasmAstNodeWrapper<WasmTableRow>),
|
||||||
|
VerseBlock(WasmAstNodeWrapper<WasmVerseBlock>),
|
||||||
|
CommentBlock(WasmAstNodeWrapper<WasmCommentBlock>),
|
||||||
|
ExampleBlock(WasmAstNodeWrapper<WasmExampleBlock>),
|
||||||
|
ExportBlock(WasmAstNodeWrapper<WasmExportBlock>),
|
||||||
|
SrcBlock(WasmAstNodeWrapper<WasmSrcBlock>),
|
||||||
|
Clock(WasmAstNodeWrapper<WasmClock>),
|
||||||
|
DiarySexp(WasmAstNodeWrapper<WasmDiarySexp>),
|
||||||
|
Planning(WasmAstNodeWrapper<WasmPlanning>),
|
||||||
|
FixedWidthArea(WasmAstNodeWrapper<WasmFixedWidthArea>),
|
||||||
|
HorizontalRule(WasmAstNodeWrapper<WasmHorizontalRule>),
|
||||||
|
Keyword(WasmAstNodeWrapper<WasmKeyword>),
|
||||||
|
BabelCall(WasmAstNodeWrapper<WasmBabelCall>),
|
||||||
|
LatexEnvironment(WasmAstNodeWrapper<WasmLatexEnvironment>),
|
||||||
|
// Objects
|
||||||
|
Bold(WasmAstNodeWrapper<WasmBold>),
|
||||||
|
Italic(WasmAstNodeWrapper<WasmItalic>),
|
||||||
|
Underline(WasmAstNodeWrapper<WasmUnderline>),
|
||||||
|
StrikeThrough(WasmAstNodeWrapper<WasmStrikeThrough>),
|
||||||
|
Code(WasmAstNodeWrapper<WasmCode>),
|
||||||
|
Verbatim(WasmAstNodeWrapper<WasmVerbatim>),
|
||||||
|
PlainText(WasmAstNodeWrapper<WasmPlainText>),
|
||||||
|
RegularLink(WasmAstNodeWrapper<WasmRegularLink>),
|
||||||
|
RadioLink(WasmAstNodeWrapper<WasmRadioLink>),
|
||||||
|
RadioTarget(WasmAstNodeWrapper<WasmRadioTarget>),
|
||||||
|
PlainLink(WasmAstNodeWrapper<WasmPlainLink>),
|
||||||
|
AngleLink(WasmAstNodeWrapper<WasmAngleLink>),
|
||||||
|
OrgMacro(WasmAstNodeWrapper<WasmOrgMacro>),
|
||||||
|
Entity(WasmAstNodeWrapper<WasmEntity>),
|
||||||
|
LatexFragment(WasmAstNodeWrapper<WasmLatexFragment>),
|
||||||
|
ExportSnippet(WasmAstNodeWrapper<WasmExportSnippet>),
|
||||||
|
FootnoteReference(WasmAstNodeWrapper<WasmFootnoteReference>),
|
||||||
|
Citation(WasmAstNodeWrapper<WasmCitation>),
|
||||||
|
CitationReference(WasmAstNodeWrapper<WasmCitationReference>),
|
||||||
|
InlineBabelCall(WasmAstNodeWrapper<WasmInlineBabelCall>),
|
||||||
|
InlineSourceBlock(WasmAstNodeWrapper<WasmInlineSourceBlock>),
|
||||||
|
LineBreak(WasmAstNodeWrapper<WasmLineBreak>),
|
||||||
|
Target(WasmAstNodeWrapper<WasmTarget>),
|
||||||
|
StatisticsCookie(WasmAstNodeWrapper<WasmStatisticsCookie>),
|
||||||
|
Subscript(WasmAstNodeWrapper<WasmSubscript>),
|
||||||
|
Superscript(WasmAstNodeWrapper<WasmSuperscript>),
|
||||||
|
TableCell(WasmAstNodeWrapper<WasmTableCell>),
|
||||||
|
Timestamp(WasmAstNodeWrapper<WasmTimestamp>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WasmAstNode {}
|
||||||
50
src/wasm/babel_call.rs
Normal file
50
src/wasm/babel_call.rs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use super::AdditionalProperties;
|
||||||
|
use crate::types::BabelCall;
|
||||||
|
use crate::types::GetAffiliatedKeywords;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmBabelCall {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) additional_properties: AdditionalProperties,
|
||||||
|
pub(crate) call: Option<String>,
|
||||||
|
#[serde(rename = "inside-header")]
|
||||||
|
pub(crate) inside_header: Option<String>,
|
||||||
|
pub(crate) arguments: Option<String>,
|
||||||
|
#[serde(rename = "end-header")]
|
||||||
|
pub(crate) end_header: Option<String>,
|
||||||
|
pub(crate) value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmBabelCall,
|
||||||
|
BabelCall<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::BabelCall(original) },
|
||||||
|
{ "babel-call".into() },
|
||||||
|
{
|
||||||
|
let additional_properties = original
|
||||||
|
.get_affiliated_keywords()
|
||||||
|
.to_wasm(wasm_context.clone())?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
Vec::new(),
|
||||||
|
WasmBabelCall {
|
||||||
|
additional_properties,
|
||||||
|
call: original.call.map(|s| s.to_owned()),
|
||||||
|
inside_header: original.inside_header.map(|s| s.to_owned()),
|
||||||
|
arguments: original.arguments.map(|s| s.to_owned()),
|
||||||
|
end_header: original.end_header.map(|s| s.to_owned()),
|
||||||
|
value: original.value.to_owned(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
34
src/wasm/bold.rs
Normal file
34
src/wasm/bold.rs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::Bold;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmBold {}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmBold,
|
||||||
|
Bold<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::Bold(original) },
|
||||||
|
{ "bold".into() },
|
||||||
|
{
|
||||||
|
let children = original
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok((children, WasmBold {}))
|
||||||
|
}
|
||||||
|
);
|
||||||
48
src/wasm/center_block.rs
Normal file
48
src/wasm/center_block.rs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use super::AdditionalProperties;
|
||||||
|
use crate::types::CenterBlock;
|
||||||
|
use crate::types::GetAffiliatedKeywords;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmCenterBlock {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) additional_properties: AdditionalProperties,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmCenterBlock,
|
||||||
|
CenterBlock<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::CenterBlock(original) },
|
||||||
|
{ "center-block".into() },
|
||||||
|
{
|
||||||
|
let additional_properties = original
|
||||||
|
.get_affiliated_keywords()
|
||||||
|
.to_wasm(wasm_context.clone())?;
|
||||||
|
|
||||||
|
let children = original
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
children,
|
||||||
|
WasmCenterBlock {
|
||||||
|
additional_properties,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
71
src/wasm/citation.rs
Normal file
71
src/wasm/citation.rs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::Citation;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmCitation {
|
||||||
|
pub(crate) style: Option<String>,
|
||||||
|
pub(crate) prefix: Option<Vec<WasmAstNode>>,
|
||||||
|
pub(crate) suffix: Option<Vec<WasmAstNode>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmCitation,
|
||||||
|
Citation<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::Citation(original) },
|
||||||
|
{ "citation".into() },
|
||||||
|
{
|
||||||
|
let children = original
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
let prefix = original
|
||||||
|
.prefix
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
let suffix = original
|
||||||
|
.suffix
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
children,
|
||||||
|
WasmCitation {
|
||||||
|
style: original.style.map(|s| s.to_owned()),
|
||||||
|
prefix: if prefix.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(prefix)
|
||||||
|
},
|
||||||
|
suffix: if suffix.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(suffix)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
62
src/wasm/citation_reference.rs
Normal file
62
src/wasm/citation_reference.rs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::CitationReference;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmCitationReference {
|
||||||
|
pub(crate) key: String,
|
||||||
|
pub(crate) prefix: Option<Vec<WasmAstNode>>,
|
||||||
|
pub(crate) suffix: Option<Vec<WasmAstNode>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmCitationReference,
|
||||||
|
CitationReference<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::CitationReference(original) },
|
||||||
|
{ "citation-reference".into() },
|
||||||
|
{
|
||||||
|
let prefix = original
|
||||||
|
.prefix
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
let suffix = original
|
||||||
|
.suffix
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
Vec::new(),
|
||||||
|
WasmCitationReference {
|
||||||
|
key: original.key.to_owned(),
|
||||||
|
prefix: if prefix.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(prefix)
|
||||||
|
},
|
||||||
|
suffix: if suffix.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(suffix)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
43
src/wasm/clock.rs
Normal file
43
src/wasm/clock.rs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::Clock;
|
||||||
|
use crate::types::ClockStatus;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmClock {
|
||||||
|
#[serde(rename = "value")]
|
||||||
|
pub(crate) timestamp: Box<WasmAstNode>,
|
||||||
|
pub(crate) duration: Option<String>,
|
||||||
|
pub(crate) status: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmClock,
|
||||||
|
Clock<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::Clock(original) },
|
||||||
|
{ "clock".into() },
|
||||||
|
{
|
||||||
|
Ok((
|
||||||
|
Vec::new(),
|
||||||
|
WasmClock {
|
||||||
|
timestamp: Box::new(Into::<WasmAstNode>::into(
|
||||||
|
original.timestamp.to_wasm(wasm_context.clone())?,
|
||||||
|
)),
|
||||||
|
duration: original.duration.map(|s| s.to_owned()),
|
||||||
|
status: match original.status {
|
||||||
|
ClockStatus::Running => "running",
|
||||||
|
ClockStatus::Closed => "closed",
|
||||||
|
}
|
||||||
|
.to_owned(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
31
src/wasm/code.rs
Normal file
31
src/wasm/code.rs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::Code;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmCode {
|
||||||
|
pub(crate) value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmCode,
|
||||||
|
Code<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::Code(original) },
|
||||||
|
{ "code".into() },
|
||||||
|
{
|
||||||
|
Ok((
|
||||||
|
Vec::new(),
|
||||||
|
WasmCode {
|
||||||
|
value: original.contents.to_owned(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
31
src/wasm/comment.rs
Normal file
31
src/wasm/comment.rs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::Comment;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmComment {
|
||||||
|
pub(crate) value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmComment,
|
||||||
|
Comment<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::Comment(original) },
|
||||||
|
{ "comment".into() },
|
||||||
|
{
|
||||||
|
Ok((
|
||||||
|
Vec::new(),
|
||||||
|
WasmComment {
|
||||||
|
value: original.get_value(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
40
src/wasm/comment_block.rs
Normal file
40
src/wasm/comment_block.rs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use super::AdditionalProperties;
|
||||||
|
use crate::types::CommentBlock;
|
||||||
|
use crate::types::GetAffiliatedKeywords;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmCommentBlock {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) additional_properties: AdditionalProperties,
|
||||||
|
pub(crate) value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmCommentBlock,
|
||||||
|
CommentBlock<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::CommentBlock(original) },
|
||||||
|
{ "comment-block".into() },
|
||||||
|
{
|
||||||
|
let additional_properties = original
|
||||||
|
.get_affiliated_keywords()
|
||||||
|
.to_wasm(wasm_context.clone())?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
Vec::new(),
|
||||||
|
WasmCommentBlock {
|
||||||
|
additional_properties,
|
||||||
|
value: original.contents.to_owned(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
40
src/wasm/diary_sexp.rs
Normal file
40
src/wasm/diary_sexp.rs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use super::AdditionalProperties;
|
||||||
|
use crate::types::DiarySexp;
|
||||||
|
use crate::types::GetAffiliatedKeywords;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmDiarySexp {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) additional_properties: AdditionalProperties,
|
||||||
|
pub(crate) value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmDiarySexp,
|
||||||
|
DiarySexp<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::DiarySexp(original) },
|
||||||
|
{ "diary-sexp".into() },
|
||||||
|
{
|
||||||
|
let additional_properties = original
|
||||||
|
.get_affiliated_keywords()
|
||||||
|
.to_wasm(wasm_context.clone())?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
Vec::new(),
|
||||||
|
WasmDiarySexp {
|
||||||
|
additional_properties,
|
||||||
|
value: original.value.to_owned(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
69
src/wasm/document.rs
Normal file
69
src/wasm/document.rs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::additional_property::AdditionalProperties;
|
||||||
|
use super::additional_property::AdditionalPropertyValue;
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::Document;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmDocument {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) additional_properties: AdditionalProperties,
|
||||||
|
#[serde(rename = "CATEGORY")]
|
||||||
|
pub(crate) category: Option<String>,
|
||||||
|
pub(crate) path: Option<PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmDocument,
|
||||||
|
Document<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::Document(original) },
|
||||||
|
{ "org-data".into() },
|
||||||
|
{
|
||||||
|
let category = original.category.as_deref();
|
||||||
|
let path = original.path.clone();
|
||||||
|
|
||||||
|
let mut additional_properties = AdditionalProperties::default();
|
||||||
|
for (name, val) in original.get_additional_properties().map(|node_property| {
|
||||||
|
(
|
||||||
|
node_property.property_name.to_uppercase(),
|
||||||
|
AdditionalPropertyValue::SingleString(node_property.value.unwrap_or("").to_owned()),
|
||||||
|
)
|
||||||
|
}) {
|
||||||
|
additional_properties.properties.insert(name, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
let children = original
|
||||||
|
.zeroth_section
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.chain(original.children.iter().map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
}))
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
children,
|
||||||
|
WasmDocument {
|
||||||
|
additional_properties,
|
||||||
|
category: category.map(str::to_owned),
|
||||||
|
path,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
51
src/wasm/drawer.rs
Normal file
51
src/wasm/drawer.rs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use super::AdditionalProperties;
|
||||||
|
use crate::types::Drawer;
|
||||||
|
use crate::types::GetAffiliatedKeywords;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmDrawer {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) additional_properties: AdditionalProperties,
|
||||||
|
#[serde(rename = "drawer-name")]
|
||||||
|
pub(crate) drawer_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmDrawer,
|
||||||
|
Drawer<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::Drawer(original) },
|
||||||
|
{ "drawer".into() },
|
||||||
|
{
|
||||||
|
let additional_properties = original
|
||||||
|
.get_affiliated_keywords()
|
||||||
|
.to_wasm(wasm_context.clone())?;
|
||||||
|
|
||||||
|
let children = original
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
children,
|
||||||
|
WasmDrawer {
|
||||||
|
additional_properties,
|
||||||
|
drawer_name: original.drawer_name.to_owned(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
53
src/wasm/dynamic_block.rs
Normal file
53
src/wasm/dynamic_block.rs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use super::AdditionalProperties;
|
||||||
|
use crate::types::DynamicBlock;
|
||||||
|
use crate::types::GetAffiliatedKeywords;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmDynamicBlock {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) additional_properties: AdditionalProperties,
|
||||||
|
#[serde(rename = "block-name")]
|
||||||
|
pub(crate) block_name: String,
|
||||||
|
pub(crate) arguments: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmDynamicBlock,
|
||||||
|
DynamicBlock<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::DynamicBlock(original) },
|
||||||
|
{ "dynamic-block".into() },
|
||||||
|
{
|
||||||
|
let additional_properties = original
|
||||||
|
.get_affiliated_keywords()
|
||||||
|
.to_wasm(wasm_context.clone())?;
|
||||||
|
|
||||||
|
let children = original
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
children,
|
||||||
|
WasmDynamicBlock {
|
||||||
|
additional_properties,
|
||||||
|
block_name: original.block_name.to_owned(),
|
||||||
|
arguments: original.parameters.map(|s| s.to_owned()),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
49
src/wasm/entity.rs
Normal file
49
src/wasm/entity.rs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::headline::Noop;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::Entity;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmEntity {
|
||||||
|
pub(crate) name: String,
|
||||||
|
pub(crate) latex: String,
|
||||||
|
#[serde(rename = "latex-math-p")]
|
||||||
|
pub(crate) latex_math_mode: bool,
|
||||||
|
pub(crate) html: String,
|
||||||
|
pub(crate) ascii: String,
|
||||||
|
pub(crate) latin1: Noop,
|
||||||
|
#[serde(rename = "utf-8")]
|
||||||
|
pub(crate) utf8: String,
|
||||||
|
#[serde(rename = "use-brackets-p")]
|
||||||
|
pub(crate) use_brackets: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmEntity,
|
||||||
|
Entity<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::Entity(original) },
|
||||||
|
{ "entity".into() },
|
||||||
|
{
|
||||||
|
Ok((
|
||||||
|
Vec::new(),
|
||||||
|
WasmEntity {
|
||||||
|
name: original.name.to_owned(),
|
||||||
|
latex: original.latex.to_owned(),
|
||||||
|
latex_math_mode: original.latex_math_mode,
|
||||||
|
html: original.html.to_owned(),
|
||||||
|
ascii: original.ascii.to_owned(),
|
||||||
|
latin1: Noop {},
|
||||||
|
utf8: original.utf8.to_owned(),
|
||||||
|
use_brackets: original.use_brackets,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
75
src/wasm/example_block.rs
Normal file
75
src/wasm/example_block.rs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::src_block::WasmNumberLines;
|
||||||
|
use super::src_block::WasmNumberLinesWrapper;
|
||||||
|
use super::src_block::WasmRetainLabels;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use super::AdditionalProperties;
|
||||||
|
use crate::types::CharOffsetInLine;
|
||||||
|
use crate::types::ExampleBlock;
|
||||||
|
use crate::types::GetAffiliatedKeywords;
|
||||||
|
use crate::types::RetainLabels;
|
||||||
|
use crate::types::SwitchNumberLines;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmExampleBlock {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) additional_properties: AdditionalProperties,
|
||||||
|
pub(crate) value: String,
|
||||||
|
pub(crate) switches: Option<String>,
|
||||||
|
#[serde(rename = "number-lines")]
|
||||||
|
pub(crate) number_lines: Option<WasmNumberLinesWrapper>,
|
||||||
|
#[serde(rename = "preserve-indent")]
|
||||||
|
pub(crate) preserve_indent: Option<CharOffsetInLine>,
|
||||||
|
#[serde(rename = "retain-labels")]
|
||||||
|
pub(crate) retain_labels: WasmRetainLabels,
|
||||||
|
#[serde(rename = "use-labels")]
|
||||||
|
pub(crate) use_labels: bool,
|
||||||
|
#[serde(rename = "label-fmt")]
|
||||||
|
pub(crate) label_format: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmExampleBlock,
|
||||||
|
ExampleBlock<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::ExampleBlock(original) },
|
||||||
|
{ "example-block".into() },
|
||||||
|
{
|
||||||
|
let additional_properties = original
|
||||||
|
.get_affiliated_keywords()
|
||||||
|
.to_wasm(wasm_context.clone())?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
Vec::new(),
|
||||||
|
WasmExampleBlock {
|
||||||
|
additional_properties,
|
||||||
|
value: original.get_value().into_owned(),
|
||||||
|
switches: original.switches.map(|s| s.to_owned()),
|
||||||
|
number_lines: match original.number_lines {
|
||||||
|
None => None,
|
||||||
|
Some(SwitchNumberLines::New(n)) => Some(WasmNumberLinesWrapper {
|
||||||
|
inner: WasmNumberLines::New(n),
|
||||||
|
}),
|
||||||
|
Some(SwitchNumberLines::Continued(n)) => Some(WasmNumberLinesWrapper {
|
||||||
|
inner: WasmNumberLines::Continued(n),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
preserve_indent: original.preserve_indent,
|
||||||
|
retain_labels: match original.retain_labels {
|
||||||
|
RetainLabels::No => WasmRetainLabels::YesNo(false),
|
||||||
|
RetainLabels::Yes => WasmRetainLabels::YesNo(true),
|
||||||
|
RetainLabels::Keep(n) => WasmRetainLabels::Keep(n),
|
||||||
|
},
|
||||||
|
use_labels: original.use_labels,
|
||||||
|
label_format: original.label_format.map(|s| s.to_owned()),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
43
src/wasm/export_block.rs
Normal file
43
src/wasm/export_block.rs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use super::AdditionalProperties;
|
||||||
|
use crate::types::ExportBlock;
|
||||||
|
use crate::types::GetAffiliatedKeywords;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmExportBlock {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) additional_properties: AdditionalProperties,
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub(crate) export_type: Option<String>,
|
||||||
|
pub(crate) value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmExportBlock,
|
||||||
|
ExportBlock<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::ExportBlock(original) },
|
||||||
|
{ "export-block".into() },
|
||||||
|
{
|
||||||
|
let additional_properties = original
|
||||||
|
.get_affiliated_keywords()
|
||||||
|
.to_wasm(wasm_context.clone())?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
Vec::new(),
|
||||||
|
WasmExportBlock {
|
||||||
|
additional_properties,
|
||||||
|
export_type: original.get_export_type(),
|
||||||
|
value: original.get_value().into_owned(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
34
src/wasm/export_snippet.rs
Normal file
34
src/wasm/export_snippet.rs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::ExportSnippet;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmExportSnippet {
|
||||||
|
#[serde(rename = "back-end")]
|
||||||
|
pub(crate) backend: String,
|
||||||
|
pub(crate) value: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmExportSnippet,
|
||||||
|
ExportSnippet<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::ExportSnippet(original) },
|
||||||
|
{ "export-snippet".into() },
|
||||||
|
{
|
||||||
|
Ok((
|
||||||
|
Vec::new(),
|
||||||
|
WasmExportSnippet {
|
||||||
|
backend: original.backend.to_owned(),
|
||||||
|
value: original.contents.map(|s| s.to_owned()),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
42
src/wasm/fixed_width_area.rs
Normal file
42
src/wasm/fixed_width_area.rs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use super::AdditionalProperties;
|
||||||
|
use crate::types::FixedWidthArea;
|
||||||
|
use crate::types::GetAffiliatedKeywords;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmFixedWidthArea {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) additional_properties: AdditionalProperties,
|
||||||
|
pub(crate) value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmFixedWidthArea,
|
||||||
|
FixedWidthArea<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::FixedWidthArea(original) },
|
||||||
|
{ "fixed-width".into() },
|
||||||
|
{
|
||||||
|
let additional_properties = original
|
||||||
|
.get_affiliated_keywords()
|
||||||
|
.to_wasm(wasm_context.clone())?;
|
||||||
|
|
||||||
|
let value = original.get_value();
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
Vec::new(),
|
||||||
|
WasmFixedWidthArea {
|
||||||
|
additional_properties,
|
||||||
|
value,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
54
src/wasm/footnote_definition.rs
Normal file
54
src/wasm/footnote_definition.rs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::headline::Noop;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use super::AdditionalProperties;
|
||||||
|
use crate::types::FootnoteDefinition;
|
||||||
|
use crate::types::GetAffiliatedKeywords;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmFootnoteDefinition {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) additional_properties: AdditionalProperties,
|
||||||
|
pub(crate) label: String,
|
||||||
|
#[serde(rename = "pre-blank")]
|
||||||
|
pub(crate) pre_blank: Noop,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmFootnoteDefinition,
|
||||||
|
FootnoteDefinition<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::FootnoteDefinition(original) },
|
||||||
|
{ "footnote-definition".into() },
|
||||||
|
{
|
||||||
|
let additional_properties = original
|
||||||
|
.get_affiliated_keywords()
|
||||||
|
.to_wasm(wasm_context.clone())?;
|
||||||
|
|
||||||
|
let children = original
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
children,
|
||||||
|
WasmFootnoteDefinition {
|
||||||
|
additional_properties,
|
||||||
|
label: original.label.to_owned(),
|
||||||
|
pre_blank: Noop {},
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
49
src/wasm/footnote_reference.rs
Normal file
49
src/wasm/footnote_reference.rs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::FootnoteReference;
|
||||||
|
use crate::types::FootnoteReferenceType;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmFootnoteReference {
|
||||||
|
pub(crate) label: Option<String>,
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub(crate) footnote_reference_type: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmFootnoteReference,
|
||||||
|
FootnoteReference<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::FootnoteReference(original) },
|
||||||
|
{ "footnote-reference".into() },
|
||||||
|
{
|
||||||
|
let children = original
|
||||||
|
.definition
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
children,
|
||||||
|
WasmFootnoteReference {
|
||||||
|
label: original.label.map(|s| s.to_owned()),
|
||||||
|
footnote_reference_type: match original.get_type() {
|
||||||
|
FootnoteReferenceType::Standard => "standard",
|
||||||
|
FootnoteReferenceType::Inline => "inline",
|
||||||
|
}
|
||||||
|
.to_owned(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
140
src/wasm/headline.rs
Normal file
140
src/wasm/headline.rs
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use super::AdditionalProperties;
|
||||||
|
use super::AdditionalPropertyValue;
|
||||||
|
use crate::types::Heading;
|
||||||
|
use crate::types::HeadlineLevel;
|
||||||
|
use crate::types::PriorityCookie;
|
||||||
|
use crate::types::TodoKeywordType;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmHeadline {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) additional_properties: AdditionalProperties,
|
||||||
|
pub(crate) level: HeadlineLevel,
|
||||||
|
pub(crate) tags: Vec<String>,
|
||||||
|
#[serde(rename = "todo-keyword")]
|
||||||
|
pub(crate) todo_keyword: Option<String>,
|
||||||
|
#[serde(rename = "todo-type")]
|
||||||
|
pub(crate) todo_type: Option<String>,
|
||||||
|
pub(crate) title: Vec<WasmAstNode>,
|
||||||
|
pub(crate) priority: Option<PriorityCookie>,
|
||||||
|
#[serde(rename = "archivedp")]
|
||||||
|
pub(crate) is_archived: bool,
|
||||||
|
#[serde(rename = "commentedp")]
|
||||||
|
pub(crate) is_comment: bool,
|
||||||
|
#[serde(rename = "raw-value")]
|
||||||
|
pub(crate) raw_value: String,
|
||||||
|
#[serde(rename = "footnote-section-p")]
|
||||||
|
pub(crate) is_footnote_section: bool,
|
||||||
|
pub(crate) scheduled: Option<Box<WasmAstNode>>,
|
||||||
|
pub(crate) deadline: Option<Box<WasmAstNode>>,
|
||||||
|
pub(crate) closed: Option<Box<WasmAstNode>>,
|
||||||
|
#[serde(rename = "pre-blank")]
|
||||||
|
pub(crate) pre_blank: Noop,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(tag = "noop")]
|
||||||
|
pub struct Noop {}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmHeadline,
|
||||||
|
Heading<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::Headline(original) },
|
||||||
|
{ "headline".into() },
|
||||||
|
{
|
||||||
|
let mut additional_properties = AdditionalProperties::default();
|
||||||
|
for (name, val) in original.get_additional_properties().map(|node_property| {
|
||||||
|
(
|
||||||
|
node_property.property_name.to_uppercase(),
|
||||||
|
AdditionalPropertyValue::SingleString(node_property.value.unwrap_or("").to_owned()),
|
||||||
|
)
|
||||||
|
}) {
|
||||||
|
additional_properties.properties.insert(name, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
let children = original
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
children,
|
||||||
|
WasmHeadline {
|
||||||
|
additional_properties,
|
||||||
|
level: original.level,
|
||||||
|
tags: original.tags.iter().map(|tag| (*tag).to_owned()).collect(),
|
||||||
|
todo_keyword: original
|
||||||
|
.todo_keyword
|
||||||
|
.as_ref()
|
||||||
|
.map(|(_, keyword)| (*keyword).to_owned()),
|
||||||
|
todo_type: original
|
||||||
|
.todo_keyword
|
||||||
|
.as_ref()
|
||||||
|
.map(|(keyword, _)| match keyword {
|
||||||
|
TodoKeywordType::Done => "done".to_owned(),
|
||||||
|
TodoKeywordType::Todo => "todo".to_owned(),
|
||||||
|
}),
|
||||||
|
title: original
|
||||||
|
.title
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?,
|
||||||
|
priority: original.priority_cookie,
|
||||||
|
is_archived: original.is_archived,
|
||||||
|
is_comment: original.is_comment,
|
||||||
|
raw_value: original.get_raw_value(),
|
||||||
|
is_footnote_section: original.is_footnote_section,
|
||||||
|
scheduled: original
|
||||||
|
.scheduled
|
||||||
|
.as_ref()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.map_or(Ok(None), |r| r.map(Some))?
|
||||||
|
.map(Box::new),
|
||||||
|
deadline: original
|
||||||
|
.deadline
|
||||||
|
.as_ref()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.map_or(Ok(None), |r| r.map(Some))?
|
||||||
|
.map(Box::new),
|
||||||
|
closed: original
|
||||||
|
.closed
|
||||||
|
.as_ref()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.map_or(Ok(None), |r| r.map(Some))?
|
||||||
|
.map(Box::new),
|
||||||
|
pre_blank: Noop {},
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
38
src/wasm/horizontal_rule.rs
Normal file
38
src/wasm/horizontal_rule.rs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use super::AdditionalProperties;
|
||||||
|
use crate::types::GetAffiliatedKeywords;
|
||||||
|
use crate::types::HorizontalRule;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmHorizontalRule {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) additional_properties: AdditionalProperties,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmHorizontalRule,
|
||||||
|
HorizontalRule<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::HorizontalRule(original) },
|
||||||
|
{ "horizontal-rule".into() },
|
||||||
|
{
|
||||||
|
let additional_properties = original
|
||||||
|
.get_affiliated_keywords()
|
||||||
|
.to_wasm(wasm_context.clone())?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
Vec::new(),
|
||||||
|
WasmHorizontalRule {
|
||||||
|
additional_properties,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
41
src/wasm/inline_babel_call.rs
Normal file
41
src/wasm/inline_babel_call.rs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::InlineBabelCall;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmInlineBabelCall {
|
||||||
|
pub(crate) call: String,
|
||||||
|
#[serde(rename = "inside-header")]
|
||||||
|
pub(crate) inside_header: Option<String>,
|
||||||
|
pub(crate) arguments: Option<String>,
|
||||||
|
#[serde(rename = "end-header")]
|
||||||
|
pub(crate) end_header: Option<String>,
|
||||||
|
pub(crate) value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmInlineBabelCall,
|
||||||
|
InlineBabelCall<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::InlineBabelCall(original) },
|
||||||
|
{ "inline-babel-call".into() },
|
||||||
|
{
|
||||||
|
Ok((
|
||||||
|
Vec::new(),
|
||||||
|
WasmInlineBabelCall {
|
||||||
|
call: original.call.to_owned(),
|
||||||
|
inside_header: original.inside_header.map(|s| s.to_owned()),
|
||||||
|
arguments: original.arguments.map(|s| s.to_owned()),
|
||||||
|
end_header: original.end_header.map(|s| s.to_owned()),
|
||||||
|
value: original.value.to_owned(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
35
src/wasm/inline_source_block.rs
Normal file
35
src/wasm/inline_source_block.rs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::InlineSourceBlock;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmInlineSourceBlock {
|
||||||
|
pub(crate) language: String,
|
||||||
|
pub(crate) value: String,
|
||||||
|
pub(crate) parameters: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmInlineSourceBlock,
|
||||||
|
InlineSourceBlock<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::InlineSourceBlock(original) },
|
||||||
|
{ "inline-src-block".into() },
|
||||||
|
{
|
||||||
|
Ok((
|
||||||
|
Vec::new(),
|
||||||
|
WasmInlineSourceBlock {
|
||||||
|
language: original.language.to_owned(),
|
||||||
|
value: original.value.to_owned(),
|
||||||
|
parameters: original.parameters.map(str::to_owned),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
34
src/wasm/italic.rs
Normal file
34
src/wasm/italic.rs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::Italic;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmItalic {}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmItalic,
|
||||||
|
Italic<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::Italic(original) },
|
||||||
|
{ "italic".into() },
|
||||||
|
{
|
||||||
|
let children = original
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok((children, WasmItalic {}))
|
||||||
|
}
|
||||||
|
);
|
||||||
42
src/wasm/keyword.rs
Normal file
42
src/wasm/keyword.rs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use super::AdditionalProperties;
|
||||||
|
use crate::types::GetAffiliatedKeywords;
|
||||||
|
use crate::types::Keyword;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmKeyword {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) additional_properties: AdditionalProperties,
|
||||||
|
pub(crate) key: String,
|
||||||
|
pub(crate) value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmKeyword,
|
||||||
|
Keyword<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::Keyword(original) },
|
||||||
|
{ "keyword".into() },
|
||||||
|
{
|
||||||
|
let additional_properties = original
|
||||||
|
.get_affiliated_keywords()
|
||||||
|
.to_wasm(wasm_context.clone())?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
Vec::new(),
|
||||||
|
WasmKeyword {
|
||||||
|
additional_properties,
|
||||||
|
key: original.key.to_uppercase(),
|
||||||
|
value: original.value.to_owned(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
40
src/wasm/latex_environment.rs
Normal file
40
src/wasm/latex_environment.rs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use super::AdditionalProperties;
|
||||||
|
use crate::types::GetAffiliatedKeywords;
|
||||||
|
use crate::types::LatexEnvironment;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmLatexEnvironment {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) additional_properties: AdditionalProperties,
|
||||||
|
pub(crate) value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmLatexEnvironment,
|
||||||
|
LatexEnvironment<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::LatexEnvironment(original) },
|
||||||
|
{ "latex-environment".into() },
|
||||||
|
{
|
||||||
|
let additional_properties = original
|
||||||
|
.get_affiliated_keywords()
|
||||||
|
.to_wasm(wasm_context.clone())?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
Vec::new(),
|
||||||
|
WasmLatexEnvironment {
|
||||||
|
additional_properties,
|
||||||
|
value: original.value.to_owned(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
31
src/wasm/latex_fragment.rs
Normal file
31
src/wasm/latex_fragment.rs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::LatexFragment;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmLatexFragment {
|
||||||
|
pub(crate) value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmLatexFragment,
|
||||||
|
LatexFragment<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::LatexFragment(original) },
|
||||||
|
{ "latex-fragment".into() },
|
||||||
|
{
|
||||||
|
Ok((
|
||||||
|
Vec::new(),
|
||||||
|
WasmLatexFragment {
|
||||||
|
value: original.value.to_owned(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
22
src/wasm/line_break.rs
Normal file
22
src/wasm/line_break.rs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::LineBreak;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmLineBreak {}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmLineBreak,
|
||||||
|
LineBreak<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::LineBreak(original) },
|
||||||
|
{ "line-break".into() },
|
||||||
|
{ Ok((Vec::new(), WasmLineBreak {},)) }
|
||||||
|
);
|
||||||
59
src/wasm/macros.rs
Normal file
59
src/wasm/macros.rs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/// Write the implementation for the intermediate ast node.
|
||||||
|
///
|
||||||
|
/// This exists to make changing the type signature easier.
|
||||||
|
macro_rules! to_wasm {
|
||||||
|
($ostruct:ty, $istruct:ty, $original:ident, $wasm_context:ident, $fnbody:tt) => {
|
||||||
|
impl<'s> ToWasm for $istruct {
|
||||||
|
type Output = $ostruct;
|
||||||
|
|
||||||
|
fn to_wasm(
|
||||||
|
&self,
|
||||||
|
$wasm_context: crate::wasm::to_wasm::ToWasmContext<'_>,
|
||||||
|
) -> Result<Self::Output, crate::error::CustomError> {
|
||||||
|
let $original = self;
|
||||||
|
#[allow(unused_braces)]
|
||||||
|
$fnbody
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($ostruct:ty, $istruct:ty, $original:ident, $wasm_context:ident, $toastnodebody:tt, $elispnamebody:tt, $fnbody:tt) => {
|
||||||
|
impl<'s> ToWasm for $istruct {
|
||||||
|
type Output = crate::wasm::ast_node::WasmAstNodeWrapper<$ostruct>;
|
||||||
|
|
||||||
|
fn to_wasm(
|
||||||
|
&self,
|
||||||
|
$wasm_context: crate::wasm::to_wasm::ToWasmContext<'_>,
|
||||||
|
) -> Result<Self::Output, crate::error::CustomError> {
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
let $original = self;
|
||||||
|
let standard_properties =
|
||||||
|
self.to_wasm_standard_properties($wasm_context.clone())?;
|
||||||
|
|
||||||
|
$fnbody.map(
|
||||||
|
|(children, inner)| crate::wasm::ast_node::WasmAstNodeWrapper {
|
||||||
|
ast_node: inner.get_elisp_name().into_owned(),
|
||||||
|
standard_properties,
|
||||||
|
children,
|
||||||
|
properties: inner,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::wasm::ast_node::WasmAstNodeWrapper<$ostruct>> for WasmAstNode {
|
||||||
|
fn from($original: crate::wasm::ast_node::WasmAstNodeWrapper<$ostruct>) -> Self {
|
||||||
|
let ret = $toastnodebody;
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'s> crate::util::elisp_fact::ElispFact<'s> for $ostruct {
|
||||||
|
fn get_elisp_name<'b>(&'b self) -> std::borrow::Cow<'s, str> {
|
||||||
|
let ret = $elispnamebody;
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) use to_wasm;
|
||||||
76
src/wasm/mod.rs
Normal file
76
src/wasm/mod.rs
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
mod additional_property;
|
||||||
|
mod angle_link;
|
||||||
|
mod ast_node;
|
||||||
|
mod babel_call;
|
||||||
|
mod bold;
|
||||||
|
mod center_block;
|
||||||
|
mod citation;
|
||||||
|
mod citation_reference;
|
||||||
|
mod clock;
|
||||||
|
mod code;
|
||||||
|
mod comment;
|
||||||
|
mod comment_block;
|
||||||
|
mod diary_sexp;
|
||||||
|
mod document;
|
||||||
|
mod drawer;
|
||||||
|
mod dynamic_block;
|
||||||
|
mod entity;
|
||||||
|
mod example_block;
|
||||||
|
mod export_block;
|
||||||
|
mod export_snippet;
|
||||||
|
mod fixed_width_area;
|
||||||
|
mod footnote_definition;
|
||||||
|
mod footnote_reference;
|
||||||
|
mod headline;
|
||||||
|
mod horizontal_rule;
|
||||||
|
mod inline_babel_call;
|
||||||
|
mod inline_source_block;
|
||||||
|
mod italic;
|
||||||
|
mod keyword;
|
||||||
|
mod latex_environment;
|
||||||
|
mod latex_fragment;
|
||||||
|
mod line_break;
|
||||||
|
mod macros;
|
||||||
|
mod node_property;
|
||||||
|
mod org_macro;
|
||||||
|
mod paragraph;
|
||||||
|
mod parse_result;
|
||||||
|
mod plain_link;
|
||||||
|
mod plain_list;
|
||||||
|
mod plain_list_item;
|
||||||
|
mod plain_text;
|
||||||
|
mod planning;
|
||||||
|
mod property_drawer;
|
||||||
|
mod quote_block;
|
||||||
|
mod radio_link;
|
||||||
|
mod radio_target;
|
||||||
|
mod regular_link;
|
||||||
|
mod section;
|
||||||
|
mod special_block;
|
||||||
|
mod src_block;
|
||||||
|
mod standard_properties;
|
||||||
|
mod statistics_cookie;
|
||||||
|
mod strike_through;
|
||||||
|
mod subscript;
|
||||||
|
mod superscript;
|
||||||
|
mod table;
|
||||||
|
mod table_cell;
|
||||||
|
mod table_row;
|
||||||
|
mod target;
|
||||||
|
mod timestamp;
|
||||||
|
mod to_wasm;
|
||||||
|
mod underline;
|
||||||
|
mod verbatim;
|
||||||
|
mod verse_block;
|
||||||
|
|
||||||
|
pub use additional_property::AdditionalProperties;
|
||||||
|
pub use additional_property::AdditionalPropertyValue;
|
||||||
|
pub use ast_node::WasmAstNode;
|
||||||
|
#[cfg(feature = "wasm_test")]
|
||||||
|
pub use ast_node::WasmAstNodeWrapper;
|
||||||
|
#[cfg(feature = "wasm_test")]
|
||||||
|
pub use document::WasmDocument;
|
||||||
|
pub use parse_result::ParseResult;
|
||||||
|
pub(crate) use standard_properties::WasmStandardProperties;
|
||||||
|
pub use to_wasm::ToWasm;
|
||||||
|
pub use to_wasm::ToWasmContext;
|
||||||
33
src/wasm/node_property.rs
Normal file
33
src/wasm/node_property.rs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::NodeProperty;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmNodeProperty {
|
||||||
|
pub(crate) key: String,
|
||||||
|
pub(crate) value: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmNodeProperty,
|
||||||
|
NodeProperty<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::NodeProperty(original) },
|
||||||
|
{ "node-property".into() },
|
||||||
|
{
|
||||||
|
Ok((
|
||||||
|
Vec::new(),
|
||||||
|
WasmNodeProperty {
|
||||||
|
key: original.property_name.to_owned(),
|
||||||
|
value: original.value.map(|s| s.to_owned()),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
35
src/wasm/org_macro.rs
Normal file
35
src/wasm/org_macro.rs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::OrgMacro;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmOrgMacro {
|
||||||
|
pub(crate) key: String,
|
||||||
|
pub(crate) value: String,
|
||||||
|
pub(crate) args: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmOrgMacro,
|
||||||
|
OrgMacro<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::OrgMacro(original) },
|
||||||
|
{ "macro".into() },
|
||||||
|
{
|
||||||
|
Ok((
|
||||||
|
Vec::new(),
|
||||||
|
WasmOrgMacro {
|
||||||
|
key: original.key.to_lowercase(),
|
||||||
|
value: original.value.to_owned(),
|
||||||
|
args: original.get_args().map(|s| s.into_owned()).collect(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
48
src/wasm/paragraph.rs
Normal file
48
src/wasm/paragraph.rs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use super::AdditionalProperties;
|
||||||
|
use crate::types::GetAffiliatedKeywords;
|
||||||
|
use crate::types::Paragraph;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmParagraph {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) additional_properties: AdditionalProperties,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmParagraph,
|
||||||
|
Paragraph<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::Paragraph(original) },
|
||||||
|
{ "paragraph".into() },
|
||||||
|
{
|
||||||
|
let additional_properties = original
|
||||||
|
.get_affiliated_keywords()
|
||||||
|
.to_wasm(wasm_context.clone())?;
|
||||||
|
|
||||||
|
let children = original
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
children,
|
||||||
|
WasmParagraph {
|
||||||
|
additional_properties,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
15
src/wasm/parse_result.rs
Normal file
15
src/wasm/parse_result.rs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNodeWrapper;
|
||||||
|
use super::document::WasmDocument;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(tag = "status", content = "content")]
|
||||||
|
pub enum ParseResult {
|
||||||
|
#[serde(rename = "success")]
|
||||||
|
Success(WasmAstNodeWrapper<WasmDocument>),
|
||||||
|
|
||||||
|
#[serde(rename = "error")]
|
||||||
|
Error(String),
|
||||||
|
}
|
||||||
52
src/wasm/plain_link.rs
Normal file
52
src/wasm/plain_link.rs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::LinkType;
|
||||||
|
use crate::types::PlainLink;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(tag = "format")]
|
||||||
|
#[serde(rename = "plain")]
|
||||||
|
pub struct WasmPlainLink {
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub(crate) link_type: String,
|
||||||
|
pub(crate) path: String,
|
||||||
|
#[serde(rename = "raw-link")]
|
||||||
|
pub(crate) raw_link: String,
|
||||||
|
pub(crate) application: Option<String>,
|
||||||
|
#[serde(rename = "search-option")]
|
||||||
|
pub(crate) search_option: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmPlainLink,
|
||||||
|
PlainLink<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::PlainLink(original) },
|
||||||
|
{ "link".into() },
|
||||||
|
{
|
||||||
|
Ok((
|
||||||
|
Vec::new(),
|
||||||
|
WasmPlainLink {
|
||||||
|
link_type: match &original.link_type {
|
||||||
|
LinkType::File => "file".to_owned(),
|
||||||
|
LinkType::Protocol(protocol) => protocol.clone().into_owned(),
|
||||||
|
LinkType::Id => "id".to_owned(),
|
||||||
|
LinkType::CustomId => "custom-id".to_owned(),
|
||||||
|
LinkType::CodeRef => "coderef".to_owned(),
|
||||||
|
LinkType::Fuzzy => "fuzzy".to_owned(),
|
||||||
|
},
|
||||||
|
path: original.path.to_owned(),
|
||||||
|
raw_link: original.raw_link.to_owned(),
|
||||||
|
application: original.application.map(str::to_owned),
|
||||||
|
search_option: original.search_option.map(str::to_owned),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
57
src/wasm/plain_list.rs
Normal file
57
src/wasm/plain_list.rs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use super::AdditionalProperties;
|
||||||
|
use crate::types::GetAffiliatedKeywords;
|
||||||
|
use crate::types::PlainList;
|
||||||
|
use crate::types::PlainListType;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmPlainList {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) additional_properties: AdditionalProperties,
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub(crate) list_type: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmPlainList,
|
||||||
|
PlainList<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::PlainList(original) },
|
||||||
|
{ "plain-list".into() },
|
||||||
|
{
|
||||||
|
let additional_properties = original
|
||||||
|
.get_affiliated_keywords()
|
||||||
|
.to_wasm(wasm_context.clone())?;
|
||||||
|
|
||||||
|
let children = original
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
children,
|
||||||
|
WasmPlainList {
|
||||||
|
additional_properties,
|
||||||
|
list_type: match original.list_type {
|
||||||
|
PlainListType::Unordered => "unordered",
|
||||||
|
PlainListType::Ordered => "ordered",
|
||||||
|
PlainListType::Descriptive => "descriptive",
|
||||||
|
}
|
||||||
|
.to_owned(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
68
src/wasm/plain_list_item.rs
Normal file
68
src/wasm/plain_list_item.rs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::CheckboxType;
|
||||||
|
use crate::types::PlainListItem;
|
||||||
|
use crate::types::PlainListItemCounter;
|
||||||
|
use crate::types::PlainListItemPreBlank;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmPlainListItem {
|
||||||
|
pub(crate) tag: Vec<WasmAstNode>,
|
||||||
|
pub(crate) bullet: String,
|
||||||
|
pub(crate) counter: Option<PlainListItemCounter>,
|
||||||
|
pub(crate) checkbox: Option<String>,
|
||||||
|
#[serde(rename = "pre-blank")]
|
||||||
|
pub(crate) pre_blank: PlainListItemPreBlank,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmPlainListItem,
|
||||||
|
PlainListItem<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::PlainListItem(original) },
|
||||||
|
{ "item".into() },
|
||||||
|
{
|
||||||
|
let children = original
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
children,
|
||||||
|
WasmPlainListItem {
|
||||||
|
tag: original
|
||||||
|
.tag
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?,
|
||||||
|
bullet: original.bullet.to_owned(),
|
||||||
|
counter: original.counter,
|
||||||
|
checkbox: original.checkbox.as_ref().map(|(checkbox_type, _)| {
|
||||||
|
match checkbox_type {
|
||||||
|
CheckboxType::On => "on",
|
||||||
|
CheckboxType::Trans => "trans",
|
||||||
|
CheckboxType::Off => "off",
|
||||||
|
}
|
||||||
|
.to_owned()
|
||||||
|
}),
|
||||||
|
pre_blank: original.pre_blank,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
22
src/wasm/plain_text.rs
Normal file
22
src/wasm/plain_text.rs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::PlainText;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmPlainText {}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmPlainText,
|
||||||
|
PlainText<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::PlainText(original) },
|
||||||
|
{ "plain-text".into() },
|
||||||
|
{ Ok((Vec::new(), WasmPlainText {},)) }
|
||||||
|
);
|
||||||
62
src/wasm/planning.rs
Normal file
62
src/wasm/planning.rs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::Planning;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmPlanning {
|
||||||
|
pub(crate) scheduled: Option<Box<WasmAstNode>>,
|
||||||
|
pub(crate) deadline: Option<Box<WasmAstNode>>,
|
||||||
|
pub(crate) closed: Option<Box<WasmAstNode>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmPlanning,
|
||||||
|
Planning<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::Planning(original) },
|
||||||
|
{ "planning".into() },
|
||||||
|
{
|
||||||
|
Ok((
|
||||||
|
Vec::new(),
|
||||||
|
WasmPlanning {
|
||||||
|
scheduled: original
|
||||||
|
.scheduled
|
||||||
|
.as_ref()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.map_or(Ok(None), |r| r.map(Some))?
|
||||||
|
.map(Box::new),
|
||||||
|
deadline: original
|
||||||
|
.deadline
|
||||||
|
.as_ref()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.map_or(Ok(None), |r| r.map(Some))?
|
||||||
|
.map(Box::new),
|
||||||
|
closed: original
|
||||||
|
.closed
|
||||||
|
.as_ref()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.map_or(Ok(None), |r| r.map(Some))?
|
||||||
|
.map(Box::new),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
34
src/wasm/property_drawer.rs
Normal file
34
src/wasm/property_drawer.rs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::PropertyDrawer;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmPropertyDrawer {}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmPropertyDrawer,
|
||||||
|
PropertyDrawer<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::PropertyDrawer(original) },
|
||||||
|
{ "property-drawer".into() },
|
||||||
|
{
|
||||||
|
let children = original
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok((children, WasmPropertyDrawer {}))
|
||||||
|
}
|
||||||
|
);
|
||||||
48
src/wasm/quote_block.rs
Normal file
48
src/wasm/quote_block.rs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use super::AdditionalProperties;
|
||||||
|
use crate::types::GetAffiliatedKeywords;
|
||||||
|
use crate::types::QuoteBlock;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmQuoteBlock {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) additional_properties: AdditionalProperties,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmQuoteBlock,
|
||||||
|
QuoteBlock<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::QuoteBlock(original) },
|
||||||
|
{ "quote-block".into() },
|
||||||
|
{
|
||||||
|
let additional_properties = original
|
||||||
|
.get_affiliated_keywords()
|
||||||
|
.to_wasm(wasm_context.clone())?;
|
||||||
|
|
||||||
|
let children = original
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
children,
|
||||||
|
WasmQuoteBlock {
|
||||||
|
additional_properties,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
54
src/wasm/radio_link.rs
Normal file
54
src/wasm/radio_link.rs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::RadioLink;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(tag = "format")]
|
||||||
|
#[serde(rename = "plain")]
|
||||||
|
pub struct WasmRadioLink {
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub(crate) link_type: String,
|
||||||
|
pub(crate) path: String,
|
||||||
|
#[serde(rename = "raw-link")]
|
||||||
|
pub(crate) raw_link: String,
|
||||||
|
pub(crate) application: Option<String>, // Always None
|
||||||
|
#[serde(rename = "search-option")]
|
||||||
|
pub(crate) search_option: Option<String>, // Always None
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmRadioLink,
|
||||||
|
RadioLink<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::RadioLink(original) },
|
||||||
|
{ "link".into() },
|
||||||
|
{
|
||||||
|
let children = original
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
children,
|
||||||
|
WasmRadioLink {
|
||||||
|
link_type: "radio".to_owned(),
|
||||||
|
path: original.path.to_owned(),
|
||||||
|
raw_link: original.get_raw_link().to_owned(),
|
||||||
|
application: None,
|
||||||
|
search_option: None,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
41
src/wasm/radio_target.rs
Normal file
41
src/wasm/radio_target.rs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::RadioTarget;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmRadioTarget {
|
||||||
|
pub(crate) value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmRadioTarget,
|
||||||
|
RadioTarget<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::RadioTarget(original) },
|
||||||
|
{ "radio-target".into() },
|
||||||
|
{
|
||||||
|
let children = original
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
children,
|
||||||
|
WasmRadioTarget {
|
||||||
|
value: original.value.to_owned(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
67
src/wasm/regular_link.rs
Normal file
67
src/wasm/regular_link.rs
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::LinkType;
|
||||||
|
use crate::types::RegularLink;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(tag = "format")]
|
||||||
|
#[serde(rename = "bracket")]
|
||||||
|
pub struct WasmRegularLink {
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub(crate) link_type: String,
|
||||||
|
pub(crate) path: String,
|
||||||
|
#[serde(rename = "raw-link")]
|
||||||
|
pub(crate) raw_link: String,
|
||||||
|
pub(crate) application: Option<String>,
|
||||||
|
#[serde(rename = "search-option")]
|
||||||
|
pub(crate) search_option: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmRegularLink,
|
||||||
|
RegularLink<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::RegularLink(original) },
|
||||||
|
{ "link".into() },
|
||||||
|
{
|
||||||
|
let children = original
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
children,
|
||||||
|
WasmRegularLink {
|
||||||
|
link_type: match &original.link_type {
|
||||||
|
LinkType::File => "file".to_owned(),
|
||||||
|
LinkType::Protocol(protocol) => protocol.clone().into_owned(),
|
||||||
|
LinkType::Id => "id".to_owned(),
|
||||||
|
LinkType::CustomId => "custom-id".to_owned(),
|
||||||
|
LinkType::CodeRef => "coderef".to_owned(),
|
||||||
|
LinkType::Fuzzy => "fuzzy".to_owned(),
|
||||||
|
},
|
||||||
|
path: original.get_path().into_owned(),
|
||||||
|
raw_link: original.get_raw_link().into_owned(),
|
||||||
|
application: original
|
||||||
|
.application
|
||||||
|
.as_ref()
|
||||||
|
.map(|c| c.clone().into_owned()),
|
||||||
|
search_option: original.get_search_option().map(Cow::into_owned),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
34
src/wasm/section.rs
Normal file
34
src/wasm/section.rs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::Section;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmSection {}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmSection,
|
||||||
|
Section<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::Section(original) },
|
||||||
|
{ "section".into() },
|
||||||
|
{
|
||||||
|
let children = original
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok((children, WasmSection {}))
|
||||||
|
}
|
||||||
|
);
|
||||||
56
src/wasm/special_block.rs
Normal file
56
src/wasm/special_block.rs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::headline::Noop;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use super::AdditionalProperties;
|
||||||
|
use crate::types::GetAffiliatedKeywords;
|
||||||
|
use crate::types::SpecialBlock;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmSpecialBlock {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) additional_properties: AdditionalProperties,
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub(crate) block_type: String,
|
||||||
|
pub(crate) parameters: Option<String>,
|
||||||
|
pub(crate) results: Noop,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmSpecialBlock,
|
||||||
|
SpecialBlock<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::SpecialBlock(original) },
|
||||||
|
{ "special-block".into() },
|
||||||
|
{
|
||||||
|
let additional_properties = original
|
||||||
|
.get_affiliated_keywords()
|
||||||
|
.to_wasm(wasm_context.clone())?;
|
||||||
|
|
||||||
|
let children = original
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
children,
|
||||||
|
WasmSpecialBlock {
|
||||||
|
additional_properties,
|
||||||
|
block_type: original.block_type.to_owned(),
|
||||||
|
parameters: original.parameters.map(|s| s.to_owned()),
|
||||||
|
results: Noop {},
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
100
src/wasm/src_block.rs
Normal file
100
src/wasm/src_block.rs
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use super::AdditionalProperties;
|
||||||
|
use crate::types::CharOffsetInLine;
|
||||||
|
use crate::types::GetAffiliatedKeywords;
|
||||||
|
use crate::types::LineNumber;
|
||||||
|
use crate::types::RetainLabels;
|
||||||
|
use crate::types::SrcBlock;
|
||||||
|
use crate::types::SwitchNumberLines;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmSrcBlock {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) additional_properties: AdditionalProperties,
|
||||||
|
pub(crate) language: Option<String>,
|
||||||
|
pub(crate) value: String,
|
||||||
|
pub(crate) switches: Option<String>,
|
||||||
|
pub(crate) parameters: Option<String>,
|
||||||
|
#[serde(rename = "number-lines")]
|
||||||
|
pub(crate) number_lines: Option<WasmNumberLinesWrapper>,
|
||||||
|
#[serde(rename = "preserve-indent")]
|
||||||
|
pub(crate) preserve_indent: Option<CharOffsetInLine>,
|
||||||
|
#[serde(rename = "retain-labels")]
|
||||||
|
pub(crate) retain_labels: WasmRetainLabels,
|
||||||
|
#[serde(rename = "use-labels")]
|
||||||
|
pub(crate) use_labels: bool,
|
||||||
|
#[serde(rename = "label-fmt")]
|
||||||
|
pub(crate) label_format: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub(crate) enum WasmRetainLabels {
|
||||||
|
YesNo(bool),
|
||||||
|
Keep(CharOffsetInLine),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub(crate) enum WasmNumberLines {
|
||||||
|
#[serde(rename = "new")]
|
||||||
|
New(LineNumber),
|
||||||
|
#[serde(rename = "continued")]
|
||||||
|
Continued(LineNumber),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(rename = "number-lines")]
|
||||||
|
#[serde(tag = "number-lines")]
|
||||||
|
pub(crate) struct WasmNumberLinesWrapper {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) inner: WasmNumberLines,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmSrcBlock,
|
||||||
|
SrcBlock<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::SrcBlock(original) },
|
||||||
|
{ "src-block".into() },
|
||||||
|
{
|
||||||
|
let additional_properties = original
|
||||||
|
.get_affiliated_keywords()
|
||||||
|
.to_wasm(wasm_context.clone())?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
Vec::new(),
|
||||||
|
WasmSrcBlock {
|
||||||
|
additional_properties,
|
||||||
|
language: original.language.map(|s| s.to_owned()),
|
||||||
|
value: original.get_value().into_owned(),
|
||||||
|
switches: original.switches.map(|s| s.to_owned()),
|
||||||
|
parameters: original.parameters.map(|s| s.to_owned()),
|
||||||
|
number_lines: match original.number_lines {
|
||||||
|
None => None,
|
||||||
|
Some(SwitchNumberLines::New(n)) => Some(WasmNumberLinesWrapper {
|
||||||
|
inner: WasmNumberLines::New(n),
|
||||||
|
}),
|
||||||
|
Some(SwitchNumberLines::Continued(n)) => Some(WasmNumberLinesWrapper {
|
||||||
|
inner: WasmNumberLines::Continued(n),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
preserve_indent: original.preserve_indent,
|
||||||
|
retain_labels: match original.retain_labels {
|
||||||
|
RetainLabels::No => WasmRetainLabels::YesNo(false),
|
||||||
|
RetainLabels::Yes => WasmRetainLabels::YesNo(true),
|
||||||
|
RetainLabels::Keep(n) => WasmRetainLabels::Keep(n),
|
||||||
|
},
|
||||||
|
use_labels: original.use_labels,
|
||||||
|
label_format: original.label_format.map(|s| s.to_owned()),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
74
src/wasm/standard_properties.rs
Normal file
74
src/wasm/standard_properties.rs
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::to_wasm::ToWasmContext;
|
||||||
|
use super::to_wasm::ToWasmStandardProperties;
|
||||||
|
use crate::types::PostBlank;
|
||||||
|
use crate::types::StandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub(crate) struct WasmStandardProperties {
|
||||||
|
pub(crate) begin: usize,
|
||||||
|
pub(crate) end: usize,
|
||||||
|
#[serde(rename = "contents-begin")]
|
||||||
|
pub(crate) contents_begin: Option<usize>,
|
||||||
|
#[serde(rename = "contents-end")]
|
||||||
|
pub(crate) contents_end: Option<usize>,
|
||||||
|
#[serde(rename = "post-blank")]
|
||||||
|
pub(crate) post_blank: PostBlank,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'s, SP: StandardProperties<'s>> ToWasmStandardProperties for SP {
|
||||||
|
type Output = WasmStandardProperties;
|
||||||
|
|
||||||
|
fn to_wasm_standard_properties(
|
||||||
|
&self,
|
||||||
|
wasm_context: ToWasmContext<'_>,
|
||||||
|
) -> Result<Self::Output, crate::error::CustomError> {
|
||||||
|
let (begin, end) = get_char_indices(wasm_context.full_document, self.get_source());
|
||||||
|
let (contents_begin, contents_end) = match self.get_contents() {
|
||||||
|
Some(contents) => {
|
||||||
|
let (begin, end) = get_char_indices(wasm_context.full_document, contents);
|
||||||
|
(Some(begin), Some(end))
|
||||||
|
}
|
||||||
|
None => (None, None),
|
||||||
|
};
|
||||||
|
|
||||||
|
let post_blank = self.get_post_blank();
|
||||||
|
Ok(WasmStandardProperties {
|
||||||
|
begin,
|
||||||
|
end,
|
||||||
|
contents_begin,
|
||||||
|
contents_end,
|
||||||
|
post_blank,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_char_indices(original_document: &str, subset: &str) -> (usize, usize) {
|
||||||
|
let (begin_byte, end_byte) = get_rust_byte_offsets(original_document, subset);
|
||||||
|
// Add 1 to make this 1-based to match emacs.
|
||||||
|
let begin_char = original_document[..begin_byte].chars().count() + 1;
|
||||||
|
// Also 1-based:
|
||||||
|
let end_char = begin_char + original_document[begin_byte..end_byte].chars().count();
|
||||||
|
(begin_char, end_char)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the byte offset into source that the string slice exists at.
|
||||||
|
///
|
||||||
|
/// These offsets are zero-based.
|
||||||
|
fn get_rust_byte_offsets(original_document: &str, subset: &str) -> (usize, usize) {
|
||||||
|
debug_assert!(is_slice_of(original_document, subset));
|
||||||
|
let offset = subset.as_ptr() as usize - original_document.as_ptr() as usize;
|
||||||
|
let end = offset + subset.len();
|
||||||
|
(offset, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if the child string slice is a slice of the parent string slice.
|
||||||
|
fn is_slice_of(parent: &str, child: &str) -> bool {
|
||||||
|
let parent_start = parent.as_ptr() as usize;
|
||||||
|
let parent_end = parent_start + parent.len();
|
||||||
|
let child_start = child.as_ptr() as usize;
|
||||||
|
let child_end = child_start + child.len();
|
||||||
|
child_start >= parent_start && child_end <= parent_end
|
||||||
|
}
|
||||||
31
src/wasm/statistics_cookie.rs
Normal file
31
src/wasm/statistics_cookie.rs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::StatisticsCookie;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmStatisticsCookie {
|
||||||
|
pub(crate) value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmStatisticsCookie,
|
||||||
|
StatisticsCookie<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::StatisticsCookie(original) },
|
||||||
|
{ "statistics-cookie".into() },
|
||||||
|
{
|
||||||
|
Ok((
|
||||||
|
Vec::new(),
|
||||||
|
WasmStatisticsCookie {
|
||||||
|
value: original.value.to_owned(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
34
src/wasm/strike_through.rs
Normal file
34
src/wasm/strike_through.rs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::StrikeThrough;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmStrikeThrough {}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmStrikeThrough,
|
||||||
|
StrikeThrough<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::StrikeThrough(original) },
|
||||||
|
{ "strike-through".into() },
|
||||||
|
{
|
||||||
|
let children = original
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok((children, WasmStrikeThrough {}))
|
||||||
|
}
|
||||||
|
);
|
||||||
42
src/wasm/subscript.rs
Normal file
42
src/wasm/subscript.rs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::Subscript;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmSubscript {
|
||||||
|
#[serde(rename = "use-brackets-p")]
|
||||||
|
pub(crate) use_brackets: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmSubscript,
|
||||||
|
Subscript<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::Subscript(original) },
|
||||||
|
{ "subscript".into() },
|
||||||
|
{
|
||||||
|
let children = original
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
children,
|
||||||
|
WasmSubscript {
|
||||||
|
use_brackets: original.use_brackets,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
42
src/wasm/superscript.rs
Normal file
42
src/wasm/superscript.rs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::Superscript;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmSuperscript {
|
||||||
|
#[serde(rename = "use-brackets-p")]
|
||||||
|
pub(crate) use_brackets: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmSuperscript,
|
||||||
|
Superscript<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::Superscript(original) },
|
||||||
|
{ "superscript".into() },
|
||||||
|
{
|
||||||
|
let children = original
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
children,
|
||||||
|
WasmSuperscript {
|
||||||
|
use_brackets: original.use_brackets,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
75
src/wasm/table.rs
Normal file
75
src/wasm/table.rs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use super::AdditionalProperties;
|
||||||
|
use crate::types::GetAffiliatedKeywords;
|
||||||
|
use crate::types::Table;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmTable {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) additional_properties: AdditionalProperties,
|
||||||
|
#[serde(rename = "tblfm")]
|
||||||
|
pub(crate) formulas: Option<WasmStringSet>,
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub(crate) table_type: String,
|
||||||
|
pub(crate) value: Option<String>, // Always None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(tag = "string-set")]
|
||||||
|
#[serde(rename = "string-set")]
|
||||||
|
pub(crate) struct WasmStringSet {
|
||||||
|
value: BTreeSet<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmTable,
|
||||||
|
Table<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::Table(original) },
|
||||||
|
{ "table".into() },
|
||||||
|
{
|
||||||
|
let additional_properties = original
|
||||||
|
.get_affiliated_keywords()
|
||||||
|
.to_wasm(wasm_context.clone())?;
|
||||||
|
|
||||||
|
let children = original
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
children,
|
||||||
|
WasmTable {
|
||||||
|
additional_properties,
|
||||||
|
formulas: if original.formulas.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(WasmStringSet {
|
||||||
|
value: original
|
||||||
|
.formulas
|
||||||
|
.iter()
|
||||||
|
.map(|kw| kw.value.to_owned())
|
||||||
|
.collect(),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
table_type: "org".to_owned(),
|
||||||
|
value: None,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
34
src/wasm/table_cell.rs
Normal file
34
src/wasm/table_cell.rs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::TableCell;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmTableCell {}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmTableCell,
|
||||||
|
TableCell<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::TableCell(original) },
|
||||||
|
{ "table-cell".into() },
|
||||||
|
{
|
||||||
|
let children = original
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok((children, WasmTableCell {}))
|
||||||
|
}
|
||||||
|
);
|
||||||
47
src/wasm/table_row.rs
Normal file
47
src/wasm/table_row.rs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::TableRow;
|
||||||
|
use crate::types::TableRowType;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmTableRow {
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub(crate) row_type: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmTableRow,
|
||||||
|
TableRow<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::TableRow(original) },
|
||||||
|
{ "table-row".into() },
|
||||||
|
{
|
||||||
|
let children = original
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
children,
|
||||||
|
WasmTableRow {
|
||||||
|
row_type: match original.get_type() {
|
||||||
|
TableRowType::Standard => "standard",
|
||||||
|
TableRowType::Rule => "rule",
|
||||||
|
}
|
||||||
|
.to_owned(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
31
src/wasm/target.rs
Normal file
31
src/wasm/target.rs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::Target;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmTarget {
|
||||||
|
pub(crate) value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmTarget,
|
||||||
|
Target<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::Target(original) },
|
||||||
|
{ "target".into() },
|
||||||
|
{
|
||||||
|
Ok((
|
||||||
|
Vec::new(),
|
||||||
|
WasmTarget {
|
||||||
|
value: original.value.to_owned(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
146
src/wasm/timestamp.rs
Normal file
146
src/wasm/timestamp.rs
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::DayOfMonthInner;
|
||||||
|
use crate::types::HourInner;
|
||||||
|
use crate::types::MinuteInner;
|
||||||
|
use crate::types::MonthInner;
|
||||||
|
use crate::types::RepeaterType;
|
||||||
|
use crate::types::RepeaterWarningDelayValueType;
|
||||||
|
use crate::types::TimeUnit;
|
||||||
|
use crate::types::Timestamp;
|
||||||
|
use crate::types::TimestampRangeType;
|
||||||
|
use crate::types::TimestampType;
|
||||||
|
use crate::types::WarningDelayType;
|
||||||
|
use crate::types::YearInner;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmTimestamp {
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub(crate) timestamp_type: String,
|
||||||
|
#[serde(rename = "range-type")]
|
||||||
|
pub(crate) range_type: Option<String>,
|
||||||
|
#[serde(rename = "raw-value")]
|
||||||
|
pub(crate) raw_value: String,
|
||||||
|
#[serde(rename = "year-start")]
|
||||||
|
pub(crate) year_start: Option<YearInner>,
|
||||||
|
#[serde(rename = "month-start")]
|
||||||
|
pub(crate) month_start: Option<MonthInner>,
|
||||||
|
#[serde(rename = "day-start")]
|
||||||
|
pub(crate) day_of_month_start: Option<DayOfMonthInner>,
|
||||||
|
#[serde(rename = "hour-start")]
|
||||||
|
pub(crate) hour_start: Option<HourInner>,
|
||||||
|
#[serde(rename = "minute-start")]
|
||||||
|
pub(crate) minute_start: Option<MinuteInner>,
|
||||||
|
#[serde(rename = "year-end")]
|
||||||
|
pub(crate) year_end: Option<YearInner>,
|
||||||
|
#[serde(rename = "month-end")]
|
||||||
|
pub(crate) month_end: Option<MonthInner>,
|
||||||
|
#[serde(rename = "day-end")]
|
||||||
|
pub(crate) day_of_month_end: Option<DayOfMonthInner>,
|
||||||
|
#[serde(rename = "hour-end")]
|
||||||
|
pub(crate) hour_end: Option<HourInner>,
|
||||||
|
#[serde(rename = "minute-end")]
|
||||||
|
pub(crate) minute_end: Option<MinuteInner>,
|
||||||
|
#[serde(rename = "repeater-type")]
|
||||||
|
pub(crate) repeater_type: Option<String>,
|
||||||
|
#[serde(rename = "repeater-unit")]
|
||||||
|
pub(crate) repeater_unit: Option<String>,
|
||||||
|
#[serde(rename = "repeater-value")]
|
||||||
|
pub(crate) repeater_value: Option<RepeaterWarningDelayValueType>,
|
||||||
|
#[serde(rename = "warning-type")]
|
||||||
|
pub(crate) warning_type: Option<String>,
|
||||||
|
#[serde(rename = "warning-unit")]
|
||||||
|
pub(crate) warning_unit: Option<String>,
|
||||||
|
#[serde(rename = "warning-value")]
|
||||||
|
pub(crate) warning_value: Option<RepeaterWarningDelayValueType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmTimestamp,
|
||||||
|
Timestamp<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::Timestamp(original) },
|
||||||
|
{ "timestamp".into() },
|
||||||
|
{
|
||||||
|
Ok((
|
||||||
|
Vec::new(),
|
||||||
|
WasmTimestamp {
|
||||||
|
timestamp_type: match original.timestamp_type {
|
||||||
|
TimestampType::Diary => "diary",
|
||||||
|
TimestampType::Active => "active",
|
||||||
|
TimestampType::Inactive => "inactive",
|
||||||
|
TimestampType::ActiveRange => "active-range",
|
||||||
|
TimestampType::InactiveRange => "inactive-range",
|
||||||
|
}
|
||||||
|
.to_owned(),
|
||||||
|
range_type: match original.range_type {
|
||||||
|
TimestampRangeType::DateRange => Some("daterange".to_owned()),
|
||||||
|
TimestampRangeType::TimeRange => Some("timerange".to_owned()),
|
||||||
|
TimestampRangeType::None => None,
|
||||||
|
},
|
||||||
|
raw_value: original.get_raw_value().to_owned(),
|
||||||
|
year_start: original.start.as_ref().map(|date| date.get_year().0),
|
||||||
|
month_start: original.start.as_ref().map(|date| date.get_month().0),
|
||||||
|
day_of_month_start: original
|
||||||
|
.start
|
||||||
|
.as_ref()
|
||||||
|
.map(|date| date.get_day_of_month().0),
|
||||||
|
hour_start: original.start_time.as_ref().map(|time| time.get_hour().0),
|
||||||
|
minute_start: original.start_time.as_ref().map(|time| time.get_minute().0),
|
||||||
|
year_end: original.end.as_ref().map(|date| date.get_year().0),
|
||||||
|
month_end: original.end.as_ref().map(|date| date.get_month().0),
|
||||||
|
day_of_month_end: original.end.as_ref().map(|date| date.get_day_of_month().0),
|
||||||
|
hour_end: original.end_time.as_ref().map(|time| time.get_hour().0),
|
||||||
|
minute_end: original.end_time.as_ref().map(|time| time.get_minute().0),
|
||||||
|
repeater_type: original
|
||||||
|
.repeater
|
||||||
|
.as_ref()
|
||||||
|
.map(|repeater| match repeater.repeater_type {
|
||||||
|
RepeaterType::Cumulative => "cumulate",
|
||||||
|
RepeaterType::CatchUp => "catch-up",
|
||||||
|
RepeaterType::Restart => "restart",
|
||||||
|
})
|
||||||
|
.map(|text| text.to_owned()),
|
||||||
|
repeater_unit: original
|
||||||
|
.repeater
|
||||||
|
.as_ref()
|
||||||
|
.map(|repeater| match repeater.unit {
|
||||||
|
TimeUnit::Hour => "hour",
|
||||||
|
TimeUnit::Day => "day",
|
||||||
|
TimeUnit::Week => "week",
|
||||||
|
TimeUnit::Month => "month",
|
||||||
|
TimeUnit::Year => "year",
|
||||||
|
})
|
||||||
|
.map(|text| text.to_owned()),
|
||||||
|
repeater_value: original.repeater.as_ref().map(|repeater| repeater.value),
|
||||||
|
warning_type: original
|
||||||
|
.warning_delay
|
||||||
|
.as_ref()
|
||||||
|
.map(|warning| match warning.warning_delay_type {
|
||||||
|
WarningDelayType::All => "all",
|
||||||
|
WarningDelayType::First => "first",
|
||||||
|
})
|
||||||
|
.map(|text| text.to_owned()),
|
||||||
|
warning_unit: original
|
||||||
|
.warning_delay
|
||||||
|
.as_ref()
|
||||||
|
.map(|warning| match warning.unit {
|
||||||
|
TimeUnit::Hour => "hour",
|
||||||
|
TimeUnit::Day => "day",
|
||||||
|
TimeUnit::Week => "week",
|
||||||
|
TimeUnit::Month => "month",
|
||||||
|
TimeUnit::Year => "year",
|
||||||
|
})
|
||||||
|
.map(|text| text.to_owned()),
|
||||||
|
warning_value: original.warning_delay.as_ref().map(|warning| warning.value),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
124
src/wasm/to_wasm.rs
Normal file
124
src/wasm/to_wasm.rs
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::WasmAstNode;
|
||||||
|
use crate::error::CustomError;
|
||||||
|
use crate::types::DocumentElement;
|
||||||
|
use crate::types::Element;
|
||||||
|
use crate::types::Object;
|
||||||
|
|
||||||
|
pub trait ToWasm {
|
||||||
|
type Output;
|
||||||
|
|
||||||
|
fn to_wasm(&self, full_document: ToWasmContext<'_>) -> Result<Self::Output, CustomError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) trait ToWasmStandardProperties {
|
||||||
|
type Output;
|
||||||
|
|
||||||
|
fn to_wasm_standard_properties(
|
||||||
|
&self,
|
||||||
|
wasm_context: ToWasmContext<'_>,
|
||||||
|
) -> Result<Self::Output, CustomError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ToWasmContext<'s> {
|
||||||
|
pub(crate) full_document: &'s str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'s> ToWasmContext<'s> {
|
||||||
|
pub fn new(full_document: &'s str) -> ToWasmContext<'s> {
|
||||||
|
ToWasmContext { full_document }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(WasmAstNode, DocumentElement<'s>, original, wasm_context, {
|
||||||
|
match original {
|
||||||
|
DocumentElement::Heading(inner) => {
|
||||||
|
inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into)
|
||||||
|
}
|
||||||
|
DocumentElement::Section(inner) => {
|
||||||
|
inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
to_wasm!(WasmAstNode, Element<'s>, original, wasm_context, {
|
||||||
|
match original {
|
||||||
|
Element::Paragraph(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Element::PlainList(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Element::CenterBlock(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Element::QuoteBlock(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Element::SpecialBlock(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Element::DynamicBlock(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Element::FootnoteDefinition(inner) => {
|
||||||
|
inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into)
|
||||||
|
}
|
||||||
|
Element::Comment(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Element::Drawer(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Element::PropertyDrawer(inner) => {
|
||||||
|
inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into)
|
||||||
|
}
|
||||||
|
Element::Table(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Element::VerseBlock(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Element::CommentBlock(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Element::ExampleBlock(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Element::ExportBlock(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Element::SrcBlock(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Element::Clock(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Element::DiarySexp(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Element::Planning(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Element::FixedWidthArea(inner) => {
|
||||||
|
inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into)
|
||||||
|
}
|
||||||
|
Element::HorizontalRule(inner) => {
|
||||||
|
inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into)
|
||||||
|
}
|
||||||
|
Element::Keyword(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Element::BabelCall(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Element::LatexEnvironment(inner) => {
|
||||||
|
inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
to_wasm!(WasmAstNode, Object<'s>, original, wasm_context, {
|
||||||
|
match original {
|
||||||
|
Object::Bold(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Object::Italic(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Object::Underline(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Object::StrikeThrough(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Object::Code(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Object::Verbatim(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Object::PlainText(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Object::RegularLink(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Object::RadioLink(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Object::RadioTarget(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Object::PlainLink(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Object::AngleLink(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Object::OrgMacro(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Object::Entity(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Object::LatexFragment(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Object::ExportSnippet(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Object::FootnoteReference(inner) => {
|
||||||
|
inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into)
|
||||||
|
}
|
||||||
|
Object::Citation(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Object::CitationReference(inner) => {
|
||||||
|
inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into)
|
||||||
|
}
|
||||||
|
Object::InlineBabelCall(inner) => {
|
||||||
|
inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into)
|
||||||
|
}
|
||||||
|
Object::InlineSourceBlock(inner) => {
|
||||||
|
inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into)
|
||||||
|
}
|
||||||
|
Object::LineBreak(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Object::Target(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Object::StatisticsCookie(inner) => {
|
||||||
|
inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into)
|
||||||
|
}
|
||||||
|
Object::Subscript(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Object::Superscript(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
Object::Timestamp(inner) => inner.to_wasm(wasm_context).map(Into::<WasmAstNode>::into),
|
||||||
|
}
|
||||||
|
});
|
||||||
34
src/wasm/underline.rs
Normal file
34
src/wasm/underline.rs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::Underline;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmUnderline {}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmUnderline,
|
||||||
|
Underline<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::Underline(original) },
|
||||||
|
{ "underline".into() },
|
||||||
|
{
|
||||||
|
let children = original
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok((children, WasmUnderline {}))
|
||||||
|
}
|
||||||
|
);
|
||||||
31
src/wasm/verbatim.rs
Normal file
31
src/wasm/verbatim.rs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::Verbatim;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmVerbatim {
|
||||||
|
pub(crate) value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmVerbatim,
|
||||||
|
Verbatim<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::Verbatim(original) },
|
||||||
|
{ "verbatim".into() },
|
||||||
|
{
|
||||||
|
Ok((
|
||||||
|
Vec::new(),
|
||||||
|
WasmVerbatim {
|
||||||
|
value: original.contents.to_owned(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
48
src/wasm/verse_block.rs
Normal file
48
src/wasm/verse_block.rs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use super::AdditionalProperties;
|
||||||
|
use crate::types::GetAffiliatedKeywords;
|
||||||
|
use crate::types::VerseBlock;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmVerseBlock {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) additional_properties: AdditionalProperties,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmVerseBlock,
|
||||||
|
VerseBlock<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::VerseBlock(original) },
|
||||||
|
{ "verse-block".into() },
|
||||||
|
{
|
||||||
|
let additional_properties = original
|
||||||
|
.get_affiliated_keywords()
|
||||||
|
.to_wasm(wasm_context.clone())?;
|
||||||
|
|
||||||
|
let children = original
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
children,
|
||||||
|
WasmVerseBlock {
|
||||||
|
additional_properties,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
25
src/wasm_cli/mod.rs
Normal file
25
src/wasm_cli/mod.rs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
use gloo_utils::format::JsValueSerdeExt;
|
||||||
|
use wasm_bindgen::JsValue;
|
||||||
|
|
||||||
|
use crate::parser::parse_with_settings;
|
||||||
|
use crate::settings::GlobalSettings;
|
||||||
|
use crate::wasm::ParseResult;
|
||||||
|
use crate::wasm::ToWasm;
|
||||||
|
use crate::wasm::ToWasmContext;
|
||||||
|
|
||||||
|
pub fn parse_org(org_contents: &str) -> wasm_bindgen::JsValue {
|
||||||
|
let rust_parsed = match parse_with_settings(org_contents, &GlobalSettings::default()) {
|
||||||
|
Ok(document) => document,
|
||||||
|
Err(err) => {
|
||||||
|
return JsValue::from_serde(&ParseResult::Error(format!("{:?}", err))).unwrap();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let to_wasm_context = ToWasmContext::new(org_contents);
|
||||||
|
let wasm_document = match rust_parsed.to_wasm(to_wasm_context) {
|
||||||
|
Ok(document) => document,
|
||||||
|
Err(err) => {
|
||||||
|
return JsValue::from_serde(&ParseResult::Error(format!("{:?}", err))).unwrap();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
JsValue::from_serde(&ParseResult::Success(wasm_document)).unwrap()
|
||||||
|
}
|
||||||
912
src/wasm_test/compare.rs
Normal file
912
src/wasm_test/compare.rs
Normal file
@@ -0,0 +1,912 @@
|
|||||||
|
use std::borrow::Borrow;
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::collections::BTreeSet;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use super::diff::WasmDiffResult;
|
||||||
|
use super::diff::WasmDiffStatus;
|
||||||
|
use crate::util::elisp::maybe_token_to_usize;
|
||||||
|
use crate::util::elisp::unquote;
|
||||||
|
use crate::util::elisp::EmacsStandardProperties;
|
||||||
|
use crate::util::elisp::TextWithProperties;
|
||||||
|
use crate::util::elisp::Token;
|
||||||
|
use crate::wasm::WasmAstNodeWrapper;
|
||||||
|
use crate::wasm::WasmDocument;
|
||||||
|
|
||||||
|
pub fn wasm_compare_document<'s>(
|
||||||
|
source: &'s str,
|
||||||
|
emacs: &Token<'s>,
|
||||||
|
wasm: &WasmAstNodeWrapper<WasmDocument>,
|
||||||
|
) -> Result<WasmDiffResult<'s>, Box<dyn std::error::Error>> {
|
||||||
|
let wasm_json = serde_json::to_string(&wasm)?;
|
||||||
|
let wasm_json_parsed = serde_json::from_str(&wasm_json)?;
|
||||||
|
compare_json_value(source, emacs, &wasm_json_parsed)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compare_json_value<'s>(
|
||||||
|
source: &'s str,
|
||||||
|
emacs: &Token<'s>,
|
||||||
|
wasm: &serde_json::Value,
|
||||||
|
) -> Result<WasmDiffResult<'s>, Box<dyn std::error::Error>> {
|
||||||
|
// println!("XXXXXXXXXXXXXX compare_json_value XXXXXXXXXXXXXX");
|
||||||
|
// println!("{:?}", emacs);
|
||||||
|
// println!("{:?}", wasm);
|
||||||
|
// println!("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
|
||||||
|
match (wasm, emacs) {
|
||||||
|
(serde_json::Value::Object(wasm), Token::List(el)) if wasm.contains_key("ast-node") => {
|
||||||
|
// We hit a regular ast node.
|
||||||
|
compare_ast_node(source, el, wasm)
|
||||||
|
}
|
||||||
|
(serde_json::Value::String(w), Token::Atom(e))
|
||||||
|
if e.starts_with('"') && e.ends_with('"') =>
|
||||||
|
{
|
||||||
|
// We hit a string compared against a quoted string from elisp (as opposed to an unquoted literal).
|
||||||
|
compare_quoted_string(source, e, w)
|
||||||
|
}
|
||||||
|
(serde_json::Value::Array(w), Token::List(e)) => {
|
||||||
|
// TODO: This is creating children with no names.
|
||||||
|
wasm_compare_list(source, e.iter(), w.iter())
|
||||||
|
}
|
||||||
|
(serde_json::Value::Object(wasm), Token::List(e))
|
||||||
|
if wasm.contains_key("optval") && wasm.contains_key("val") =>
|
||||||
|
{
|
||||||
|
compare_optional_pair(source, e, wasm)
|
||||||
|
}
|
||||||
|
(serde_json::Value::Object(wasm), Token::List(el)) if wasm.contains_key("object-tree") => {
|
||||||
|
// We hit an object tree additional property.
|
||||||
|
compare_object_tree(source, el, wasm)
|
||||||
|
}
|
||||||
|
(serde_json::Value::Object(wasm), Token::List(el)) if wasm.contains_key("number-lines") => {
|
||||||
|
// We hit an object tree additional property.
|
||||||
|
compare_number_lines(source, el, wasm)
|
||||||
|
}
|
||||||
|
(serde_json::Value::Object(wasm), Token::List(el)) if wasm.contains_key("string-set") => {
|
||||||
|
// We hit an object tree additional property.
|
||||||
|
compare_string_set(source, el, wasm)
|
||||||
|
}
|
||||||
|
(serde_json::Value::Object(w), Token::TextWithProperties(e)) if is_plain_text(w) => {
|
||||||
|
compare_plain_text(source, e, w)
|
||||||
|
}
|
||||||
|
(serde_json::Value::Null, Token::Atom("nil")) => Ok(WasmDiffResult::default()),
|
||||||
|
(serde_json::Value::Bool(false), Token::Atom("nil")) => Ok(WasmDiffResult::default()),
|
||||||
|
(serde_json::Value::Bool(true), Token::Atom(e)) if (*e) != "nil" => {
|
||||||
|
Ok(WasmDiffResult::default())
|
||||||
|
}
|
||||||
|
(serde_json::Value::Bool(w), Token::Atom(e)) => {
|
||||||
|
let mut result = WasmDiffResult::default();
|
||||||
|
result.status.push(WasmDiffStatus::Bad(
|
||||||
|
format!(
|
||||||
|
"Value mismatch. Emacs=({emacs:?}) Wasm=({wasm:?}).",
|
||||||
|
emacs = e,
|
||||||
|
wasm = w,
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
(serde_json::Value::Number(w), Token::Atom(e)) if w.to_string().as_str() == (*e) => {
|
||||||
|
Ok(WasmDiffResult::default())
|
||||||
|
}
|
||||||
|
(serde_json::Value::Number(w), Token::Atom(e)) => {
|
||||||
|
let mut result = WasmDiffResult::default();
|
||||||
|
result.status.push(WasmDiffStatus::Bad(
|
||||||
|
format!(
|
||||||
|
"Value mismatch. Emacs=({emacs:?}) Wasm=({wasm:?}).",
|
||||||
|
emacs = e,
|
||||||
|
wasm = w,
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
(serde_json::Value::Array(w), Token::Atom("nil")) if w.is_empty() => {
|
||||||
|
Ok(WasmDiffResult::default())
|
||||||
|
}
|
||||||
|
(serde_json::Value::String(w), Token::Atom(e)) if w.as_str() == *e => {
|
||||||
|
Ok(WasmDiffResult::default())
|
||||||
|
}
|
||||||
|
(serde_json::Value::Object(w), _) if w.contains_key("noop") => {
|
||||||
|
Ok(WasmDiffResult::default())
|
||||||
|
}
|
||||||
|
(serde_json::Value::Null, Token::Atom(_)) => todo!(),
|
||||||
|
(serde_json::Value::Null, Token::List(_)) => todo!(),
|
||||||
|
(serde_json::Value::Null, Token::TextWithProperties(_)) => todo!(),
|
||||||
|
(serde_json::Value::Null, Token::Vector(_)) => todo!(),
|
||||||
|
// (serde_json::Value::Bool(_), Token::Atom(_)) => todo!(),
|
||||||
|
(serde_json::Value::Bool(_), Token::List(_)) => todo!(),
|
||||||
|
(serde_json::Value::Bool(_), Token::TextWithProperties(_)) => todo!(),
|
||||||
|
(serde_json::Value::Bool(_), Token::Vector(_)) => todo!(),
|
||||||
|
// (serde_json::Value::Number(_), Token::Atom(_)) => todo!(),
|
||||||
|
(serde_json::Value::Number(_), Token::List(_)) => todo!(),
|
||||||
|
(serde_json::Value::Number(_), Token::TextWithProperties(_)) => todo!(),
|
||||||
|
(serde_json::Value::Number(_), Token::Vector(_)) => todo!(),
|
||||||
|
// (serde_json::Value::String(_), Token::Atom(_)) => todo!(),
|
||||||
|
(serde_json::Value::String(w), Token::Atom(e)) => {
|
||||||
|
let mut result = WasmDiffResult::default();
|
||||||
|
result.status.push(WasmDiffStatus::Bad(
|
||||||
|
format!(
|
||||||
|
"Value mismatch. Emacs=({emacs:?}) Wasm=({wasm:?}).",
|
||||||
|
emacs = e,
|
||||||
|
wasm = w,
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
(serde_json::Value::String(_), Token::List(_)) => todo!(),
|
||||||
|
(serde_json::Value::String(_), Token::TextWithProperties(_)) => todo!(),
|
||||||
|
(serde_json::Value::String(_), Token::Vector(_)) => todo!(),
|
||||||
|
(serde_json::Value::Array(_), Token::Atom(_)) => todo!(),
|
||||||
|
// (serde_json::Value::Array(_), Token::List(_)) => todo!(),
|
||||||
|
(serde_json::Value::Array(_), Token::TextWithProperties(_)) => todo!(),
|
||||||
|
(serde_json::Value::Array(_), Token::Vector(_)) => todo!(),
|
||||||
|
(serde_json::Value::Object(_), Token::Atom(_)) => todo!(),
|
||||||
|
(serde_json::Value::Object(_), Token::List(_)) => todo!(),
|
||||||
|
(serde_json::Value::Object(_), Token::TextWithProperties(_)) => todo!(),
|
||||||
|
(serde_json::Value::Object(_), Token::Vector(_)) => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compare_optional_json_value<'s>(
|
||||||
|
source: &'s str,
|
||||||
|
emacs: Option<&Token<'s>>,
|
||||||
|
wasm: Option<&serde_json::Value>,
|
||||||
|
) -> Result<WasmDiffResult<'s>, Box<dyn std::error::Error>> {
|
||||||
|
match (emacs, wasm) {
|
||||||
|
(None, None) | (None, Some(serde_json::Value::Null)) | (Some(Token::Atom("nil")), None) => {
|
||||||
|
Ok(WasmDiffResult::default())
|
||||||
|
}
|
||||||
|
(None, Some(serde_json::Value::Object(w))) if w.contains_key("noop") => {
|
||||||
|
Ok(WasmDiffResult::default())
|
||||||
|
}
|
||||||
|
(Some(e), Some(w)) => compare_json_value(source, e, w),
|
||||||
|
_ => Ok(WasmDiffResult {
|
||||||
|
status: vec![WasmDiffStatus::Bad(
|
||||||
|
format!(
|
||||||
|
"Nullness mismatch. Emacs=({emacs:?}) Wasm=({wasm:?}).",
|
||||||
|
emacs = emacs,
|
||||||
|
wasm = wasm
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
)],
|
||||||
|
children: Vec::new(),
|
||||||
|
name: "".into(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compare_ast_node<'s>(
|
||||||
|
source: &'s str,
|
||||||
|
emacs: &[Token<'s>],
|
||||||
|
wasm: &serde_json::Map<String, serde_json::Value>,
|
||||||
|
) -> Result<WasmDiffResult<'s>, Box<dyn std::error::Error>> {
|
||||||
|
let mut result = WasmDiffResult::default();
|
||||||
|
let mut emacs_list_iter = emacs.iter();
|
||||||
|
|
||||||
|
{
|
||||||
|
// Compare ast node type.
|
||||||
|
let emacs_name = emacs_list_iter
|
||||||
|
.next()
|
||||||
|
.ok_or("Should have a name as the first child.")?
|
||||||
|
.as_atom()?;
|
||||||
|
let wasm_name = wasm
|
||||||
|
.get("ast-node")
|
||||||
|
.ok_or("Should have a ast node type.")?
|
||||||
|
.as_str()
|
||||||
|
.ok_or("Ast node type should be a string.")?;
|
||||||
|
result.name = emacs_name.into();
|
||||||
|
if emacs_name != wasm_name {
|
||||||
|
result.status.push(WasmDiffStatus::Bad(
|
||||||
|
format!(
|
||||||
|
"AST node name mismatch. Emacs=({emacs}) Wasm=({wasm}).",
|
||||||
|
emacs = emacs_name,
|
||||||
|
wasm = wasm_name,
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.is_bad() {
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
let emacs_attributes_map = emacs_list_iter
|
||||||
|
.next()
|
||||||
|
.ok_or("Should have an attributes child.")?
|
||||||
|
.as_map()?;
|
||||||
|
let wasm_attributes_map = wasm
|
||||||
|
.get("properties")
|
||||||
|
.ok_or(r#"Wasm ast node should have a "properties" attribute."#)?
|
||||||
|
.as_object()
|
||||||
|
.ok_or(r#"Wasm ast node "properties" attribute should be an object."#)?;
|
||||||
|
|
||||||
|
{
|
||||||
|
// Compare attribute names.
|
||||||
|
let emacs_keys: std::collections::BTreeSet<String> = emacs_attributes_map
|
||||||
|
.keys()
|
||||||
|
.map(|s| (*s).to_owned())
|
||||||
|
.collect();
|
||||||
|
// wasm_attributes_map.iter().filter_map(|(k,v)| if matches!(v, serde_json::Value::Null) {None} else {Some(k)}).map(wasm_key_to_emacs_key)
|
||||||
|
let wasm_keys: std::collections::BTreeSet<String> =
|
||||||
|
std::iter::once(":standard-properties".to_owned())
|
||||||
|
.chain(wasm_attributes_map.keys().map(wasm_key_to_emacs_key))
|
||||||
|
.collect();
|
||||||
|
let emacs_only_attributes: Vec<&String> = emacs_keys.difference(&wasm_keys).collect();
|
||||||
|
let wasm_only_attributes: Vec<&String> = wasm_keys
|
||||||
|
.difference(&emacs_keys)
|
||||||
|
.filter(|attribute| {
|
||||||
|
emacs_attributes_map
|
||||||
|
.get(attribute.as_str())
|
||||||
|
.map(|token| !matches!(token, Token::Atom("nil")))
|
||||||
|
.unwrap_or(false)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
if !emacs_only_attributes.is_empty() {
|
||||||
|
result.status.push(WasmDiffStatus::Bad(
|
||||||
|
format!(
|
||||||
|
"Wasm node lacked field present in elisp node ({name}).",
|
||||||
|
name = emacs_only_attributes
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.as_str())
|
||||||
|
.intersperse(", ")
|
||||||
|
.collect::<String>(),
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if !wasm_only_attributes.is_empty() {
|
||||||
|
result.status.push(WasmDiffStatus::Bad(
|
||||||
|
format!(
|
||||||
|
"Elisp node lacked field present in wasm node ({name}).",
|
||||||
|
name = wasm_only_attributes
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.as_str())
|
||||||
|
.intersperse(", ")
|
||||||
|
.collect::<String>(),
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.is_bad() {
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Compare attributes.
|
||||||
|
for attribute_name in wasm_attributes_map.keys() {
|
||||||
|
let mut layer = WasmDiffResult::<'_> {
|
||||||
|
name: Cow::Owned(attribute_name.clone()),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let wasm_attribute_value = wasm_attributes_map.get(attribute_name);
|
||||||
|
let emacs_key = wasm_key_to_emacs_key(attribute_name);
|
||||||
|
let emacs_attribute_value = emacs_attributes_map.get(emacs_key.as_str()).copied();
|
||||||
|
let inner_layer =
|
||||||
|
compare_optional_json_value(source, emacs_attribute_value, wasm_attribute_value)?;
|
||||||
|
if !inner_layer.name.is_empty() {
|
||||||
|
layer.children.push(inner_layer);
|
||||||
|
} else {
|
||||||
|
layer.extend(inner_layer)?;
|
||||||
|
}
|
||||||
|
result.children.push(layer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Compare standard-properties.
|
||||||
|
let mut layer = WasmDiffResult::<'_> {
|
||||||
|
name: "standard-properties".into(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let emacs_standard_properties = wasm_get_emacs_standard_properties(&emacs_attributes_map)?;
|
||||||
|
let wasm_standard_properties = wasm
|
||||||
|
.get("standard-properties")
|
||||||
|
.ok_or(r#"Wasm AST nodes should have a "standard-properties" attribute."#)?
|
||||||
|
.as_object()
|
||||||
|
.ok_or(r#"Wasm ast node "standard-properties" attribute should be an object."#)?;
|
||||||
|
for (emacs_value, wasm_name) in [
|
||||||
|
(emacs_standard_properties.begin, "begin"),
|
||||||
|
(emacs_standard_properties.end, "end"),
|
||||||
|
(emacs_standard_properties.contents_begin, "contents-begin"),
|
||||||
|
(emacs_standard_properties.contents_end, "contents-end"),
|
||||||
|
(emacs_standard_properties.post_blank, "post-blank"),
|
||||||
|
] {
|
||||||
|
match (emacs_value, wasm_standard_properties.get(wasm_name)) {
|
||||||
|
(None, None) | (None, Some(serde_json::Value::Null)) => {}
|
||||||
|
(None, Some(_)) => {
|
||||||
|
layer.status.push(WasmDiffStatus::Bad(
|
||||||
|
format!(
|
||||||
|
"Elisp node lacked field present in wasm node. Name=({name}).",
|
||||||
|
name = wasm_name,
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
(Some(_), None) => {
|
||||||
|
layer.status.push(WasmDiffStatus::Bad(
|
||||||
|
format!(
|
||||||
|
"Wasm node lacked field present in elisp node. Name=({name}).",
|
||||||
|
name = wasm_name,
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
(Some(e), Some(serde_json::Value::Number(w)))
|
||||||
|
if w.as_u64().map(|w| w as usize) == Some(e) => {}
|
||||||
|
(Some(e), Some(w)) => {
|
||||||
|
layer.status.push(WasmDiffStatus::Bad(
|
||||||
|
format!(
|
||||||
|
"Property value mismatch. Emacs=({emacs:?}) Wasm=({wasm:?}).",
|
||||||
|
emacs = e,
|
||||||
|
wasm = w,
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.children.push(layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Compare children.
|
||||||
|
let mut layer = WasmDiffResult::<'_> {
|
||||||
|
name: "children".into(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
if let Some(wasm_iter) = wasm
|
||||||
|
.get("children")
|
||||||
|
.and_then(|children| children.as_array())
|
||||||
|
.map(|children| children.iter())
|
||||||
|
{
|
||||||
|
layer.extend(wasm_compare_list(source, emacs_list_iter, wasm_iter)?)?;
|
||||||
|
} else {
|
||||||
|
layer.extend(wasm_compare_list(
|
||||||
|
source,
|
||||||
|
emacs_list_iter,
|
||||||
|
std::iter::empty::<&serde_json::Value>(),
|
||||||
|
)?)?;
|
||||||
|
}
|
||||||
|
result.children.push(layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wasm_key_to_emacs_key<WK: std::fmt::Display>(wasm_key: WK) -> String {
|
||||||
|
format!(":{key}", key = wasm_key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compare_quoted_string<'s>(
|
||||||
|
_source: &'s str,
|
||||||
|
emacs: &str,
|
||||||
|
wasm: &String,
|
||||||
|
) -> Result<WasmDiffResult<'s>, Box<dyn std::error::Error>> {
|
||||||
|
let mut result = WasmDiffResult::default();
|
||||||
|
let emacs_text = unquote(emacs)?;
|
||||||
|
if wasm.as_str() != emacs_text {
|
||||||
|
result.status.push(WasmDiffStatus::Bad(
|
||||||
|
format!(
|
||||||
|
"Text mismatch. Emacs=({emacs:?}) Wasm=({wasm:?}).",
|
||||||
|
emacs = emacs_text,
|
||||||
|
wasm = wasm,
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn wasm_get_emacs_standard_properties(
|
||||||
|
attributes_map: &HashMap<&str, &Token<'_>>,
|
||||||
|
) -> Result<EmacsStandardProperties, Box<dyn std::error::Error>> {
|
||||||
|
let standard_properties = attributes_map.get(":standard-properties");
|
||||||
|
Ok(if standard_properties.is_some() {
|
||||||
|
let mut std_props = standard_properties
|
||||||
|
.expect("if statement proves its Some")
|
||||||
|
.as_vector()?
|
||||||
|
.iter();
|
||||||
|
let begin = maybe_token_to_usize(std_props.next())?;
|
||||||
|
let post_affiliated = maybe_token_to_usize(std_props.next())?;
|
||||||
|
let contents_begin = maybe_token_to_usize(std_props.next())?;
|
||||||
|
let contents_end = maybe_token_to_usize(std_props.next())?;
|
||||||
|
let end = maybe_token_to_usize(std_props.next())?;
|
||||||
|
let post_blank = maybe_token_to_usize(std_props.next())?;
|
||||||
|
EmacsStandardProperties {
|
||||||
|
begin,
|
||||||
|
post_affiliated,
|
||||||
|
contents_begin,
|
||||||
|
contents_end,
|
||||||
|
end,
|
||||||
|
post_blank,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let begin = maybe_token_to_usize(attributes_map.get(":begin").copied())?;
|
||||||
|
let end = maybe_token_to_usize(attributes_map.get(":end").copied())?;
|
||||||
|
let contents_begin = maybe_token_to_usize(attributes_map.get(":contents-begin").copied())?;
|
||||||
|
let contents_end = maybe_token_to_usize(attributes_map.get(":contents-end").copied())?;
|
||||||
|
let post_blank = maybe_token_to_usize(attributes_map.get(":post-blank").copied())?;
|
||||||
|
let post_affiliated =
|
||||||
|
maybe_token_to_usize(attributes_map.get(":post-affiliated").copied())?;
|
||||||
|
EmacsStandardProperties {
|
||||||
|
begin,
|
||||||
|
post_affiliated,
|
||||||
|
contents_begin,
|
||||||
|
contents_end,
|
||||||
|
end,
|
||||||
|
post_blank,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wasm_compare_list<'e, 's: 'e, 'w, EI, WI>(
|
||||||
|
source: &'s str,
|
||||||
|
mut emacs: EI,
|
||||||
|
wasm: WI,
|
||||||
|
) -> Result<WasmDiffResult<'s>, Box<dyn std::error::Error>>
|
||||||
|
where
|
||||||
|
EI: Iterator<Item = &'e Token<'s>> + ExactSizeIterator,
|
||||||
|
WI: Iterator<Item = &'w serde_json::Value> + ExactSizeIterator,
|
||||||
|
{
|
||||||
|
let emacs_length = emacs.len();
|
||||||
|
let wasm_length = wasm.len();
|
||||||
|
if emacs_length == 1 && wasm_length == 0 && emacs.all(|t| matches!(t.as_atom(), Ok(r#""""#))) {
|
||||||
|
return Ok(WasmDiffResult::default());
|
||||||
|
}
|
||||||
|
if emacs_length != wasm_length {
|
||||||
|
return Ok(WasmDiffResult {
|
||||||
|
status: vec![WasmDiffStatus::Bad(
|
||||||
|
format!(
|
||||||
|
"Child length mismatch. Emacs=({emacs:?}) Wasm=({wasm:?}).",
|
||||||
|
emacs = emacs_length,
|
||||||
|
wasm = wasm_length
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
)],
|
||||||
|
children: Vec::new(),
|
||||||
|
name: "".into(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut child_status = Vec::with_capacity(emacs_length);
|
||||||
|
for (emacs_child, wasm_child) in emacs.zip(wasm) {
|
||||||
|
child_status.push(compare_json_value(source, emacs_child, wasm_child)?);
|
||||||
|
}
|
||||||
|
Ok(WasmDiffResult {
|
||||||
|
status: Vec::new(),
|
||||||
|
children: child_status,
|
||||||
|
name: "".into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compare_optional_pair<'s>(
|
||||||
|
source: &'s str,
|
||||||
|
emacs: &Vec<Token<'s>>,
|
||||||
|
wasm: &serde_json::Map<String, serde_json::Value>,
|
||||||
|
) -> Result<WasmDiffResult<'s>, Box<dyn std::error::Error>> {
|
||||||
|
let mut result = WasmDiffResult::default();
|
||||||
|
let wasm_optval = wasm
|
||||||
|
.get("optval")
|
||||||
|
.ok_or(r#"Wasm optional pair should have an "optval" attribute."#)?;
|
||||||
|
let wasm_val = wasm
|
||||||
|
.get("val")
|
||||||
|
.ok_or(r#"Wasm optional pair should have an "optval" attribute."#)?;
|
||||||
|
if let serde_json::Value::Null = wasm_optval {
|
||||||
|
// If the optval is null, then the elisp should have just a single value of a quoted string.
|
||||||
|
if emacs.len() != 1 {
|
||||||
|
return Ok(WasmDiffResult {
|
||||||
|
status: vec![WasmDiffStatus::Bad(
|
||||||
|
format!(
|
||||||
|
"Optional pair with null optval should have 1 element. Emacs=({emacs:?}) Wasm=({wasm:?}).",
|
||||||
|
emacs = emacs,
|
||||||
|
wasm = wasm
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
)],
|
||||||
|
children: Vec::new(),
|
||||||
|
name: "".into(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let emacs_val = emacs
|
||||||
|
.first()
|
||||||
|
.expect("If-statement proves this will be Some.");
|
||||||
|
result.extend(compare_json_value(source, emacs_val, wasm_val)?)?;
|
||||||
|
} else {
|
||||||
|
// If the optval is not null, then the elisp should have 3 values, the optval, a dot, and the val.
|
||||||
|
if emacs.len() != 3 {
|
||||||
|
return Ok(WasmDiffResult {
|
||||||
|
status: vec![WasmDiffStatus::Bad(
|
||||||
|
format!(
|
||||||
|
"Optional pair with non-null optval should have 3 elements. Emacs=({emacs:?}) Wasm=({wasm:?}).",
|
||||||
|
emacs = emacs,
|
||||||
|
wasm = wasm
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
)],
|
||||||
|
children: Vec::new(),
|
||||||
|
name: "".into(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let emacs_optval = emacs
|
||||||
|
.get(2)
|
||||||
|
.expect("If-statement proves this will be Some.");
|
||||||
|
let emacs_val = emacs
|
||||||
|
.first()
|
||||||
|
.expect("If-statement proves this will be Some.");
|
||||||
|
result.extend(compare_json_value(source, emacs_optval, wasm_optval)?)?;
|
||||||
|
result.extend(compare_json_value(source, emacs_val, wasm_val)?)?;
|
||||||
|
}
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compare_object_tree<'s>(
|
||||||
|
source: &'s str,
|
||||||
|
emacs: &[Token<'s>],
|
||||||
|
wasm: &serde_json::Map<String, serde_json::Value>,
|
||||||
|
) -> Result<WasmDiffResult<'s>, Box<dyn std::error::Error>> {
|
||||||
|
let mut result = WasmDiffResult::default();
|
||||||
|
let wasm_attributes = wasm
|
||||||
|
.get("object-tree")
|
||||||
|
.ok_or(r#"Wasm object tree should have an "object-tree" attribute."#)?
|
||||||
|
.as_array()
|
||||||
|
.ok_or(r#"Wasm "object-tree" attribute should be a list."#)?;
|
||||||
|
let emacs_outer_length = emacs.len();
|
||||||
|
let wasm_outer_length = wasm_attributes.len();
|
||||||
|
if emacs_outer_length != wasm_outer_length {
|
||||||
|
result.status.push(WasmDiffStatus::Bad(
|
||||||
|
format!(
|
||||||
|
"Child length mismatch. Emacs=({emacs:?}) Wasm=({wasm:?}).",
|
||||||
|
emacs = emacs_outer_length,
|
||||||
|
wasm = wasm_outer_length
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (emacs_attribute, wasm_attribute) in emacs.iter().zip(wasm_attributes.iter()) {
|
||||||
|
let wasm_attribute = wasm_attribute
|
||||||
|
.as_array()
|
||||||
|
.ok_or("Wasm middle layer in object tree should be a list.")?;
|
||||||
|
if wasm_attribute.len() != 2 {
|
||||||
|
result.status.push(WasmDiffStatus::Bad(
|
||||||
|
format!(
|
||||||
|
"Wasm middle layer in object tree should have a length of 2. Wasm=({wasm:?}).",
|
||||||
|
wasm = wasm_attribute.len()
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
if let Ok("nil") = emacs_attribute.as_atom() {
|
||||||
|
if let Some(serde_json::Value::Null) = wasm_attribute.first() {
|
||||||
|
if let Some(serde_json::Value::Array(w)) = wasm_attribute.get(1) {
|
||||||
|
if w.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let emacs_attribute = emacs_attribute.as_list()?;
|
||||||
|
if let Some(serde_json::Value::Null) = wasm_attribute.first() {
|
||||||
|
// If optval is null then the emacs array should only contain 1 value.
|
||||||
|
if emacs_attribute.len() != 1 {
|
||||||
|
result.status.push(WasmDiffStatus::Bad(
|
||||||
|
format!(
|
||||||
|
"Emacs middle layer in object tree should have a length of 1. Emacs=({emacs:?}) Wasm=({wasm:?}).",
|
||||||
|
emacs = emacs_attribute,
|
||||||
|
wasm = wasm_attribute
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
let emacs_val = emacs_attribute
|
||||||
|
.first()
|
||||||
|
.expect("If-statement proves this will be Some.");
|
||||||
|
let wasm_val = wasm_attribute
|
||||||
|
.get(1)
|
||||||
|
.expect("If-statement proves this will be Some.");
|
||||||
|
result.extend(compare_json_value(source, emacs_val, wasm_val)?)?;
|
||||||
|
} else {
|
||||||
|
// If optval is not null, then the emacs array should contain a list, the first child of which is a list for optval, and all other entries to the outer list are the val.
|
||||||
|
if emacs_attribute.len() < 2 {
|
||||||
|
result.status.push(WasmDiffStatus::Bad(
|
||||||
|
format!(
|
||||||
|
"Emacs middle layer in object tree should have a length of at least 2. Emacs=({emacs:?}) Wasm=({wasm:?}).",
|
||||||
|
emacs = emacs_attribute,
|
||||||
|
wasm = wasm_attribute
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
let emacs_optval = emacs_attribute.iter().skip(1);
|
||||||
|
let wasm_optval = wasm_attribute
|
||||||
|
.first()
|
||||||
|
.expect("If-statement proves this will be Some.")
|
||||||
|
.as_array()
|
||||||
|
.ok_or("first value in wasm object tree should be a list.")?;
|
||||||
|
let emacs_val = emacs_attribute
|
||||||
|
.first()
|
||||||
|
.ok_or("If-statement proves this will be Some.")?;
|
||||||
|
let wasm_val = wasm_attribute
|
||||||
|
.get(1)
|
||||||
|
.expect("If-statement proves this will be Some.")
|
||||||
|
.as_array()
|
||||||
|
.ok_or("2nd value in wasm object tree should be a list.")?;
|
||||||
|
result.extend(wasm_compare_list(source, emacs_optval, wasm_optval.iter())?)?;
|
||||||
|
if let Ok("nil") = emacs_val.as_atom() {
|
||||||
|
result.extend(wasm_compare_list(
|
||||||
|
source,
|
||||||
|
std::iter::empty(),
|
||||||
|
wasm_val.iter(),
|
||||||
|
)?)?;
|
||||||
|
} else {
|
||||||
|
result.extend(wasm_compare_list(
|
||||||
|
source,
|
||||||
|
emacs_val.as_list()?.iter(),
|
||||||
|
wasm_val.iter(),
|
||||||
|
)?)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compare_number_lines<'s>(
|
||||||
|
_source: &'s str,
|
||||||
|
emacs: &[Token<'s>],
|
||||||
|
wasm: &serde_json::Map<String, serde_json::Value>,
|
||||||
|
) -> Result<WasmDiffResult<'s>, Box<dyn std::error::Error>> {
|
||||||
|
let mut result = WasmDiffResult::default();
|
||||||
|
let mut emacs_iter = emacs.iter();
|
||||||
|
let emacs_directive = emacs_iter
|
||||||
|
.next()
|
||||||
|
.ok_or("Emacs number lines should have 3 children.")?
|
||||||
|
.as_atom()?;
|
||||||
|
let emacs_number: i64 = emacs_iter
|
||||||
|
.nth(1)
|
||||||
|
.ok_or("Emacs number lines should have 3 children.")?
|
||||||
|
.as_atom()?
|
||||||
|
.parse()?;
|
||||||
|
if wasm.contains_key("new") {
|
||||||
|
let wasm_number = wasm
|
||||||
|
.get("new")
|
||||||
|
.ok_or(r#"Wasm number lines should have a "new" attribute."#)?
|
||||||
|
.as_i64()
|
||||||
|
.ok_or(r#"Wasm number lines "new" attribute should be a number."#)?;
|
||||||
|
if emacs_directive != "new" {
|
||||||
|
result.status.push(WasmDiffStatus::Bad(
|
||||||
|
format!(
|
||||||
|
"Number lines directive mismatch. Emacs=({emacs:?}) Wasm=({wasm:?}).",
|
||||||
|
emacs = emacs_directive,
|
||||||
|
wasm = "new"
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
if emacs_number != wasm_number {
|
||||||
|
result.status.push(WasmDiffStatus::Bad(
|
||||||
|
format!(
|
||||||
|
"Number lines number mismatch. Emacs=({emacs:?}) Wasm=({wasm:?}).",
|
||||||
|
emacs = emacs_number,
|
||||||
|
wasm = wasm_number
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
} else if wasm.contains_key("continued") {
|
||||||
|
let wasm_number = wasm
|
||||||
|
.get("continued")
|
||||||
|
.ok_or(r#"Wasm number lines should have a "continued" attribute."#)?
|
||||||
|
.as_i64()
|
||||||
|
.ok_or(r#"Wasm number lines "continued" attribute should be a number."#)?;
|
||||||
|
if emacs_directive != "continued" {
|
||||||
|
result.status.push(WasmDiffStatus::Bad(
|
||||||
|
format!(
|
||||||
|
"Number lines directive mismatch. Emacs=({emacs:?}) Wasm=({wasm:?}).",
|
||||||
|
emacs = emacs_directive,
|
||||||
|
wasm = "continued"
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
if emacs_number != wasm_number {
|
||||||
|
result.status.push(WasmDiffStatus::Bad(
|
||||||
|
format!(
|
||||||
|
"Number lines number mismatch. Emacs=({emacs:?}) Wasm=({wasm:?}).",
|
||||||
|
emacs = emacs_number,
|
||||||
|
wasm = wasm_number
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result.status.push(WasmDiffStatus::Bad(
|
||||||
|
format!(
|
||||||
|
"Unrecognized number lines directive. Wasm=({wasm:?}).",
|
||||||
|
wasm = wasm
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compare_string_set<'s>(
|
||||||
|
_source: &'s str,
|
||||||
|
emacs: &[Token<'s>],
|
||||||
|
wasm: &serde_json::Map<String, serde_json::Value>,
|
||||||
|
) -> Result<WasmDiffResult<'s>, Box<dyn std::error::Error>> {
|
||||||
|
let mut result = WasmDiffResult::default();
|
||||||
|
let wasm_list = wasm
|
||||||
|
.get("value")
|
||||||
|
.ok_or(r#"Wasm string set should have a "value" attribute."#)?
|
||||||
|
.as_array()
|
||||||
|
.ok_or(r#"Wasm string set "value" attribute should be a list."#)?;
|
||||||
|
let wasm_strings = wasm_list
|
||||||
|
.iter()
|
||||||
|
.map(|v| v.as_str().ok_or("Non-string in wasm string set."))
|
||||||
|
.collect::<Result<BTreeSet<_>, &'static str>>()?;
|
||||||
|
let emacs_strings = emacs
|
||||||
|
.iter()
|
||||||
|
.map(|v| v.as_atom())
|
||||||
|
.collect::<Result<Vec<_>, Box<dyn std::error::Error>>>()?
|
||||||
|
.into_iter()
|
||||||
|
.map(unquote)
|
||||||
|
.collect::<Result<Vec<_>, Box<dyn std::error::Error>>>()?;
|
||||||
|
let emacs_strings = emacs_strings
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.borrow())
|
||||||
|
.collect::<BTreeSet<&str>>();
|
||||||
|
|
||||||
|
let mismatched: Vec<_> = emacs_strings
|
||||||
|
.symmetric_difference(&wasm_strings)
|
||||||
|
.copied()
|
||||||
|
.collect();
|
||||||
|
if !mismatched.is_empty() {
|
||||||
|
result.status.push(WasmDiffStatus::Bad(
|
||||||
|
format!(
|
||||||
|
"String set mismatch. MismatchedValues=({values:?}).",
|
||||||
|
values = mismatched.join(", ")
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_plain_text(wasm: &serde_json::Map<String, serde_json::Value>) -> bool {
|
||||||
|
if let Some(serde_json::Value::String(node_type)) = wasm.get("ast-node") {
|
||||||
|
node_type == "plain-text"
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compare_plain_text<'s>(
|
||||||
|
source: &'s str,
|
||||||
|
emacs: &TextWithProperties<'s>,
|
||||||
|
wasm: &serde_json::Map<String, serde_json::Value>,
|
||||||
|
) -> Result<WasmDiffResult<'s>, Box<dyn std::error::Error>> {
|
||||||
|
let mut result = WasmDiffResult::<'_> {
|
||||||
|
name: "plain-text".into(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
if !is_plain_text(wasm) {
|
||||||
|
result.status.push(WasmDiffStatus::Bad(
|
||||||
|
format!(
|
||||||
|
"AST node type mismatch. Emacs=({emacs:?}) Wasm=({wasm:?}).",
|
||||||
|
emacs = emacs,
|
||||||
|
wasm = wasm,
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
let emacs_text = unquote(emacs.text)?;
|
||||||
|
let wasm_standard_properties = wasm
|
||||||
|
.get("standard-properties")
|
||||||
|
.ok_or(r#"Wasm AST nodes should have a "standard-properties" attribute."#)?
|
||||||
|
.as_object()
|
||||||
|
.ok_or(r#"Wasm ast node "standard-properties" attribute should be an object."#)?;
|
||||||
|
let wasm_begin = {
|
||||||
|
if let Some(serde_json::Value::Number(begin)) = wasm_standard_properties.get("begin") {
|
||||||
|
begin
|
||||||
|
.as_u64()
|
||||||
|
.map(|w| w as usize)
|
||||||
|
.ok_or("Begin should be a number.")?
|
||||||
|
} else {
|
||||||
|
result.status.push(WasmDiffStatus::Bad(
|
||||||
|
format!(
|
||||||
|
"AST node type mismatch. Emacs=({emacs:?}) Wasm=({wasm:?}).",
|
||||||
|
emacs = emacs,
|
||||||
|
wasm = wasm,
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let wasm_end = {
|
||||||
|
if let Some(serde_json::Value::Number(end)) = wasm_standard_properties.get("end") {
|
||||||
|
end.as_u64()
|
||||||
|
.map(|w| w as usize)
|
||||||
|
.ok_or("End should be a number.")?
|
||||||
|
} else {
|
||||||
|
result.status.push(WasmDiffStatus::Bad(
|
||||||
|
format!(
|
||||||
|
"AST node type mismatch. Emacs=({emacs:?}) Wasm=({wasm:?}).",
|
||||||
|
emacs = emacs,
|
||||||
|
wasm = wasm,
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let wasm_text: String = source
|
||||||
|
.chars()
|
||||||
|
.skip(wasm_begin - 1)
|
||||||
|
.take(wasm_end - wasm_begin)
|
||||||
|
.collect();
|
||||||
|
if wasm_text != emacs_text {
|
||||||
|
result.status.push(WasmDiffStatus::Bad(
|
||||||
|
format!(
|
||||||
|
"Text mismatch. Emacs=({emacs:?}) Wasm=({wasm:?}).",
|
||||||
|
emacs = emacs_text,
|
||||||
|
wasm = wasm_text,
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
let emacs_start = emacs
|
||||||
|
.properties
|
||||||
|
.first()
|
||||||
|
.map(|t| t.as_atom())
|
||||||
|
.map_or(Ok(None), |r| r.map(Some))?;
|
||||||
|
if emacs_start != Some("0") {
|
||||||
|
result.status.push(WasmDiffStatus::Bad(
|
||||||
|
format!(
|
||||||
|
"Text should start at offset 0. Emacs=({emacs:?}) Wasm=({wasm:?}).",
|
||||||
|
emacs = emacs,
|
||||||
|
wasm = wasm,
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let emacs_end = emacs
|
||||||
|
.properties
|
||||||
|
.get(1)
|
||||||
|
.map(|t| t.as_atom())
|
||||||
|
.map_or(Ok(None), |r| r.map(Some))?;
|
||||||
|
if emacs_end != Some((wasm_end - wasm_begin).to_string().as_str()) {
|
||||||
|
result.status.push(WasmDiffStatus::Bad(
|
||||||
|
format!(
|
||||||
|
"Text end mismatch. Emacs=({emacs:?}) Wasm=({wasm:?}).",
|
||||||
|
emacs = emacs_end,
|
||||||
|
wasm = wasm_end - wasm_begin,
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
109
src/wasm_test/diff.rs
Normal file
109
src/wasm_test/diff.rs
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
use crate::util::terminal::foreground_color;
|
||||||
|
use crate::util::terminal::reset_color;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct WasmDiffResult<'s> {
|
||||||
|
pub(crate) status: Vec<WasmDiffStatus>,
|
||||||
|
pub(crate) name: Cow<'s, str>,
|
||||||
|
pub(crate) children: Vec<WasmDiffResult<'s>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) enum WasmDiffStatus {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
Good,
|
||||||
|
Bad(Cow<'static, str>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'s> WasmDiffResult<'s> {
|
||||||
|
pub(crate) fn extend(
|
||||||
|
&mut self,
|
||||||
|
other: WasmDiffResult<'s>,
|
||||||
|
) -> Result<&mut WasmDiffResult<'s>, Box<dyn std::error::Error>> {
|
||||||
|
if self.name.is_empty() {
|
||||||
|
self.name = other.name;
|
||||||
|
}
|
||||||
|
self.status.extend(other.status);
|
||||||
|
self.children.extend(other.children);
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_bad(&self) -> bool {
|
||||||
|
self.is_self_bad() || self.has_bad_children()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_self_bad(&self) -> bool {
|
||||||
|
self.status
|
||||||
|
.iter()
|
||||||
|
.any(|status| matches!(status, WasmDiffStatus::Bad(_)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_bad_children(&self) -> bool {
|
||||||
|
self.children.iter().any(WasmDiffResult::is_bad)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print(&self, original_document: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
self.print_indented(0, original_document)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_indented(
|
||||||
|
&self,
|
||||||
|
indentation: usize,
|
||||||
|
_original_document: &str,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let status_text = {
|
||||||
|
if self.is_self_bad() {
|
||||||
|
format!(
|
||||||
|
"{color}BAD{reset}",
|
||||||
|
color = foreground_color(255, 0, 0),
|
||||||
|
reset = reset_color(),
|
||||||
|
)
|
||||||
|
} else if self.has_bad_children() {
|
||||||
|
format!(
|
||||||
|
"{color}BADCHILD{reset}",
|
||||||
|
color = foreground_color(255, 255, 0),
|
||||||
|
reset = reset_color(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
format!(
|
||||||
|
"{color}GOOD{reset}",
|
||||||
|
color = foreground_color(0, 255, 0),
|
||||||
|
reset = reset_color(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let message = self
|
||||||
|
.status
|
||||||
|
.iter()
|
||||||
|
.filter_map(|status| match status {
|
||||||
|
WasmDiffStatus::Good => None,
|
||||||
|
WasmDiffStatus::Bad(message) => Some(message),
|
||||||
|
})
|
||||||
|
.next();
|
||||||
|
println!(
|
||||||
|
"{indentation}{status_text} {name} {message}",
|
||||||
|
indentation = " ".repeat(indentation),
|
||||||
|
status_text = status_text,
|
||||||
|
name = self.name,
|
||||||
|
message = message.unwrap_or(&Cow::Borrowed(""))
|
||||||
|
);
|
||||||
|
|
||||||
|
for child in self.children.iter() {
|
||||||
|
child.print_indented(indentation + 1, _original_document)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'s> Default for WasmDiffResult<'s> {
|
||||||
|
fn default() -> Self {
|
||||||
|
WasmDiffResult {
|
||||||
|
status: Vec::new(),
|
||||||
|
name: "".into(),
|
||||||
|
children: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/wasm_test/mod.rs
Normal file
8
src/wasm_test/mod.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
mod compare;
|
||||||
|
mod diff;
|
||||||
|
mod runner;
|
||||||
|
|
||||||
|
pub use runner::wasm_run_anonymous_compare;
|
||||||
|
pub use runner::wasm_run_anonymous_compare_with_settings;
|
||||||
|
pub use runner::wasm_run_compare_on_file;
|
||||||
|
pub use runner::wasm_run_compare_on_file_with_settings;
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user