From b8e7127bd7ad6750f4547ff518f33b2ae520ec9a Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Fri, 30 Dec 2022 23:43:38 +0000 Subject: [PATCH] Revamp benchmark runner --- benchmarks/Cargo.toml | 1 - benchmarks/index.js | 46 ------- benchmarks/index.mjs | 82 ++++++++++++ benchmarks/package-lock.json | 175 ++++++++++++++++++++++++- benchmarks/package.json | 13 +- benchmarks/src/lib.rs | 241 ++++++++++++----------------------- 6 files changed, 348 insertions(+), 210 deletions(-) delete mode 100644 benchmarks/index.js create mode 100644 benchmarks/index.mjs diff --git a/benchmarks/Cargo.toml b/benchmarks/Cargo.toml index a520f40..b60c017 100644 --- a/benchmarks/Cargo.toml +++ b/benchmarks/Cargo.toml @@ -16,7 +16,6 @@ wasm-bindgen = { version = "^0.2" } crate-type = ["cdylib", "rlib"] [features] -default = ["serde-json", "serde-wasm-bindgen"] serde-json = ["wasm-bindgen/serde-serialize"] msgpack = ["rmp-serde"] diff --git a/benchmarks/index.js b/benchmarks/index.js deleted file mode 100644 index 55f22c0..0000000 --- a/benchmarks/index.js +++ /dev/null @@ -1,46 +0,0 @@ -'use strict'; - -const { Suite } = require('benchmark'); -const benches = require('./pkg'); - -let suites = { - parse: new Suite('parse'), - serialize: new Suite('serialize') -}; - -for (let input of ['canada', 'citm_catalog', 'twitter']) { - const json = require(`./data/${input}.json`); - - for (const lib of ['serde_json', 'serde_wasm_bindgen', 'msgpack']) { - const parse = benches[`parse_${input}_with_${lib}`]; - if (parse) { - suites.parse.add(`${input} x ${lib}`, () => parse(json).free()); - } - - const serialize = benches[`serialize_${input}_with_${lib}`]; - if (serialize) { - let parsed = parse(json); - suites.serialize.add(`${input} x ${lib}`, () => serialize(parsed), { - onComplete: () => parsed.free() - }); - } - } -} - -function runSuite(suite) { - console.log('='.repeat(suite.name.length)); - console.log(suite.name); - console.log('='.repeat(suite.name.length)); - - suite - .on('error', event => console.error(event.target.error)) - .on('cycle', event => console.log(event.target.toString())) - .run(); -} - -if (process.argv.length > 2) { - runSuite(suites[process.argv[2]]); -} else { - runSuite(suites.parse); - runSuite(suites.serialize); -} diff --git a/benchmarks/index.mjs b/benchmarks/index.mjs new file mode 100644 index 0000000..ec1d11a --- /dev/null +++ b/benchmarks/index.mjs @@ -0,0 +1,82 @@ +import Benchmark from 'benchmark'; +import { readdirSync, readFileSync } from 'fs'; +import prettyBytes from 'pretty-bytes'; +import { fileSync as brotliSize } from 'brotli-size'; +import { createRequire } from 'module'; +import { inspect } from 'util'; + +const { Suite } = Benchmark; +const require = createRequire(import.meta.url); + +let suiteOps = { + parse: {}, + serialize: {} +}; + +const libs = readdirSync('pkg').map(dir => ({ + name: dir, + impl: require(`./pkg/${dir}`) +})); + +console.log('=== Sizes ==='); +for (let { name } of libs) { + const [js, wasm] = [ + 'serde_wasm_bindgen_benches.js', + 'serde_wasm_bindgen_benches_bg.wasm' + ].map(file => prettyBytes(brotliSize(`pkg/${name}/${file}`))); + + console.log(`${name}: JS = ${js}, Wasm = ${wasm}`); +} +console.log(); + +function loadData(name) { + return JSON.parse(readFileSync(`./data/${name}.json`, 'utf8')); +} + +const datasets = { + Canada: loadData('canada'), + CitmCatalog: loadData('citm_catalog'), + Twitter: loadData('twitter') +}; + +let filter = new RegExp(process.argv[2] || '(?:)'); + +for (let { name: libName, impl } of libs) { + for (let [dataName, json] of Object.entries(datasets)) { + let { parse } = impl[dataName]; + + let parseBenchName = `parse ${dataName} ${libName}`; + if (filter.test(parseBenchName)) { + (suiteOps.parse[dataName] ??= new Suite(dataName)).add(libName, () => + parse(json).free() + ); + } + + let serializeBenchName = `serialize ${dataName} ${libName}`; + if (filter.test(serializeBenchName)) { + let parsed = parse(json); + (suiteOps.serialize[dataName] ??= new Suite(dataName)).add( + libName, + () => parsed.serialize(), + { + onComplete: () => parsed.free() + } + ); + } + } +} + +console.log('=== Benchmarks ==='); + +for (let [op, suites] of Object.entries(suiteOps)) { + console.group(op); + for (let suite of Object.values(suites)) { + console.group(suite.name); + suite + .on('error', event => console.error(event.target.error)) + .on('cycle', event => console.log(event.target.toString())) + .run(); + console.groupEnd(); + } + console.groupEnd(); +} diff --git a/benchmarks/package-lock.json b/benchmarks/package-lock.json index 1ccb0a3..ca5d493 100644 --- a/benchmarks/package-lock.json +++ b/benchmarks/package-lock.json @@ -7,7 +7,10 @@ "name": "@serde-wasm-bindgen/benches", "dependencies": { "@msgpack/msgpack": "^2.8.0", - "benchmark": "^2.1.4" + "benchmark": "^2.1.4", + "brotli-size": "^4.0.0", + "cross-env": "^7.0.3", + "pretty-bytes": "^6.0.0" } }, "node_modules/@msgpack/msgpack": { @@ -27,15 +30,118 @@ "platform": "^1.3.3" } }, + "node_modules/brotli-size": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/brotli-size/-/brotli-size-4.0.0.tgz", + "integrity": "sha512-uA9fOtlTRC0iqKfzff1W34DXUA3GyVqbUaeo3Rw3d4gd1eavKVCETXrn3NzO74W+UVkG3UHu8WxUi+XvKI/huA==", + "dependencies": { + "duplexer": "0.1.1" + }, + "engines": { + "node": ">= 10.16.0" + } + }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/duplexer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha512-sxNZ+ljy+RA1maXoUReeqBBpBC6RLKmg5ewzV+x+mSETmWNoKdZN6vcQjpFROemza23hGFskJtFNoUWUaQ+R4Q==" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, "node_modules/platform": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.5.tgz", "integrity": "sha512-TuvHS8AOIZNAlE77WUDiR4rySV/VMptyMfcfeoMgs4P8apaZM3JrnbzBiixKUv+XR6i+BXrQh8WAnjaSPFO65Q==" + }, + "node_modules/pretty-bytes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.0.0.tgz", + "integrity": "sha512-6UqkYefdogmzqAZWzJ7laYeJnaXDy2/J+ZqiiMtS7t7OfpXWTlaeGMwX8U6EFvPV/YWWEKRkS8hKS4k60WHTOg==", + "engines": { + "node": "^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } } }, "dependencies": { @@ -53,15 +159,82 @@ "platform": "^1.3.3" } }, + "brotli-size": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/brotli-size/-/brotli-size-4.0.0.tgz", + "integrity": "sha512-uA9fOtlTRC0iqKfzff1W34DXUA3GyVqbUaeo3Rw3d4gd1eavKVCETXrn3NzO74W+UVkG3UHu8WxUi+XvKI/huA==", + "requires": { + "duplexer": "0.1.1" + } + }, + "cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "requires": { + "cross-spawn": "^7.0.1" + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "duplexer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha512-sxNZ+ljy+RA1maXoUReeqBBpBC6RLKmg5ewzV+x+mSETmWNoKdZN6vcQjpFROemza23hGFskJtFNoUWUaQ+R4Q==" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, "platform": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.5.tgz", "integrity": "sha512-TuvHS8AOIZNAlE77WUDiR4rySV/VMptyMfcfeoMgs4P8apaZM3JrnbzBiixKUv+XR6i+BXrQh8WAnjaSPFO65Q==" + }, + "pretty-bytes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.0.0.tgz", + "integrity": "sha512-6UqkYefdogmzqAZWzJ7laYeJnaXDy2/J+ZqiiMtS7t7OfpXWTlaeGMwX8U6EFvPV/YWWEKRkS8hKS4k60WHTOg==" + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } } } } diff --git a/benchmarks/package.json b/benchmarks/package.json index da6ddfa..95bc87b 100644 --- a/benchmarks/package.json +++ b/benchmarks/package.json @@ -3,10 +3,17 @@ "private": true, "dependencies": { "@msgpack/msgpack": "^2.8.0", - "benchmark": "^2.1.4" + "benchmark": "^2.1.4", + "brotli-size": "^4.0.0", + "cross-env": "^7.0.3", + "pretty-bytes": "^6.0.0" }, "scripts": { - "pretest": "wasm-pack build -t nodejs --profiling", - "test": "node index.js" + "build:swb": "wasm-pack build -t nodejs --out-dir pkg/serde-wasm-bindgen -- --features serde-wasm-bindgen", + "build:swb-reftypes": "cross-env RUSTFLAGS=\"-C target-feature=+reference-types\" WASM_BINDGEN_EXTERNREF=1 wasm-pack build -t nodejs --out-dir pkg/serde-wasm-bindgen-reftypes -- --features serde-wasm-bindgen", + "build:json": "wasm-pack build -t nodejs --out-dir pkg/serde-json -- --features serde-json", + "build:msgpack": "wasm-pack build -t nodejs --out-dir pkg/msgpack -- --features msgpack", + "build": "npm run build:swb && npm run build:swb-reftypes && npm run build:json", + "test": "node index.mjs" } } diff --git a/benchmarks/src/lib.rs b/benchmarks/src/lib.rs index bb0a01e..ad388f0 100644 --- a/benchmarks/src/lib.rs +++ b/benchmarks/src/lib.rs @@ -1,171 +1,94 @@ -use serde::{Deserialize, Serialize}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; use wasm_bindgen::prelude::*; use wasm_bindgen::JsValue; mod color; mod prim_str; -mod canada; -mod citm_catalog; -mod twitter; - -#[wasm_bindgen] -#[derive(Serialize, Deserialize)] -pub struct Canada(canada::Canada); - -#[wasm_bindgen] -#[derive(Serialize, Deserialize)] -pub struct CitmCatalog(citm_catalog::CitmCatalog); - -#[wasm_bindgen] -#[derive(Serialize, Deserialize)] -pub struct Twitter(twitter::Twitter); - #[wasm_bindgen(start)] pub fn init_console() { console_error_panic_hook::set_once(); } -// Like serde_wasm_bindgen_to_value but with JSON-like output (no Maps). -fn serde_wasm_bindgen_to_value( - value: &impl Serialize, -) -> Result { - let serializer = serde_wasm_bindgen::Serializer::new().serialize_maps_as_objects(true); - value.serialize(&serializer) -} - -#[cfg(feature = "msgpack")] -mod msgpack_js { - use super::*; - use serde::de::DeserializeOwned; - - #[wasm_bindgen(module = "@msgpack/msgpack")] - extern "C" { - fn encode(input: &JsValue) -> Vec; - fn decode(input: &[u8]) -> JsValue; - } - - pub fn parse(input: JsValue) -> Result { - let input = encode(&input); - rmp_serde::from_slice(&input) - } - - pub fn serialize(input: &T) -> Result { - let input = rmp_serde::to_vec(input)?; - Ok(decode(&input)) - } -} - -#[cfg(feature = "serde-wasm-bindgen")] -#[wasm_bindgen] -pub fn parse_canada_with_serde_wasm_bindgen(input: JsValue) -> Canada { - serde_wasm_bindgen::from_value(input).unwrap() -} - -#[cfg(feature = "serde-json")] -#[wasm_bindgen] -#[allow(deprecated)] -pub fn parse_canada_with_serde_json(input: JsValue) -> Canada { - input.into_serde().unwrap() -} - -#[cfg(feature = "msgpack")] -#[wasm_bindgen] -pub fn parse_canada_with_msgpack(input: JsValue) -> Canada { - msgpack_js::parse(input).unwrap() -} - -#[cfg(feature = "serde-wasm-bindgen")] -#[wasm_bindgen] -pub fn serialize_canada_with_serde_wasm_bindgen(input: &Canada) -> JsValue { - serde_wasm_bindgen_to_value(input).unwrap() -} - -#[cfg(feature = "serde-json")] -#[wasm_bindgen] -#[allow(deprecated)] -pub fn serialize_canada_with_serde_json(input: &Canada) -> JsValue { - JsValue::from_serde(input).unwrap() -} - -#[cfg(feature = "msgpack")] -#[wasm_bindgen] -pub fn serialize_canada_with_msgpack(input: &Canada) -> JsValue { - msgpack_js::serialize(input).unwrap() -} - -#[cfg(feature = "serde-wasm-bindgen")] -#[wasm_bindgen] -pub fn parse_citm_catalog_with_serde_wasm_bindgen(input: JsValue) -> CitmCatalog { - serde_wasm_bindgen::from_value(input).unwrap() -} - -#[cfg(feature = "serde-json")] -#[wasm_bindgen] -#[allow(deprecated)] -pub fn parse_citm_catalog_with_serde_json(input: JsValue) -> CitmCatalog { - input.into_serde().unwrap() -} - -#[cfg(feature = "msgpack")] -#[wasm_bindgen] -pub fn parse_citm_catalog_with_msgpack(input: JsValue) -> CitmCatalog { - msgpack_js::parse(input).unwrap() -} - -#[cfg(feature = "serde-wasm-bindgen")] -#[wasm_bindgen] -pub fn serialize_citm_catalog_with_serde_wasm_bindgen(input: &CitmCatalog) -> JsValue { - serde_wasm_bindgen_to_value(input).unwrap() -} - -#[cfg(feature = "serde-json")] -#[wasm_bindgen] -#[allow(deprecated)] -pub fn serialize_citm_catalog_with_serde_json(input: &CitmCatalog) -> JsValue { - JsValue::from_serde(input).unwrap() -} - -#[cfg(feature = "msgpack")] -#[wasm_bindgen] -pub fn serialize_citm_catalog_with_msgpack(input: &CitmCatalog) -> JsValue { - msgpack_js::serialize(input).unwrap() -} - -#[cfg(feature = "serde-wasm-bindgen")] -#[wasm_bindgen] -pub fn parse_twitter_with_serde_wasm_bindgen(input: JsValue) -> Twitter { - serde_wasm_bindgen::from_value(input).unwrap() -} - -#[cfg(feature = "serde-json")] -#[wasm_bindgen] -#[allow(deprecated)] -pub fn parse_twitter_with_serde_json(input: JsValue) -> Twitter { - input.into_serde().unwrap() -} - -#[cfg(feature = "msgpack")] -#[wasm_bindgen] -pub fn parse_twitter_with_msgpack(input: JsValue) -> Twitter { - msgpack_js::parse(input).unwrap() -} - -#[cfg(feature = "serde-wasm-bindgen")] -#[wasm_bindgen] -pub fn serialize_twitter_with_serde_wasm_bindgen(input: &Twitter) -> JsValue { - serde_wasm_bindgen_to_value(input).unwrap() -} - -#[cfg(feature = "serde-json")] -#[wasm_bindgen] -#[allow(deprecated)] -pub fn serialize_twitter_with_serde_json(input: &Twitter) -> JsValue { - JsValue::from_serde(input).unwrap() -} - -#[cfg(feature = "msgpack")] -#[wasm_bindgen] -pub fn serialize_twitter_with_msgpack(input: &Twitter) -> JsValue { - msgpack_js::serialize(input).unwrap() +macro_rules! serde_impl { + (|$input:ident| { + $($feature:literal => $(# $attr:tt)* { + parse: $parse:expr, + serialize: $serialize:expr, + },)* + }) => { + $( + #[cfg(feature = $feature)] + $(# $attr)* + mod serde_impl { + use super::*; + + pub fn parse($input: JsValue) -> T { + $parse + } + + pub fn serialize($input: &T) -> JsValue { + $serialize + } + } + )* + }; +} + +serde_impl!(|input| { + "serde-wasm-bindgen" => { + parse: serde_wasm_bindgen::from_value(input).unwrap_throw(), + serialize: { + const SERIALIZER: serde_wasm_bindgen::Serializer = serde_wasm_bindgen::Serializer::json_compatible(); + input.serialize(&SERIALIZER).unwrap_throw() + }, + }, + + "serde-json" => #[allow(deprecated)] { + parse: input.into_serde().unwrap_throw(), + serialize: JsValue::from_serde(input).unwrap_throw(), + }, + + "msgpack" => { + parse: { + let input = encode(&input); + rmp_serde::from_slice(&input).unwrap_throw() + }, + + serialize: { + let input = rmp_serde::to_vec(input).unwrap_throw(); + decode(&input) + }, + }, +}); + +macro_rules! datasets { + ($($mod:ident :: $ty:ident,)*) => { + $( + mod $mod; + + #[wasm_bindgen] + #[derive(Serialize, Deserialize)] + pub struct $ty($mod::$ty); + + #[wasm_bindgen] + impl $ty { + #[wasm_bindgen] + pub fn parse(input: JsValue) -> $ty { + serde_impl::parse(input) + } + + #[wasm_bindgen] + pub fn serialize(&self) -> JsValue { + serde_impl::serialize(self) + } + } + )* + }; +} + +datasets! { + canada::Canada, + citm_catalog::CitmCatalog, + twitter::Twitter, }