Merge branch 'wasm'
This commit is contained in:
commit
03a3ddbd63
@ -192,6 +192,54 @@ spec:
|
||||
]
|
||||
- name: docker-image
|
||||
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:
|
||||
- name: report-success
|
||||
when:
|
||||
|
@ -115,7 +115,7 @@ spec:
|
||||
[
|
||||
--no-default-features,
|
||||
--features,
|
||||
compare,
|
||||
"compare,wasm_test",
|
||||
--no-fail-fast,
|
||||
--lib,
|
||||
--test,
|
||||
|
@ -127,7 +127,7 @@ spec:
|
||||
- name: command
|
||||
value: ["cargo", "fix"]
|
||||
- name: args
|
||||
value: ["--allow-dirty"]
|
||||
value: ["--all-targets", "--all-features", "--allow-dirty"]
|
||||
- name: docker-image
|
||||
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
||||
- name: commit-changes
|
||||
|
22
Cargo.toml
22
Cargo.toml
@ -39,17 +39,32 @@ path = "src/lib.rs"
|
||||
path = "src/bin_foreign_document_test.rs"
|
||||
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]
|
||||
futures = { version = "0.3.28", optional = true }
|
||||
nom = "7.1.1"
|
||||
opentelemetry = { version = "0.20.0", optional = true, default-features = false, features = ["trace", "rt-tokio"] }
|
||||
opentelemetry-otlp = { version = "0.13.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"] }
|
||||
tracing = { version = "0.1.37", optional = true }
|
||||
tracing-opentelemetry = { version = "0.20.0", optional = true }
|
||||
tracing-subscriber = { version = "0.3.17", optional = true, features = ["env-filter"] }
|
||||
walkdir = { version = "2.3.3", optional = true }
|
||||
wasm-bindgen = { version = "0.2.89", optional = true }
|
||||
|
||||
[build-dependencies]
|
||||
walkdir = "2.3.3"
|
||||
@ -60,6 +75,8 @@ compare = ["tokio/process", "tokio/macros"]
|
||||
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"]
|
||||
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.
|
||||
[profile.release-lto]
|
||||
@ -79,3 +96,8 @@ strip = "symbols"
|
||||
inherits = "release"
|
||||
lto = true
|
||||
debug = true
|
||||
|
||||
[profile.wasm]
|
||||
inherits = "release"
|
||||
lto = true
|
||||
strip = true
|
||||
|
16
Makefile
16
Makefile
@ -29,6 +29,11 @@ build:
|
||||
release:
|
||||
> cargo build --release $(RELEASEFLAGS)
|
||||
|
||||
.PHONY: wasm
|
||||
wasm:
|
||||
> cargo build --target=wasm32-unknown-unknown --profile wasm --bin wasm --features wasm
|
||||
> wasm-bindgen --target web --out-dir target/wasm32-unknown-unknown/js target/wasm32-unknown-unknown/wasm/wasm.wasm
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
> cargo clean
|
||||
@ -49,11 +54,20 @@ clippy:
|
||||
test:
|
||||
> 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
|
||||
dockertest:
|
||||
> $(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)
|
||||
|
||||
.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
|
||||
buildtest:
|
||||
> cargo build --no-default-features
|
||||
@ -62,6 +76,8 @@ buildtest:
|
||||
> 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,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
|
||||
foreign_document_test:
|
||||
|
@ -2,5 +2,6 @@ FROM rustlang/rust:nightly-alpine3.17
|
||||
|
||||
RUN apk add --no-cache musl-dev
|
||||
RUN cargo install --locked --no-default-features --features ci-autoclean cargo-cache
|
||||
RUN rustup target add wasm32-unknown-unknown
|
||||
|
||||
ENTRYPOINT ["cargo", "build"]
|
||||
|
@ -30,7 +30,7 @@ endif
|
||||
# NOTE: This target will write to folders underneath the git-root
|
||||
.PHONY: run
|
||||
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
|
||||
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(exact_size_is_empty)]
|
||||
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 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::LocalFileAccessInterface;
|
||||
use crate::parser::parse_file_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>>(
|
||||
org_contents: P,
|
||||
@ -68,8 +68,8 @@ pub async fn run_anonymous_compare_with_settings<'g, 's, P: AsRef<str>>(
|
||||
} else if !silent {
|
||||
println!(
|
||||
"{color}Entire document passes.{reset}",
|
||||
color = DiffResult::foreground_color(0, 255, 0),
|
||||
reset = DiffResult::reset_color(),
|
||||
color = foreground_color(0, 255, 0),
|
||||
reset = reset_color(),
|
||||
);
|
||||
}
|
||||
|
||||
@ -121,19 +121,10 @@ pub async fn run_compare_on_file_with_settings<'g, 's, P: AsRef<Path>>(
|
||||
} else if !silent {
|
||||
println!(
|
||||
"{color}Entire document passes.{reset}",
|
||||
color = DiffResult::foreground_color(0, 255, 0),
|
||||
reset = DiffResult::reset_color(),
|
||||
color = foreground_color(0, 255, 0),
|
||||
reset = reset_color(),
|
||||
);
|
||||
}
|
||||
|
||||
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::DiffEntry;
|
||||
use super::diff::DiffStatus;
|
||||
use super::sexp::unquote;
|
||||
use super::sexp::Token;
|
||||
use super::util::get_property;
|
||||
use super::util::get_property_numeric;
|
||||
use super::util::get_property_quoted_string;
|
||||
@ -20,6 +18,8 @@ use crate::types::CharOffsetInLine;
|
||||
use crate::types::LineNumber;
|
||||
use crate::types::RetainLabels;
|
||||
use crate::types::SwitchNumberLines;
|
||||
use crate::util::elisp::unquote;
|
||||
use crate::util::elisp::Token;
|
||||
|
||||
#[derive(Debug)]
|
||||
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_single_ast_node;
|
||||
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::assert_no_children;
|
||||
use super::util::compare_additional_properties;
|
||||
@ -109,6 +105,12 @@ use crate::types::Verbatim;
|
||||
use crate::types::VerseBlock;
|
||||
use crate::types::WarningDelayType;
|
||||
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)]
|
||||
pub enum DiffEntry<'b, 's> {
|
||||
@ -200,21 +202,21 @@ impl<'b, 's> DiffResult<'b, 's> {
|
||||
if self.has_bad_children() {
|
||||
format!(
|
||||
"{color}BADCHILD{reset}",
|
||||
color = DiffResult::foreground_color(255, 255, 0),
|
||||
reset = DiffResult::reset_color(),
|
||||
color = foreground_color(255, 255, 0),
|
||||
reset = reset_color(),
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"{color}GOOD{reset}",
|
||||
color = DiffResult::foreground_color(0, 255, 0),
|
||||
reset = DiffResult::reset_color(),
|
||||
color = foreground_color(0, 255, 0),
|
||||
reset = reset_color(),
|
||||
)
|
||||
}
|
||||
}
|
||||
DiffStatus::Bad => format!(
|
||||
"{color}BAD{reset}",
|
||||
color = DiffResult::foreground_color(255, 0, 0),
|
||||
reset = DiffResult::reset_color(),
|
||||
color = foreground_color(255, 0, 0),
|
||||
reset = reset_color(),
|
||||
),
|
||||
}
|
||||
};
|
||||
@ -239,45 +241,6 @@ impl<'b, 's> DiffResult<'b, 's> {
|
||||
.iter()
|
||||
.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> {
|
||||
@ -295,14 +258,14 @@ impl<'b, 's> DiffLayer<'b, 's> {
|
||||
let status_text = if self.has_bad_children() {
|
||||
format!(
|
||||
"{color}BADCHILD{reset}",
|
||||
color = DiffResult::foreground_color(255, 255, 0),
|
||||
reset = DiffResult::reset_color(),
|
||||
color = foreground_color(255, 255, 0),
|
||||
reset = reset_color(),
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"{color}GOOD{reset}",
|
||||
color = DiffResult::foreground_color(0, 255, 0),
|
||||
reset = DiffResult::reset_color(),
|
||||
color = foreground_color(0, 255, 0),
|
||||
reset = reset_color(),
|
||||
)
|
||||
};
|
||||
println!(
|
||||
|
@ -2,10 +2,7 @@
|
||||
mod compare;
|
||||
mod compare_field;
|
||||
mod diff;
|
||||
mod elisp_fact;
|
||||
mod macros;
|
||||
mod parse;
|
||||
mod sexp;
|
||||
mod util;
|
||||
pub use compare::run_anonymous_compare;
|
||||
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::diff::DiffEntry;
|
||||
use super::diff::DiffStatus;
|
||||
use super::elisp_fact::GetElispFact;
|
||||
use super::sexp::Token;
|
||||
use crate::compare::diff::compare_ast_node;
|
||||
use crate::compare::sexp::unquote;
|
||||
use crate::types::AffiliatedKeywordValue;
|
||||
use crate::types::AstNode;
|
||||
use crate::types::GetAffiliatedKeywords;
|
||||
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.
|
||||
fn is_slice_of(parent: &str, child: &str) -> bool {
|
||||
@ -145,80 +146,6 @@ fn assert_post_blank<'b, 's, S: StandardProperties<'s> + ?Sized>(
|
||||
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.
|
||||
///
|
||||
/// 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;
|
||||
}
|
||||
|
||||
pub fn report(original_document: &str) {
|
||||
pub(crate) fn report(original_document: &str) {
|
||||
let mut db = GLOBAL_DATA.lock().unwrap();
|
||||
let db = db.get_or_insert_with(HashMap::new);
|
||||
let mut results: Vec<_> = db.iter().collect();
|
||||
|
@ -2,5 +2,5 @@ mod database;
|
||||
mod event_type;
|
||||
|
||||
pub(crate) use database::record_event;
|
||||
pub use database::report;
|
||||
pub(crate) use database::report;
|
||||
pub(crate) use event_type::EventType;
|
||||
|
13
src/lib.rs
13
src/lib.rs
@ -3,6 +3,8 @@
|
||||
#![feature(path_file_prefix)]
|
||||
#![feature(is_sorted)]
|
||||
#![feature(test)]
|
||||
#![feature(iter_intersperse)]
|
||||
#![feature(exact_size_is_empty)]
|
||||
// TODO: #![warn(missing_docs)]
|
||||
#![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")]
|
||||
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 error;
|
||||
#[cfg(feature = "event_count")]
|
||||
pub mod event_count;
|
||||
mod event_count;
|
||||
mod iter;
|
||||
pub mod parser;
|
||||
pub mod types;
|
||||
|
62
src/main.rs
62
src/main.rs
@ -1,12 +1,4 @@
|
||||
#![feature(round_char_boundary)]
|
||||
#![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;
|
||||
use organic::parse_cli::main_body;
|
||||
|
||||
#[cfg(feature = "tracing")]
|
||||
use crate::init_tracing::init_telemetry;
|
||||
@ -30,55 +22,3 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
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;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Year(YearInner);
|
||||
pub struct Year(pub YearInner);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Month(MonthInner);
|
||||
pub struct Month(pub MonthInner);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DayOfMonth(DayOfMonthInner);
|
||||
pub struct DayOfMonth(pub DayOfMonthInner);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Hour(HourInner);
|
||||
pub struct Hour(pub HourInner);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Minute(MinuteInner);
|
||||
pub struct Minute(pub MinuteInner);
|
||||
|
||||
impl Year {
|
||||
// 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 crate::context::HeadlineLevelFilter;
|
||||
use crate::settings::GlobalSettings;
|
||||
use crate::settings::HeadlineLevelFilter;
|
||||
|
||||
/// Generate elisp to configure org-mode parsing settings
|
||||
///
|
||||
/// Currently only org-list-allow-alphabetical is supported.
|
||||
fn global_settings_elisp(global_settings: &GlobalSettings) -> String {
|
||||
// This string concatenation is wildly inefficient but its only called in tests 🤷.
|
||||
let mut ret = "".to_owned();
|
||||
if global_settings.list_allow_alphabetical {
|
||||
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();
|
||||
}
|
||||
if global_settings.odd_levels_only != HeadlineLevelFilter::default() {
|
||||
ret += match global_settings.odd_levels_only {
|
||||
HeadlineLevelFilter::Odd => "(setq org-odd-levels-only t)\n",
|
||||
HeadlineLevelFilter::OddEven => "(setq org-odd-levels-only nil)\n",
|
||||
};
|
||||
}
|
||||
ret
|
||||
pub 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(())
|
||||
}
|
||||
|
||||
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)?)
|
||||
}
|
||||
|
||||
pub(crate) async fn get_org_mode_version() -> Result<String, Box<dyn std::error::Error>> {
|
||||
let elisp_script = r#"(progn
|
||||
(org-mode)
|
||||
(message "%s" (org-version nil t nil))
|
||||
)"#;
|
||||
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>(
|
||||
@ -144,39 +169,23 @@ where
|
||||
output
|
||||
}
|
||||
|
||||
pub 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)?)
|
||||
}
|
||||
|
||||
pub async fn get_org_mode_version() -> Result<String, Box<dyn std::error::Error>> {
|
||||
let elisp_script = r#"(progn
|
||||
(org-mode)
|
||||
(message "%s" (org-version nil t nil))
|
||||
)"#;
|
||||
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)?)
|
||||
/// Generate elisp to configure org-mode parsing settings
|
||||
///
|
||||
/// Currently only org-list-allow-alphabetical is supported.
|
||||
fn global_settings_elisp(global_settings: &GlobalSettings) -> String {
|
||||
// This string concatenation is wildly inefficient but its only called in tests 🤷.
|
||||
let mut ret = "".to_owned();
|
||||
if global_settings.list_allow_alphabetical {
|
||||
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();
|
||||
}
|
||||
if global_settings.odd_levels_only != HeadlineLevelFilter::default() {
|
||||
ret += match global_settings.odd_levels_only {
|
||||
HeadlineLevelFilter::Odd => "(setq org-odd-levels-only t)\n",
|
||||
HeadlineLevelFilter::OddEven => "(setq org-odd-levels-only nil)\n",
|
||||
};
|
||||
}
|
||||
ret
|
||||
}
|
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>(
|
||||
&'p self,
|
||||
) -> 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 {},)) }
|
||||
);
|
58
src/wasm/macros.rs
Normal file
58
src/wasm/macros.rs
Normal file
@ -0,0 +1,58 @@
|
||||
/// 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;
|
||||
$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,
|
||||
},
|
||||
))
|
||||
}
|
||||
);
|
24
src/wasm_cli/mod.rs
Normal file
24
src/wasm_cli/mod.rs
Normal file
@ -0,0 +1,24 @@
|
||||
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 serde_wasm_bindgen::to_value(&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 serde_wasm_bindgen::to_value(&ParseResult::Error(format!("{:?}", err)))
|
||||
.unwrap();
|
||||
}
|
||||
};
|
||||
serde_wasm_bindgen::to_value(&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;
|
131
src/wasm_test/runner.rs
Normal file
131
src/wasm_test/runner.rs
Normal file
@ -0,0 +1,131 @@
|
||||
use std::path::Path;
|
||||
|
||||
use super::compare::wasm_compare_document;
|
||||
use crate::context::GlobalSettings;
|
||||
use crate::parser::parse_file_with_settings;
|
||||
use crate::parser::parse_with_settings;
|
||||
use crate::settings::LocalFileAccessInterface;
|
||||
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;
|
||||
use crate::wasm::ToWasm;
|
||||
use crate::wasm::ToWasmContext;
|
||||
|
||||
pub async fn wasm_run_anonymous_compare<P: AsRef<str>>(
|
||||
org_contents: P,
|
||||
) -> Result<bool, Box<dyn std::error::Error>> {
|
||||
wasm_run_anonymous_compare_with_settings(org_contents, &GlobalSettings::default(), false).await
|
||||
}
|
||||
|
||||
pub async fn wasm_run_anonymous_compare_with_settings<'g, 's, P: AsRef<str>>(
|
||||
org_contents: P,
|
||||
global_settings: &GlobalSettings<'g, 's>,
|
||||
silent: bool,
|
||||
) -> Result<bool, Box<dyn std::error::Error>> {
|
||||
// TODO: This is a work-around to pretend that dos line endings do not exist. It would be better to handle the difference in line endings.
|
||||
let org_contents = org_contents.as_ref().replace("\r\n", "\n");
|
||||
let org_contents = org_contents.as_str();
|
||||
if !silent {
|
||||
print_versions().await?;
|
||||
}
|
||||
let rust_parsed = parse_with_settings(org_contents, global_settings)?;
|
||||
let to_wasm_context = ToWasmContext::new(org_contents);
|
||||
let wasm_parsed = rust_parsed
|
||||
.to_wasm(to_wasm_context)
|
||||
.map_err(|_e| "Failed to convert to wasm.")?;
|
||||
let org_sexp = emacs_parse_anonymous_org_document(org_contents, global_settings).await?;
|
||||
let (_remaining, parsed_sexp) = sexp(org_sexp.as_str()).map_err(|e| e.to_string())?;
|
||||
|
||||
if !silent {
|
||||
println!("{}\n\n\n", org_contents);
|
||||
println!("{}", org_sexp);
|
||||
println!("{:#?}", rust_parsed);
|
||||
println!("{}", serde_json::to_string(&wasm_parsed)?);
|
||||
}
|
||||
|
||||
// We do the diffing after printing out both parsed forms in case the diffing panics
|
||||
let diff_result = wasm_compare_document(org_contents, &parsed_sexp, &wasm_parsed)?;
|
||||
if !silent {
|
||||
diff_result.print(org_contents)?;
|
||||
}
|
||||
|
||||
if diff_result.is_bad() {
|
||||
return Ok(false);
|
||||
} else if !silent {
|
||||
println!(
|
||||
"{color}Entire document passes.{reset}",
|
||||
color = foreground_color(0, 255, 0),
|
||||
reset = reset_color(),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
//wasm_run_compare_on_file
|
||||
pub async fn wasm_run_compare_on_file<P: AsRef<Path>>(
|
||||
org_path: P,
|
||||
) -> Result<bool, Box<dyn std::error::Error>> {
|
||||
wasm_run_compare_on_file_with_settings(org_path, &GlobalSettings::default(), false).await
|
||||
}
|
||||
|
||||
pub async fn wasm_run_compare_on_file_with_settings<'g, 's, P: AsRef<Path>>(
|
||||
org_path: P,
|
||||
global_settings: &GlobalSettings<'g, 's>,
|
||||
silent: bool,
|
||||
) -> Result<bool, Box<dyn std::error::Error>> {
|
||||
let org_path = org_path.as_ref();
|
||||
if !silent {
|
||||
print_versions().await?;
|
||||
}
|
||||
let parent_directory = org_path
|
||||
.parent()
|
||||
.ok_or("Should be contained inside a directory.")?;
|
||||
let org_contents = std::fs::read_to_string(org_path)?;
|
||||
// TODO: This is a work-around to pretend that dos line endings do not exist. It would be better to handle the difference in line endings.
|
||||
let org_contents = org_contents.replace("\r\n", "\n");
|
||||
let org_contents = org_contents.as_str();
|
||||
let file_access_interface = LocalFileAccessInterface {
|
||||
working_directory: Some(parent_directory.to_path_buf()),
|
||||
};
|
||||
let global_settings = {
|
||||
let mut global_settings = global_settings.clone();
|
||||
global_settings.file_access = &file_access_interface;
|
||||
global_settings
|
||||
};
|
||||
let rust_parsed = parse_file_with_settings(org_contents, &global_settings, Some(org_path))?;
|
||||
let to_wasm_context = ToWasmContext::new(org_contents);
|
||||
let wasm_parsed = rust_parsed
|
||||
.to_wasm(to_wasm_context)
|
||||
.map_err(|_e| "Failed to convert to wasm.")?;
|
||||
let org_sexp = emacs_parse_file_org_document(org_path, &global_settings).await?;
|
||||
let (_remaining, parsed_sexp) = sexp(org_sexp.as_str()).map_err(|e| e.to_string())?;
|
||||
|
||||
if !silent {
|
||||
println!("{}\n\n\n", org_contents);
|
||||
println!("{}", org_sexp);
|
||||
println!("{:#?}", rust_parsed);
|
||||
println!("{}", serde_json::to_string(&wasm_parsed)?);
|
||||
}
|
||||
|
||||
// We do the diffing after printing out both parsed forms in case the diffing panics
|
||||
let diff_result = wasm_compare_document(org_contents, &parsed_sexp, &wasm_parsed)?;
|
||||
if !silent {
|
||||
diff_result.print(org_contents)?;
|
||||
}
|
||||
|
||||
if diff_result.is_bad() {
|
||||
return Ok(false);
|
||||
} else if !silent {
|
||||
println!(
|
||||
"{color}Entire document passes.{reset}",
|
||||
color = foreground_color(0, 255, 0),
|
||||
reset = reset_color(),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user