mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 18:43:44 +00:00
feat(ast/estree)!: replace serde with custom ESTree serializer (#9256)
Replace `serde` with a custom serializer specialized for writing JSON. `impl Serialize` -> `impl ESTree`. I've called it `ESTree` in case we want to add e.g. `impl Babel` later on. The main motivation is that with control of the serializer, we can make it stateful, so we can extend it to, for example, omit TS fields based on a flag. This is pretty much impossible with `serde`'s interface. But a side-effect of the simpler implementation is that it's also more performant. The usage of `ESTree` trait is very similar to `serde::Serialize`. Main differences: * `serialize` method is infallible. Does not return a `Result`. * `state = serializer.serialize_struct();` instead of `map = serializer.serialize_map();`. * `state.serialize_field(...)` instead of `map.serialize_entry(...)`. * Struct keys must be strings, and JSON-safe (not including `"` or other characters that require escaping in JSON). This is a breaking change, as it's possible downstream consumers are using `serde` to serialize our AST to other formats. I'd guess unlikely, but possible. We can always implement `serde::Serialize` as well as `ESTree` further down the line if there's a demand.
This commit is contained in:
1 parent
ed6bd5dc8b
commit
216b33fd2a
37 files changed
+3782
-2687
No files matched your search
Generated
+4
-6
@@ -1565,6 +1565,7 @@ dependencies = [
|
||||
"allocator-api2",
|
||||
"bumpalo",
|
||||
"hashbrown 0.15.2",
|
||||
"oxc_estree",
|
||||
"rustc-hash",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -1584,8 +1585,6 @@ dependencies = [
|
||||
"oxc_regular_expression",
|
||||
"oxc_span",
|
||||
"oxc_syntax",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1751,7 +1750,8 @@ dependencies = [
|
||||
name = "oxc_estree"
|
||||
version = "0.51.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"itoa",
|
||||
"ryu-js",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1964,7 +1964,6 @@ dependencies = [
|
||||
"pico-args",
|
||||
"rustc-hash",
|
||||
"seq-macro",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1982,7 +1981,6 @@ dependencies = [
|
||||
"oxc_sourcemap",
|
||||
"rustc-hash",
|
||||
"self_cell",
|
||||
"serde_json",
|
||||
"string_wizard",
|
||||
]
|
||||
|
||||
@@ -2040,7 +2038,6 @@ dependencies = [
|
||||
"oxc_span",
|
||||
"phf",
|
||||
"rustc-hash",
|
||||
"serde",
|
||||
"unicode-id-start",
|
||||
]
|
||||
|
||||
@@ -2249,6 +2246,7 @@ dependencies = [
|
||||
"oxc_prettier",
|
||||
"serde",
|
||||
"serde-wasm-bindgen",
|
||||
"serde_json",
|
||||
"tsify",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
@@ -19,6 +19,8 @@ workspace = true
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
oxc_estree = { workspace = true, optional = true }
|
||||
|
||||
allocator-api2 = { workspace = true }
|
||||
bumpalo = { workspace = true, features = ["allocator-api2", "collections"] }
|
||||
hashbrown = { workspace = true, default-features = false, features = ["inline-more", "allocator-api2"] }
|
||||
@@ -32,4 +34,4 @@ serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
|
||||
[features]
|
||||
serialize = ["dep:serde"]
|
||||
serialize = ["dep:serde", "oxc_estree/serialize"]
|
||||
@@ -12,7 +12,9 @@ use std::{
|
||||
};
|
||||
|
||||
#[cfg(any(feature = "serialize", test))]
|
||||
use serde::{Serialize, Serializer};
|
||||
use oxc_estree::{ESTree, Serializer as ESTreeSerializer};
|
||||
#[cfg(any(feature = "serialize", test))]
|
||||
use serde::{Serialize, Serializer as SerdeSerializer};
|
||||
|
||||
use crate::Allocator;
|
||||
|
||||
@@ -183,11 +185,18 @@ impl<T: ?Sized + Debug> Debug for Box<'_, T> {
|
||||
|
||||
#[cfg(any(feature = "serialize", test))]
|
||||
impl<T: Serialize> Serialize for Box<'_, T> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
fn serialize<S: SerdeSerializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
self.deref().serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "serialize", test))]
|
||||
impl<T: ESTree> ESTree for Box<'_, T> {
|
||||
fn serialize<S: ESTreeSerializer>(&self, serializer: S) {
|
||||
self.deref().serialize(serializer);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Hash> Hash for Box<'_, T> {
|
||||
#[inline]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
@@ -238,8 +247,21 @@ mod test {
|
||||
fn box_serialize() {
|
||||
let allocator = Allocator::default();
|
||||
let b = Box::new_in("x", &allocator);
|
||||
let b = serde_json::to_string(&b).unwrap();
|
||||
assert_eq!(b, "\"x\"");
|
||||
let s = serde_json::to_string(&b).unwrap();
|
||||
assert_eq!(s, r#""x""#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn box_serialize_estree() {
|
||||
use oxc_estree::{CompactSerializer, ESTree};
|
||||
|
||||
let allocator = Allocator::default();
|
||||
let b = Box::new_in("x", &allocator);
|
||||
|
||||
let mut serializer = CompactSerializer::new();
|
||||
b.serialize(&mut serializer);
|
||||
let s = serializer.into_string();
|
||||
assert_eq!(s, r#""x""#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -18,7 +18,9 @@ use std::{
|
||||
use allocator_api2::vec::Vec as InnerVec;
|
||||
use bumpalo::Bump;
|
||||
#[cfg(any(feature = "serialize", test))]
|
||||
use serde::{Serialize, Serializer};
|
||||
use oxc_estree::{ESTree, Serializer as ESTreeSerializer};
|
||||
#[cfg(any(feature = "serialize", test))]
|
||||
use serde::{Serialize, Serializer as SerdeSerializer};
|
||||
|
||||
use crate::{Allocator, Box};
|
||||
|
||||
@@ -274,11 +276,18 @@ where
|
||||
|
||||
#[cfg(any(feature = "serialize", test))]
|
||||
impl<T: Serialize> Serialize for Vec<'_, T> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
fn serialize<S: SerdeSerializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
self.as_slice().serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "serialize", test))]
|
||||
impl<T: ESTree> ESTree for Vec<'_, T> {
|
||||
fn serialize<S: ESTreeSerializer>(&self, serializer: S) {
|
||||
self.as_slice().serialize(serializer);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Hash> Hash for Vec<'_, T> {
|
||||
#[inline(always)]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
@@ -319,8 +328,22 @@ mod test {
|
||||
let allocator = Allocator::default();
|
||||
let mut v = Vec::new_in(&allocator);
|
||||
v.push("x");
|
||||
let v = serde_json::to_string(&v).unwrap();
|
||||
assert_eq!(v, "[\"x\"]");
|
||||
let s = serde_json::to_string(&v).unwrap();
|
||||
assert_eq!(s, r#"["x"]"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vec_serialize_estree() {
|
||||
use oxc_estree::{CompactSerializer, ESTree};
|
||||
|
||||
let allocator = Allocator::default();
|
||||
let mut v = Vec::new_in(&allocator);
|
||||
v.push("x");
|
||||
|
||||
let mut serializer = CompactSerializer::new();
|
||||
v.serialize(&mut serializer);
|
||||
let s = serializer.into_string();
|
||||
assert_eq!(s, r#"["x"]"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -30,18 +30,12 @@ bitflags = { workspace = true }
|
||||
cow-utils = { workspace = true }
|
||||
nonmax = { workspace = true }
|
||||
|
||||
serde = { workspace = true, optional = true }
|
||||
serde_json = { workspace = true, optional = true }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
serialize = [
|
||||
"dep:serde",
|
||||
"dep:serde_json",
|
||||
"oxc_allocator/serialize",
|
||||
"oxc_regular_expression/serialize",
|
||||
"oxc_span/serialize",
|
||||
"oxc_syntax/serialize",
|
||||
"oxc_syntax/to_js_string",
|
||||
"oxc_estree/serialize",
|
||||
]
|
||||
@@ -45,7 +45,7 @@ impl NumericLiteral<'_> {
|
||||
return int32_value;
|
||||
}
|
||||
|
||||
// NaN, Infinity if not included in our NumericLiteral, so we just serde(skip) step 2.
|
||||
// NaN, Infinity if not included in our NumericLiteral, so we just skip step 2.
|
||||
|
||||
// step 3
|
||||
let pos_int = num.signum() * num.abs().floor();
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -22,7 +22,7 @@
|
||||
//! You can obtain an AST by parsing source code with a [`Parser`] from [`oxc_parser`].
|
||||
//!
|
||||
//! ## Cargo Features
|
||||
//! * `"serde"` enables support for serde serialization
|
||||
//! * `"serialize"` enables support for serialization to ESTree JSON
|
||||
//!
|
||||
//! [`BindingIdentifier`]: ast::BindingIdentifier
|
||||
//! [`IdentifierReference`]: ast::IdentifierReference
|
||||
|
||||
+148
-200
@@ -1,79 +1,25 @@
|
||||
use std::io::Write;
|
||||
|
||||
use cow_utils::CowUtils;
|
||||
use serde::{
|
||||
ser::{SerializeMap, SerializeSeq, Serializer},
|
||||
Serialize,
|
||||
};
|
||||
|
||||
use oxc_ast_macros::ast_meta;
|
||||
use oxc_estree::{
|
||||
CompactSerializer, ESTree, PrettySerializer, SequenceSerializer, Serializer, StructSerializer,
|
||||
};
|
||||
|
||||
use crate::ast::*;
|
||||
|
||||
impl Program<'_> {
|
||||
/// Serialize AST to JSON.
|
||||
//
|
||||
// Should not panic if everything is working correctly.
|
||||
// Serializing into a `Vec<u8>` should be infallible.
|
||||
#[expect(clippy::missing_panics_doc)]
|
||||
/// Serialize AST to ESTree JSON.
|
||||
pub fn to_json(&self) -> String {
|
||||
let buf = Vec::new();
|
||||
let ser = self.to_json_into_writer(buf).unwrap();
|
||||
let buf = ser.into_inner();
|
||||
// SAFETY: `serde_json` outputs valid UTF-8.
|
||||
// `serde_json::to_string` also uses `from_utf8_unchecked`.
|
||||
// https://github.com/serde-rs/json/blob/1174c5f57db44c26460951b525c6ede50984b655/src/ser.rs#L2209-L2219
|
||||
unsafe { String::from_utf8_unchecked(buf) }
|
||||
let mut serializer = CompactSerializer::new();
|
||||
self.serialize(&mut serializer);
|
||||
serializer.into_string()
|
||||
}
|
||||
|
||||
/// Serialize AST into a "black hole" writer.
|
||||
///
|
||||
/// Only useful for testing, to make sure serialization completes successfully.
|
||||
/// Should be faster than [`Program::to_json`], as does not actually produce any output.
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns `Err` if serialization fails.
|
||||
#[doc(hidden)]
|
||||
pub fn test_to_json(&self) -> Result<(), serde_json::Error> {
|
||||
struct BlackHole;
|
||||
|
||||
#[expect(clippy::inline_always)]
|
||||
impl Write for BlackHole {
|
||||
#[inline(always)]
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> {
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn flush(&mut self) -> Result<(), std::io::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
self.to_json_into_writer(BlackHole).map(|_| ())
|
||||
}
|
||||
|
||||
/// Serialize AST into the provided writer.
|
||||
fn to_json_into_writer<W: Write>(
|
||||
&self,
|
||||
writer: W,
|
||||
) -> Result<serde_json::Serializer<W, EcmaFormatter>, serde_json::Error> {
|
||||
let mut ser = serde_json::Serializer::with_formatter(writer, EcmaFormatter);
|
||||
self.serialize(&mut ser)?;
|
||||
Ok(ser)
|
||||
}
|
||||
}
|
||||
|
||||
/// `serde_json` formatter which uses `ryu_js` to serialize `f64`.
|
||||
pub struct EcmaFormatter;
|
||||
|
||||
impl serde_json::ser::Formatter for EcmaFormatter {
|
||||
fn write_f64<W>(&mut self, writer: &mut W, value: f64) -> std::io::Result<()>
|
||||
where
|
||||
W: ?Sized + std::io::Write,
|
||||
{
|
||||
use oxc_syntax::number::ToJsString;
|
||||
writer.write_all(value.to_js_string().as_bytes())
|
||||
/// Serialize AST to ESTree JSON.
|
||||
pub fn to_pretty_json(&self) -> String {
|
||||
let mut serializer = PrettySerializer::new();
|
||||
self.serialize(&mut serializer);
|
||||
serializer.into_string()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,9 +32,9 @@ impl serde_json::ser::Formatter for EcmaFormatter {
|
||||
#[estree(ts_type = "null")]
|
||||
pub struct Null<'b, T>(#[expect(dead_code)] pub &'b T);
|
||||
|
||||
impl<T> Serialize for Null<'_, T> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
().serialize(serializer)
|
||||
impl<T> ESTree for Null<'_, T> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
().serialize(serializer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,9 +43,9 @@ impl<T> Serialize for Null<'_, T> {
|
||||
#[estree(ts_type = "true")]
|
||||
pub struct True<'b, T>(#[expect(dead_code)] pub &'b T);
|
||||
|
||||
impl<T> Serialize for True<'_, T> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
true.serialize(serializer)
|
||||
impl<T> ESTree for True<'_, T> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
true.serialize(serializer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,9 +54,9 @@ impl<T> Serialize for True<'_, T> {
|
||||
#[estree(ts_type = "false")]
|
||||
pub struct False<'b, T>(#[expect(dead_code)] pub &'b T);
|
||||
|
||||
impl<T> Serialize for False<'_, T> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
false.serialize(serializer)
|
||||
impl<T> ESTree for False<'_, T> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
false.serialize(serializer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,9 +65,9 @@ impl<T> Serialize for False<'_, T> {
|
||||
#[estree(ts_type = "'in'")]
|
||||
pub struct In<'b, T>(#[expect(dead_code)] pub &'b T);
|
||||
|
||||
impl<T> Serialize for In<'_, T> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
"in".serialize(serializer)
|
||||
impl<T> ESTree for In<'_, T> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
"in".serialize(serializer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,9 +76,9 @@ impl<T> Serialize for In<'_, T> {
|
||||
#[estree(ts_type = "'init'")]
|
||||
pub struct Init<'b, T>(#[expect(dead_code)] pub &'b T);
|
||||
|
||||
impl<T> Serialize for Init<'_, T> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
"init".serialize(serializer)
|
||||
impl<T> ESTree for Init<'_, T> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
"init".serialize(serializer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,8 +91,9 @@ impl<T> Serialize for Init<'_, T> {
|
||||
#[estree(ts_type = "string | null")]
|
||||
pub struct BooleanLiteralRaw<'b>(pub &'b BooleanLiteral);
|
||||
|
||||
impl Serialize for BooleanLiteralRaw<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
impl ESTree for BooleanLiteralRaw<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
#[expect(clippy::collection_is_never_read)] // Clippy is wrong!
|
||||
let raw = if self.0.span.is_unspanned() {
|
||||
None
|
||||
} else if self.0.value {
|
||||
@@ -154,7 +101,7 @@ impl Serialize for BooleanLiteralRaw<'_> {
|
||||
} else {
|
||||
Some("false")
|
||||
};
|
||||
raw.serialize(serializer)
|
||||
raw.serialize(serializer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,10 +110,11 @@ impl Serialize for BooleanLiteralRaw<'_> {
|
||||
#[estree(ts_type = "'null' | null")]
|
||||
pub struct NullLiteralRaw<'b>(pub &'b NullLiteral);
|
||||
|
||||
impl Serialize for NullLiteralRaw<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
impl ESTree for NullLiteralRaw<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
#[expect(clippy::collection_is_never_read)] // Clippy is wrong!
|
||||
let raw = if self.0.span.is_unspanned() { None } else { Some("null") };
|
||||
raw.serialize(serializer)
|
||||
raw.serialize(serializer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,10 +123,10 @@ impl Serialize for NullLiteralRaw<'_> {
|
||||
#[estree(ts_type = "string")]
|
||||
pub struct BigIntLiteralBigint<'a, 'b>(pub &'b BigIntLiteral<'a>);
|
||||
|
||||
impl Serialize for BigIntLiteralBigint<'_, '_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
impl ESTree for BigIntLiteralBigint<'_, '_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let bigint = self.0.raw.strip_suffix('n').unwrap().cow_replace('_', "");
|
||||
bigint.serialize(serializer)
|
||||
bigint.serialize(serializer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,9 +137,9 @@ impl Serialize for BigIntLiteralBigint<'_, '_> {
|
||||
#[estree(ts_type = "BigInt")]
|
||||
pub struct BigIntLiteralValue<'a, 'b>(#[expect(dead_code)] pub &'b BigIntLiteral<'a>);
|
||||
|
||||
impl Serialize for BigIntLiteralValue<'_, '_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
serializer.serialize_none()
|
||||
impl ESTree for BigIntLiteralValue<'_, '_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
().serialize(serializer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,10 +148,10 @@ impl Serialize for BigIntLiteralValue<'_, '_> {
|
||||
#[estree(ts_type = "RegExp")]
|
||||
pub struct RegExpLiteralRegex<'a, 'b>(pub &'b RegExpLiteral<'a>);
|
||||
|
||||
impl Serialize for RegExpLiteralRegex<'_, '_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
map.serialize_entry("pattern", &self.0.regex.pattern)?;
|
||||
impl ESTree for RegExpLiteralRegex<'_, '_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("pattern", &self.0.regex.pattern);
|
||||
|
||||
// If `raw` field is present, flags must be in same order as in source to match Acorn.
|
||||
// Count number of set bits in `flags` to get number of flags
|
||||
@@ -212,11 +160,11 @@ impl Serialize for RegExpLiteralRegex<'_, '_> {
|
||||
if let Some(raw) = &self.0.raw {
|
||||
let flags_count = flags.bits().count_ones() as usize;
|
||||
let flags_index = raw.len() - flags_count;
|
||||
map.serialize_entry("flags", &raw[flags_index..])?;
|
||||
state.serialize_field("flags", &raw[flags_index..]);
|
||||
} else {
|
||||
map.serialize_entry("flags", &flags)?;
|
||||
state.serialize_field("flags", &flags);
|
||||
}
|
||||
map.end()
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,21 +175,21 @@ impl Serialize for RegExpLiteralRegex<'_, '_> {
|
||||
#[estree(ts_type = "RegExp | null")]
|
||||
pub struct RegExpLiteralValue<'a, 'b>(#[expect(dead_code)] pub &'b RegExpLiteral<'a>);
|
||||
|
||||
impl Serialize for RegExpLiteralValue<'_, '_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
serializer.serialize_none()
|
||||
impl ESTree for RegExpLiteralValue<'_, '_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
().serialize(serializer);
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for RegExpFlags {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
serializer.serialize_str(&self.to_string())
|
||||
impl ESTree for RegExpFlags {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
self.to_string().as_str().serialize(serializer);
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for RegExpPattern<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
serializer.serialize_str(&self.to_string())
|
||||
impl ESTree for RegExpPattern<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
self.to_string().as_str().serialize(serializer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,42 +198,42 @@ impl Serialize for RegExpPattern<'_> {
|
||||
// --------------------
|
||||
|
||||
/// Serialize `ArrayExpressionElement::Elision` variant as `null`.
|
||||
impl Serialize for Elision {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
serializer.serialize_none()
|
||||
impl ESTree for Elision {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
().serialize(serializer);
|
||||
}
|
||||
}
|
||||
|
||||
/// Serialize `FormalParameters`, to be estree compatible, with `items` and `rest` fields combined
|
||||
/// and `argument` field flattened.
|
||||
impl Serialize for FormalParameters<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let mut seq = serializer.serialize_seq(None)?;
|
||||
impl ESTree for FormalParameters<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut seq = serializer.serialize_sequence();
|
||||
for item in &self.items {
|
||||
seq.serialize_element(item)?;
|
||||
seq.serialize_element(item);
|
||||
}
|
||||
|
||||
if let Some(rest) = &self.rest {
|
||||
seq.serialize_element(&FormalParametersRest(rest))?;
|
||||
seq.serialize_element(&FormalParametersRest(rest));
|
||||
}
|
||||
|
||||
seq.end()
|
||||
seq.end();
|
||||
}
|
||||
}
|
||||
|
||||
struct FormalParametersRest<'a, 'b>(&'b BindingRestElement<'a>);
|
||||
|
||||
impl Serialize for FormalParametersRest<'_, '_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
impl ESTree for FormalParametersRest<'_, '_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let rest = self.0;
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
map.serialize_entry("type", "RestElement")?;
|
||||
map.serialize_entry("start", &rest.span.start)?;
|
||||
map.serialize_entry("end", &rest.span.end)?;
|
||||
map.serialize_entry("argument", &rest.argument.kind)?;
|
||||
map.serialize_entry("type_annotation", &rest.argument.type_annotation)?;
|
||||
map.serialize_entry("optional", &rest.argument.optional)?;
|
||||
map.end()
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "RestElement");
|
||||
state.serialize_field("start", &rest.span.start);
|
||||
state.serialize_field("end", &rest.span.end);
|
||||
state.serialize_field("argument", &rest.argument.kind);
|
||||
state.serialize_field("type_annotation", &rest.argument.type_annotation);
|
||||
state.serialize_field("optional", &rest.argument.optional);
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,59 +244,59 @@ impl Serialize for FormalParametersRest<'_, '_> {
|
||||
#[estree(ts_type = "Array<ImportDeclarationSpecifier>")]
|
||||
pub struct ImportDeclarationSpecifiers<'a, 'b>(pub &'b ImportDeclaration<'a>);
|
||||
|
||||
impl Serialize for ImportDeclarationSpecifiers<'_, '_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
impl ESTree for ImportDeclarationSpecifiers<'_, '_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
if let Some(specifiers) = &self.0.specifiers {
|
||||
specifiers.serialize(serializer)
|
||||
specifiers.serialize(serializer);
|
||||
} else {
|
||||
[(); 0].serialize(serializer)
|
||||
[(); 0].serialize(serializer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Serialize `ObjectProperty` with fields in same order as Acorn.
|
||||
impl Serialize for ObjectProperty<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
map.serialize_entry("type", "Property")?;
|
||||
map.serialize_entry("start", &self.span.start)?;
|
||||
map.serialize_entry("end", &self.span.end)?;
|
||||
map.serialize_entry("method", &self.method)?;
|
||||
map.serialize_entry("shorthand", &self.shorthand)?;
|
||||
map.serialize_entry("computed", &self.computed)?;
|
||||
map.serialize_entry("key", &self.key)?;
|
||||
impl ESTree for ObjectProperty<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "Property");
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("method", &self.method);
|
||||
state.serialize_field("shorthand", &self.shorthand);
|
||||
state.serialize_field("computed", &self.computed);
|
||||
state.serialize_field("key", &self.key);
|
||||
// Acorn has `kind` field before `value` for methods and shorthand properties
|
||||
if self.method || self.kind != PropertyKind::Init || self.shorthand {
|
||||
map.serialize_entry("kind", &self.kind)?;
|
||||
map.serialize_entry("value", &self.value)?;
|
||||
state.serialize_field("kind", &self.kind);
|
||||
state.serialize_field("value", &self.value);
|
||||
} else {
|
||||
map.serialize_entry("value", &self.value)?;
|
||||
map.serialize_entry("kind", &self.kind)?;
|
||||
state.serialize_field("value", &self.value);
|
||||
state.serialize_field("kind", &self.kind);
|
||||
}
|
||||
map.end()
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
|
||||
/// Serialize `BindingProperty` with fields in same order as Acorn.
|
||||
impl Serialize for BindingProperty<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
map.serialize_entry("type", "Property")?;
|
||||
map.serialize_entry("start", &self.span.start)?;
|
||||
map.serialize_entry("end", &self.span.end)?;
|
||||
map.serialize_entry("method", &false)?;
|
||||
map.serialize_entry("shorthand", &self.shorthand)?;
|
||||
map.serialize_entry("computed", &self.computed)?;
|
||||
map.serialize_entry("key", &self.key)?;
|
||||
impl ESTree for BindingProperty<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "Property");
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("method", &false);
|
||||
state.serialize_field("shorthand", &self.shorthand);
|
||||
state.serialize_field("computed", &self.computed);
|
||||
state.serialize_field("key", &self.key);
|
||||
// Acorn has `kind` field before `value` for shorthand properties
|
||||
if self.shorthand {
|
||||
map.serialize_entry("kind", "init")?;
|
||||
map.serialize_entry("value", &self.value)?;
|
||||
state.serialize_field("kind", "init");
|
||||
state.serialize_field("value", &self.value);
|
||||
} else {
|
||||
map.serialize_entry("value", &self.value)?;
|
||||
map.serialize_entry("kind", "init")?;
|
||||
state.serialize_field("value", &self.value);
|
||||
state.serialize_field("kind", "init");
|
||||
}
|
||||
map.end()
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,12 +308,12 @@ impl Serialize for BindingProperty<'_> {
|
||||
#[estree(ts_type = "FunctionBody | Expression")]
|
||||
pub struct ArrowFunctionExpressionBody<'a>(pub &'a ArrowFunctionExpression<'a>);
|
||||
|
||||
impl Serialize for ArrowFunctionExpressionBody<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
impl ESTree for ArrowFunctionExpressionBody<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
if let Some(expression) = self.0.get_expression() {
|
||||
expression.serialize(serializer)
|
||||
expression.serialize(serializer);
|
||||
} else {
|
||||
self.0.body.serialize(serializer)
|
||||
self.0.body.serialize(serializer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -378,18 +326,18 @@ pub struct AssignmentTargetPropertyIdentifierValue<'a>(
|
||||
pub &'a AssignmentTargetPropertyIdentifier<'a>,
|
||||
);
|
||||
|
||||
impl Serialize for AssignmentTargetPropertyIdentifierValue<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
impl ESTree for AssignmentTargetPropertyIdentifierValue<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
if let Some(init) = &self.0.init {
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
map.serialize_entry("type", "AssignmentPattern")?;
|
||||
map.serialize_entry("start", &self.0.span.start)?;
|
||||
map.serialize_entry("end", &self.0.span.end)?;
|
||||
map.serialize_entry("left", &self.0.binding)?;
|
||||
map.serialize_entry("right", init)?;
|
||||
map.end()
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "AssignmentPattern");
|
||||
state.serialize_field("start", &self.0.span.start);
|
||||
state.serialize_field("end", &self.0.span.end);
|
||||
state.serialize_field("left", &self.0.binding);
|
||||
state.serialize_field("right", init);
|
||||
state.end();
|
||||
} else {
|
||||
self.0.binding.serialize(serializer)
|
||||
self.0.binding.serialize(serializer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -402,12 +350,12 @@ impl Serialize for AssignmentTargetPropertyIdentifierValue<'_> {
|
||||
#[estree(ts_type = "Expression | null")]
|
||||
pub struct ImportExpressionArguments<'a>(pub &'a ImportExpression<'a>);
|
||||
|
||||
impl Serialize for ImportExpressionArguments<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
impl ESTree for ImportExpressionArguments<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
if let Some(expression) = self.0.arguments.first() {
|
||||
expression.serialize(serializer)
|
||||
expression.serialize(serializer);
|
||||
} else {
|
||||
().serialize(serializer)
|
||||
().serialize(serializer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -424,12 +372,12 @@ impl Serialize for ImportExpressionArguments<'_> {
|
||||
#[estree(ts_type = "Array<ImportAttribute>")]
|
||||
pub struct ImportDeclarationWithClause<'a, 'b>(pub &'b ImportDeclaration<'a>);
|
||||
|
||||
impl Serialize for ImportDeclarationWithClause<'_, '_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
impl ESTree for ImportDeclarationWithClause<'_, '_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
if let Some(with_clause) = &self.0.with_clause {
|
||||
with_clause.with_entries.serialize(serializer)
|
||||
with_clause.with_entries.serialize(serializer);
|
||||
} else {
|
||||
[(); 0].serialize(serializer)
|
||||
[(); 0].serialize(serializer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -438,12 +386,12 @@ impl Serialize for ImportDeclarationWithClause<'_, '_> {
|
||||
#[estree(ts_type = "Array<ImportAttribute>")]
|
||||
pub struct ExportNamedDeclarationWithClause<'a, 'b>(pub &'b ExportNamedDeclaration<'a>);
|
||||
|
||||
impl Serialize for ExportNamedDeclarationWithClause<'_, '_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
impl ESTree for ExportNamedDeclarationWithClause<'_, '_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
if let Some(with_clause) = &self.0.with_clause {
|
||||
with_clause.with_entries.serialize(serializer)
|
||||
with_clause.with_entries.serialize(serializer);
|
||||
} else {
|
||||
[(); 0].serialize(serializer)
|
||||
[(); 0].serialize(serializer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -452,12 +400,12 @@ impl Serialize for ExportNamedDeclarationWithClause<'_, '_> {
|
||||
#[estree(ts_type = "Array<ImportAttribute>")]
|
||||
pub struct ExportAllDeclarationWithClause<'a, 'b>(pub &'b ExportAllDeclaration<'a>);
|
||||
|
||||
impl Serialize for ExportAllDeclarationWithClause<'_, '_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
impl ESTree for ExportAllDeclarationWithClause<'_, '_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
if let Some(with_clause) = &self.0.with_clause {
|
||||
with_clause.with_entries.serialize(serializer)
|
||||
with_clause.with_entries.serialize(serializer);
|
||||
} else {
|
||||
[(); 0].serialize(serializer)
|
||||
[(); 0].serialize(serializer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -466,31 +414,31 @@ impl Serialize for ExportAllDeclarationWithClause<'_, '_> {
|
||||
// JSX
|
||||
// --------------------
|
||||
|
||||
impl Serialize for JSXElementName<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
impl ESTree for JSXElementName<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
match self {
|
||||
Self::Identifier(ident) => ident.serialize(serializer),
|
||||
Self::IdentifierReference(ident) => {
|
||||
JSXIdentifier { span: ident.span, name: ident.name }.serialize(serializer)
|
||||
JSXIdentifier { span: ident.span, name: ident.name }.serialize(serializer);
|
||||
}
|
||||
Self::NamespacedName(name) => name.serialize(serializer),
|
||||
Self::MemberExpression(expr) => expr.serialize(serializer),
|
||||
Self::ThisExpression(expr) => {
|
||||
JSXIdentifier { span: expr.span, name: "this".into() }.serialize(serializer)
|
||||
JSXIdentifier { span: expr.span, name: "this".into() }.serialize(serializer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for JSXMemberExpressionObject<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
impl ESTree for JSXMemberExpressionObject<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
match self {
|
||||
Self::IdentifierReference(ident) => {
|
||||
JSXIdentifier { span: ident.span, name: ident.name }.serialize(serializer)
|
||||
JSXIdentifier { span: ident.span, name: ident.name }.serialize(serializer);
|
||||
}
|
||||
Self::MemberExpression(expr) => expr.serialize(serializer),
|
||||
Self::ThisExpression(expr) => {
|
||||
JSXIdentifier { span: expr.span, name: "this".into() }.serialize(serializer)
|
||||
JSXIdentifier { span: expr.span, name: "this".into() }.serialize(serializer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,12 +12,16 @@ repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
description.workspace = true
|
||||
|
||||
[dependencies]
|
||||
serde = { workspace = true, optional = true }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
itoa = { workspace = true }
|
||||
ryu-js = { workspace = true, optional = true }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
serialize = ["dep:serde"]
|
||||
serialize = ["dep:ryu-js"]
|
||||
@@ -1,6 +1,12 @@
|
||||
#[cfg(feature = "serialize")]
|
||||
pub mod ser;
|
||||
#[cfg(feature = "serialize")]
|
||||
mod serialize;
|
||||
#[cfg(feature = "serialize")]
|
||||
pub use serialize::*;
|
||||
|
||||
/// Empty trait that will be used later for custom serialization and TypeScript
|
||||
/// generation for AST nodes.
|
||||
/// Placeholder for real `ESTree` trait when `serialize` feature not enabled.
|
||||
///
|
||||
/// Provided to support `#[generate_derive(ESTree)]`, without enabling the feature.
|
||||
#[cfg(not(feature = "serialize"))]
|
||||
pub trait ESTree {}
|
||||
@@ -1,4 +1,4 @@
|
||||
use serde::ser::{Serialize, SerializeSeq, Serializer};
|
||||
use crate::{ESTree, SequenceSerializer, Serializer};
|
||||
|
||||
/// A helper struct for serializing a sequence followed by an optional element.
|
||||
/// This is only used by generated ESTree serialization code.
|
||||
@@ -7,17 +7,17 @@ pub struct AppendTo<'a, TVec, TAfter> {
|
||||
pub after: &'a Option<TAfter>,
|
||||
}
|
||||
|
||||
impl<TVec: Serialize, TAfter: Serialize> Serialize for AppendTo<'_, TVec, TAfter> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
impl<TVec: ESTree, TAfter: ESTree> ESTree for AppendTo<'_, TVec, TAfter> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
if let Some(after) = self.after {
|
||||
let mut seq = serializer.serialize_seq(Some(self.array.len() + 1))?;
|
||||
let mut seq = serializer.serialize_sequence();
|
||||
for element in self.array {
|
||||
seq.serialize_element(element)?;
|
||||
seq.serialize_element(element);
|
||||
}
|
||||
seq.serialize_element(after)?;
|
||||
seq.end()
|
||||
seq.serialize_element(after);
|
||||
seq.end();
|
||||
} else {
|
||||
self.array.serialize(serializer)
|
||||
self.array.serialize(serializer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,15 +27,15 @@ pub struct AppendToConcat<'a, TVec, TAfter> {
|
||||
pub after: &'a [TAfter],
|
||||
}
|
||||
|
||||
impl<TVec: Serialize, TAfter: Serialize> Serialize for AppendToConcat<'_, TVec, TAfter> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let mut seq = serializer.serialize_seq(Some(self.array.len() + self.after.len()))?;
|
||||
impl<TVec: ESTree, TAfter: ESTree> ESTree for AppendToConcat<'_, TVec, TAfter> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut seq = serializer.serialize_sequence();
|
||||
for element in self.array {
|
||||
seq.serialize_element(element)?;
|
||||
seq.serialize_element(element);
|
||||
}
|
||||
for element in self.after {
|
||||
seq.serialize_element(element)?;
|
||||
seq.serialize_element(element);
|
||||
}
|
||||
seq.end()
|
||||
seq.end();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use super::{ESTree, Serializer};
|
||||
|
||||
/// [`ESTree`] implementation for a `&` reference to any type that implements `ESTree`.
|
||||
impl<T> ESTree for &T
|
||||
where
|
||||
T: ESTree + ?Sized,
|
||||
{
|
||||
#[inline(always)]
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
(**self).serialize(serializer);
|
||||
}
|
||||
}
|
||||
|
||||
/// [`ESTree`] implementation for a `&mut` reference to any type that implements `ESTree`.
|
||||
impl<T> ESTree for &mut T
|
||||
where
|
||||
T: ESTree + ?Sized,
|
||||
{
|
||||
#[inline(always)]
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
(**self).serialize(serializer);
|
||||
}
|
||||
}
|
||||
|
||||
/// [`ESTree`] implementation for `Option<T>`.
|
||||
impl<T: ESTree> ESTree for Option<T> {
|
||||
fn serialize<S: Serializer>(&self, mut serializer: S) {
|
||||
if let Some(value) = self {
|
||||
value.serialize(serializer);
|
||||
} else {
|
||||
serializer.buffer_mut().push_str("null");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// [`ESTree`] implementation for a `Cow` wrapping any type that implements `ESTree`.
|
||||
impl<T> ESTree for Cow<'_, T>
|
||||
where
|
||||
T: ESTree + ToOwned + ?Sized,
|
||||
{
|
||||
#[inline(always)]
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
(**self).serialize(serializer);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::CompactSerializer;
|
||||
use super::*;
|
||||
|
||||
#[expect(clippy::needless_borrow)]
|
||||
#[test]
|
||||
fn serialize_ref() {
|
||||
let cases = [(&"foo", r#""foo""#), (&&"bar", r#""bar""#)];
|
||||
|
||||
for (input, output) in cases {
|
||||
let mut serializer = CompactSerializer::new();
|
||||
input.serialize(&mut serializer);
|
||||
let s = serializer.into_string();
|
||||
assert_eq!(&s, output);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_mut_ref() {
|
||||
let cases = [(&mut "foo", r#""foo""#), (&mut &mut "bar", r#""bar""#)];
|
||||
|
||||
for (input, output) in cases {
|
||||
let mut serializer = CompactSerializer::new();
|
||||
input.serialize(&mut serializer);
|
||||
let s = serializer.into_string();
|
||||
assert_eq!(&s, output);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_option() {
|
||||
let cases = [(None, "null"), (Some(123.0f64), "123")];
|
||||
|
||||
for (input, output) in cases {
|
||||
let mut serializer = CompactSerializer::new();
|
||||
input.serialize(&mut serializer);
|
||||
let s = serializer.into_string();
|
||||
assert_eq!(&s, output);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_cow() {
|
||||
let cases =
|
||||
[(Cow::Borrowed("foo"), r#""foo""#), (Cow::Owned("bar".to_string()), r#""bar""#)];
|
||||
|
||||
for (input, output) in cases {
|
||||
let mut serializer = CompactSerializer::new();
|
||||
input.serialize(&mut serializer);
|
||||
let s = serializer.into_string();
|
||||
assert_eq!(&s, output);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/// Buffer to store serialized JSON.
|
||||
pub struct Buffer {
|
||||
bytes: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Buffer {
|
||||
/// Create new [`Buffer`].
|
||||
pub(super) fn new() -> Self {
|
||||
Self { bytes: vec![] }
|
||||
}
|
||||
|
||||
/// Push a single ASCII byte to the buffer.
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if `byte` is not ASCII.
|
||||
#[inline(always)]
|
||||
pub(super) fn push_ascii_byte(&mut self, byte: u8) {
|
||||
assert!(byte.is_ascii());
|
||||
// SAFETY: We just checked this byte is ASCII
|
||||
unsafe { self.push_ascii_byte_unchecked(byte) };
|
||||
}
|
||||
|
||||
/// Push a single ASCII byte to the buffer, without checking that it's ASCII.
|
||||
///
|
||||
/// # SAFETY
|
||||
/// `byte` must be ASCII.
|
||||
#[inline(always)]
|
||||
pub(super) unsafe fn push_ascii_byte_unchecked(&mut self, byte: u8) {
|
||||
self.bytes.push(byte);
|
||||
}
|
||||
|
||||
/// Push a raw string to the buffer.
|
||||
pub(super) fn push_str(&mut self, s: &str) {
|
||||
self.bytes.extend_from_slice(s.as_bytes());
|
||||
}
|
||||
|
||||
/// Push a slice of bytes to the buffer.
|
||||
///
|
||||
/// # SAFETY
|
||||
/// `bytes` must comprise a valid UTF-8 string.
|
||||
pub(super) unsafe fn push_bytes(&mut self, bytes: &[u8]) {
|
||||
self.bytes.extend_from_slice(bytes);
|
||||
}
|
||||
|
||||
/// Consume [`Buffer`] and convert buffer to string.
|
||||
pub(super) fn into_string(self) -> String {
|
||||
// SAFETY: None of `Serializer`'s safe methods allow
|
||||
// adding invalid byte sequences to `self.bytes`
|
||||
unsafe { String::from_utf8_unchecked(self.bytes) }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
use super::Buffer;
|
||||
|
||||
/// Formatter trait.
|
||||
pub trait Formatter {
|
||||
/// Create new [`Formatter`].
|
||||
fn new() -> Self;
|
||||
|
||||
/// Called before the first field of a struct or element of a sequence.
|
||||
/// If the struct/sequence has no fields/elements, this is not called.
|
||||
fn before_first_element(&mut self, buffer: &mut Buffer);
|
||||
|
||||
/// Called before a later field of a struct or element of a sequence
|
||||
/// (i.e. not the first field/element).
|
||||
fn before_later_element(&mut self, buffer: &mut Buffer);
|
||||
|
||||
/// Called after the key of a struct field.
|
||||
fn before_field_value(&mut self, buffer: &mut Buffer);
|
||||
|
||||
/// Called after the last element of a sequence / last element of a struct.
|
||||
/// If the struct/sequence has no fields/elements, this is not called.
|
||||
fn after_last_element(&mut self, buffer: &mut Buffer);
|
||||
}
|
||||
|
||||
/// Compact formatter.
|
||||
///
|
||||
/// All methods are no-ops.
|
||||
/// This formatter does not insert line breaks, indentation, or whitespace.
|
||||
///
|
||||
/// e.g. `{"type":"Program","start":0,"end":0,"body":[]}`
|
||||
pub struct CompactFormatter;
|
||||
|
||||
impl Formatter for CompactFormatter {
|
||||
#[inline(always)]
|
||||
fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn before_first_element(&mut self, _buffer: &mut Buffer) {}
|
||||
|
||||
#[inline(always)]
|
||||
fn before_later_element(&mut self, _buffer: &mut Buffer) {}
|
||||
|
||||
#[inline(always)]
|
||||
fn before_field_value(&mut self, _buffer: &mut Buffer) {}
|
||||
|
||||
#[inline(always)]
|
||||
fn after_last_element(&mut self, _buffer: &mut Buffer) {}
|
||||
}
|
||||
|
||||
/// Pretty-print formatter.
|
||||
///
|
||||
/// Produces pretty-formatted JSON with line breaks, indentation, and whitespace.
|
||||
///
|
||||
/// e.g.:
|
||||
///
|
||||
/// ```json
|
||||
/// {
|
||||
/// "type": "Program",
|
||||
/// "start": 0,
|
||||
/// "end": 0,
|
||||
/// "body": []
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Note that empty structs/sequences do not contain line breaks (`[]` not `[\n ]`, `{}` not `{\n }`)
|
||||
/// because `before_first_element` and `after_last_element` are only called if the struct/sequence
|
||||
/// contains at least 1 element.
|
||||
pub struct PrettyFormatter {
|
||||
indent: usize,
|
||||
}
|
||||
|
||||
impl Formatter for PrettyFormatter {
|
||||
#[inline(always)]
|
||||
fn new() -> Self {
|
||||
Self { indent: 0 }
|
||||
}
|
||||
|
||||
fn before_first_element(&mut self, buffer: &mut Buffer) {
|
||||
self.indent += 1;
|
||||
self.push_new_line_and_indent(buffer);
|
||||
}
|
||||
|
||||
fn before_later_element(&mut self, buffer: &mut Buffer) {
|
||||
self.push_new_line_and_indent(buffer);
|
||||
}
|
||||
|
||||
fn before_field_value(&mut self, buffer: &mut Buffer) {
|
||||
buffer.push_ascii_byte(b' ');
|
||||
}
|
||||
|
||||
fn after_last_element(&mut self, buffer: &mut Buffer) {
|
||||
self.indent -= 1;
|
||||
self.push_new_line_and_indent(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
impl PrettyFormatter {
|
||||
fn push_new_line_and_indent(&self, buffer: &mut Buffer) {
|
||||
buffer.push_ascii_byte(b'\n');
|
||||
// SAFETY: Spaces are ASCII
|
||||
unsafe { buffer.push_bytes(&b" ".repeat(self.indent)) };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
// Methods which are trivial or just delegate to other methods are marked `#[inline(always)]`
|
||||
#![expect(clippy::inline_always)]
|
||||
|
||||
mod blanket;
|
||||
mod buffer;
|
||||
mod formatter;
|
||||
mod primitives;
|
||||
mod sequences;
|
||||
mod strings;
|
||||
mod structs;
|
||||
use buffer::Buffer;
|
||||
use formatter::{CompactFormatter, Formatter, PrettyFormatter};
|
||||
use sequences::ESTreeSequenceSerializer;
|
||||
use structs::ESTreeStructSerializer;
|
||||
|
||||
pub use sequences::SequenceSerializer;
|
||||
pub use structs::{FlatStructSerializer, StructSerializer};
|
||||
|
||||
/// Trait for types which can be serialized to ESTree.
|
||||
pub trait ESTree {
|
||||
fn serialize<S: Serializer>(&self, serializer: S);
|
||||
}
|
||||
|
||||
/// Trait for serializers.
|
||||
//
|
||||
// This trait contains public methods.
|
||||
// Internal methods we don't want to expose outside this crate are in [`SerializerPrivate`] trait.
|
||||
#[expect(private_bounds)]
|
||||
pub trait Serializer: SerializerPrivate {
|
||||
/// Type of struct serializer this serializer uses.
|
||||
type StructSerializer: StructSerializer;
|
||||
/// Type of sequence serializer this serializer uses.
|
||||
type SequenceSerializer: SequenceSerializer;
|
||||
|
||||
/// Serialize struct.
|
||||
fn serialize_struct(self) -> Self::StructSerializer;
|
||||
|
||||
/// Serialize sequence.
|
||||
fn serialize_sequence(self) -> Self::SequenceSerializer;
|
||||
}
|
||||
|
||||
/// Trait containing internal methods of [`Serializer`]s that we don't want to expose outside this crate.
|
||||
trait SerializerPrivate: Sized {
|
||||
/// Formatter type
|
||||
type Formatter: Formatter;
|
||||
|
||||
/// Get mutable reference to buffer.
|
||||
fn buffer_mut(&mut self) -> &mut Buffer;
|
||||
|
||||
/// Get mutable references to buffer and formatter.
|
||||
fn buffer_and_formatter_mut(&mut self) -> (&mut Buffer, &mut Self::Formatter);
|
||||
}
|
||||
|
||||
/// ESTree serializer which produces compact JSON.
|
||||
pub type CompactSerializer = ESTreeSerializer<CompactFormatter>;
|
||||
|
||||
/// ESTree serializer which produces pretty JSON.
|
||||
pub type PrettySerializer = ESTreeSerializer<PrettyFormatter>;
|
||||
|
||||
/// ESTree serializer.
|
||||
pub struct ESTreeSerializer<F: Formatter> {
|
||||
buffer: Buffer,
|
||||
formatter: F,
|
||||
}
|
||||
|
||||
impl<F: Formatter> ESTreeSerializer<F> {
|
||||
/// Create new [`ESTreeSerializer`].
|
||||
pub fn new() -> Self {
|
||||
Self { buffer: Buffer::new(), formatter: F::new() }
|
||||
}
|
||||
|
||||
/// Consume this [`ESTreeSerializer`] and convert buffer to string.
|
||||
pub fn into_string(self) -> String {
|
||||
self.buffer.into_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Formatter> Default for ESTreeSerializer<F> {
|
||||
#[inline(always)]
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s, F: Formatter> Serializer for &'s mut ESTreeSerializer<F> {
|
||||
type StructSerializer = ESTreeStructSerializer<'s, F>;
|
||||
type SequenceSerializer = ESTreeSequenceSerializer<'s, F>;
|
||||
|
||||
/// Serialize struct.
|
||||
#[inline(always)]
|
||||
fn serialize_struct(self) -> ESTreeStructSerializer<'s, F> {
|
||||
ESTreeStructSerializer::new(self)
|
||||
}
|
||||
|
||||
/// Serialize sequence.
|
||||
#[inline(always)]
|
||||
fn serialize_sequence(self) -> ESTreeSequenceSerializer<'s, F> {
|
||||
ESTreeSequenceSerializer::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Formatter> SerializerPrivate for &mut ESTreeSerializer<F> {
|
||||
type Formatter = F;
|
||||
|
||||
/// Get mutable reference to buffer.
|
||||
#[inline(always)]
|
||||
fn buffer_mut(&mut self) -> &mut Buffer {
|
||||
&mut self.buffer
|
||||
}
|
||||
|
||||
/// Get mutable references to buffer and formatter.
|
||||
#[inline(always)]
|
||||
fn buffer_and_formatter_mut(&mut self) -> (&mut Buffer, &mut F) {
|
||||
(&mut self.buffer, &mut self.formatter)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
use itoa::Buffer as ItoaBuffer;
|
||||
use ryu_js::Buffer as RyuBuffer;
|
||||
|
||||
use super::{ESTree, Serializer};
|
||||
|
||||
/// [`ESTree`] implementation for `bool`.
|
||||
impl ESTree for bool {
|
||||
fn serialize<S: Serializer>(&self, mut serializer: S) {
|
||||
serializer.buffer_mut().push_str(if *self { "true" } else { "false" });
|
||||
}
|
||||
}
|
||||
|
||||
/// [`ESTree`] implementations for `f32` and `f64`.
|
||||
macro_rules! impl_float {
|
||||
($ty:ident) => {
|
||||
impl ESTree for $ty {
|
||||
fn serialize<S: Serializer>(&self, mut serializer: S) {
|
||||
if self.is_finite() {
|
||||
let mut buffer = RyuBuffer::new();
|
||||
let s = buffer.format_finite(*self);
|
||||
serializer.buffer_mut().push_str(s);
|
||||
} else if self.is_nan() {
|
||||
// Serialize `NAN` as `null`
|
||||
// TODO: Throw an error? Use a sentinel value?
|
||||
serializer.buffer_mut().push_str("null");
|
||||
} else if *self == $ty::INFINITY {
|
||||
// Serialize `INFINITY` as `1e+400. `JSON.parse` deserializes this as `Infinity`.
|
||||
serializer.buffer_mut().push_str("1e+400");
|
||||
} else {
|
||||
// Serialize `-INFINITY` as `-1e+400`. `JSON.parse` deserializes this as `-Infinity`.
|
||||
serializer.buffer_mut().push_str("-1e+400");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_float!(f32);
|
||||
impl_float!(f64);
|
||||
|
||||
/// [`ESTree`] implementations for integer types.
|
||||
macro_rules! impl_integer {
|
||||
($ty:ident) => {
|
||||
impl ESTree for $ty {
|
||||
fn serialize<S: Serializer>(&self, mut serializer: S) {
|
||||
let mut buffer = ItoaBuffer::new();
|
||||
let s = buffer.format(*self);
|
||||
serializer.buffer_mut().push_str(s);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_integer!(u8);
|
||||
impl_integer!(u16);
|
||||
impl_integer!(u32);
|
||||
impl_integer!(u64);
|
||||
impl_integer!(u128);
|
||||
impl_integer!(usize);
|
||||
impl_integer!(i8);
|
||||
impl_integer!(i16);
|
||||
impl_integer!(i32);
|
||||
impl_integer!(i64);
|
||||
impl_integer!(i128);
|
||||
impl_integer!(isize);
|
||||
|
||||
/// [`ESTree`] implementation for `()`.
|
||||
impl ESTree for () {
|
||||
fn serialize<S: Serializer>(&self, mut serializer: S) {
|
||||
serializer.buffer_mut().push_str("null");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::CompactSerializer;
|
||||
use super::*;
|
||||
|
||||
fn run_test<T: ESTree>(cases: &[(T, &str)]) {
|
||||
for (input, output) in cases {
|
||||
let mut serializer = CompactSerializer::new();
|
||||
input.serialize(&mut serializer);
|
||||
let s = serializer.into_string();
|
||||
assert_eq!(&s, output);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_bool() {
|
||||
run_test(&[(true, "true"), (false, "false")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_f32() {
|
||||
run_test(&[
|
||||
(0.0, "0"),
|
||||
(1.0, "1"),
|
||||
(123_456.0, "123456"),
|
||||
(0.12345, "0.12345"),
|
||||
(123.45, "123.45"),
|
||||
(f32::MIN, "-3.4028235e+38"),
|
||||
(f32::MAX, "3.4028235e+38"),
|
||||
(f32::INFINITY, "1e+400"),
|
||||
(-f32::INFINITY, "-1e+400"),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_f64() {
|
||||
run_test(&[
|
||||
(0.0, "0"),
|
||||
(1.0, "1"),
|
||||
(123_456.0, "123456"),
|
||||
(0.12345, "0.12345"),
|
||||
(123.45, "123.45"),
|
||||
(f64::MIN, "-1.7976931348623157e+308"),
|
||||
(f64::MAX, "1.7976931348623157e+308"),
|
||||
(f64::INFINITY, "1e+400"),
|
||||
(-f64::INFINITY, "-1e+400"),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_u8() {
|
||||
run_test(&[(0, "0"), (1, "1"), (123, "123"), (u8::MAX, "255")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_u16() {
|
||||
run_test(&[(0, "0"), (1, "1"), (12_345, "12345"), (u16::MAX, "65535")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_u32() {
|
||||
run_test(&[(0, "0"), (1, "1"), (123_456, "123456"), (u32::MAX, "4294967295")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_u64() {
|
||||
run_test(&[(0, "0"), (1, "1"), (123_456, "123456"), (u64::MAX, "18446744073709551615")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_u128() {
|
||||
run_test(&[
|
||||
(0, "0"),
|
||||
(1, "1"),
|
||||
(123_456, "123456"),
|
||||
(u128::MAX, "340282366920938463463374607431768211455"),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_usize() {
|
||||
run_test(&[(0, "0"), (1, "1"), (123_456, "123456"), (u32::MAX as usize, "4294967295")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_i8() {
|
||||
run_test(&[
|
||||
(0, "0"),
|
||||
(1, "1"),
|
||||
(-1, "-1"),
|
||||
(123, "123"),
|
||||
(i8::MIN, "-128"),
|
||||
(i8::MAX, "127"),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_i16() {
|
||||
run_test(&[
|
||||
(0, "0"),
|
||||
(1, "1"),
|
||||
(-1, "-1"),
|
||||
(12_345, "12345"),
|
||||
(i16::MIN, "-32768"),
|
||||
(i16::MAX, "32767"),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_i32() {
|
||||
run_test(&[
|
||||
(0, "0"),
|
||||
(1, "1"),
|
||||
(-1, "-1"),
|
||||
(123_456, "123456"),
|
||||
(i32::MIN, "-2147483648"),
|
||||
(i32::MAX, "2147483647"),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_i64() {
|
||||
run_test(&[
|
||||
(0, "0"),
|
||||
(1, "1"),
|
||||
(-1, "-1"),
|
||||
(123_456, "123456"),
|
||||
(i64::MIN, "-9223372036854775808"),
|
||||
(i64::MAX, "9223372036854775807"),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_i128() {
|
||||
run_test(&[
|
||||
(0, "0"),
|
||||
(1, "1"),
|
||||
(-1, "-1"),
|
||||
(123_456, "123456"),
|
||||
(i128::MIN, "-170141183460469231731687303715884105728"),
|
||||
(i128::MAX, "170141183460469231731687303715884105727"),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_isize() {
|
||||
run_test(&[
|
||||
(0, "0"),
|
||||
(1, "1"),
|
||||
(-1, "-1"),
|
||||
(123_456, "123456"),
|
||||
(i32::MIN as isize, "-2147483648"),
|
||||
(i32::MAX as isize, "2147483647"),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_unit() {
|
||||
run_test(&[((), "null")]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
use super::{ESTree, ESTreeSerializer, Formatter, Serializer, SerializerPrivate};
|
||||
|
||||
/// Trait for sequence serializers.
|
||||
pub trait SequenceSerializer {
|
||||
/// Serialize sequence entry.
|
||||
fn serialize_element<T: ESTree + ?Sized>(&mut self, value: &T);
|
||||
|
||||
/// Finish serializing sequence.
|
||||
fn end(self);
|
||||
}
|
||||
|
||||
/// Serializer for sequences.
|
||||
///
|
||||
/// This is returned by `ESTreeSerializer::serialize_sequence`.
|
||||
pub struct ESTreeSequenceSerializer<'s, F: Formatter> {
|
||||
/// Serializer
|
||||
serializer: &'s mut ESTreeSerializer<F>,
|
||||
/// State of sequence.
|
||||
/// Starts as `SequenceState::Empty`, transitions to `SequenceState::HasEntries` on first entry.
|
||||
state: SequenceState,
|
||||
}
|
||||
|
||||
impl<'s, F: Formatter> ESTreeSequenceSerializer<'s, F> {
|
||||
/// Create new [`ESTreeSequenceSerializer`].
|
||||
pub(super) fn new(mut serializer: &'s mut ESTreeSerializer<F>) -> Self {
|
||||
serializer.buffer_mut().push_ascii_byte(b'[');
|
||||
Self { serializer, state: SequenceState::Empty }
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Formatter> SequenceSerializer for ESTreeSequenceSerializer<'_, F> {
|
||||
/// Serialize sequence entry.
|
||||
fn serialize_element<T: ESTree + ?Sized>(&mut self, value: &T) {
|
||||
let (buffer, formatter) = self.serializer.buffer_and_formatter_mut();
|
||||
if self.state == SequenceState::Empty {
|
||||
self.state = SequenceState::HasEntries;
|
||||
formatter.before_first_element(buffer);
|
||||
} else {
|
||||
buffer.push_ascii_byte(b',');
|
||||
formatter.before_later_element(buffer);
|
||||
}
|
||||
|
||||
value.serialize(&mut *self.serializer);
|
||||
}
|
||||
|
||||
/// Finish serializing sequence.
|
||||
fn end(mut self) {
|
||||
let (buffer, formatter) = self.serializer.buffer_and_formatter_mut();
|
||||
if self.state == SequenceState::HasEntries {
|
||||
formatter.after_last_element(buffer);
|
||||
}
|
||||
buffer.push_ascii_byte(b']');
|
||||
}
|
||||
}
|
||||
|
||||
/// State of [`ESTreeSequenceSerializer`].
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
enum SequenceState {
|
||||
Empty,
|
||||
HasEntries,
|
||||
}
|
||||
|
||||
/// [`ESTree`] implementation for slices.
|
||||
impl<T: ESTree> ESTree for &[T] {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut seq = serializer.serialize_sequence();
|
||||
for element in *self {
|
||||
seq.serialize_element(element);
|
||||
}
|
||||
seq.end();
|
||||
}
|
||||
}
|
||||
|
||||
/// [`ESTree`] implementation for arrays.
|
||||
impl<T: ESTree, const N: usize> ESTree for [T; N] {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut seq = serializer.serialize_sequence();
|
||||
for element in self {
|
||||
seq.serialize_element(element);
|
||||
}
|
||||
seq.end();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::{CompactSerializer, PrettySerializer, StructSerializer};
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn serialize_sequence() {
|
||||
struct Foo<'a> {
|
||||
none: &'a [&'a str],
|
||||
one: &'a [&'a str],
|
||||
two: [&'a str; 2],
|
||||
}
|
||||
|
||||
impl ESTree for Foo<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("none", &self.none);
|
||||
state.serialize_field("one", &self.one);
|
||||
state.serialize_field("two", &self.two);
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
|
||||
let foo = Foo { none: &[], one: &["one"], two: ["two one", "two two"] };
|
||||
|
||||
let mut serializer = CompactSerializer::new();
|
||||
foo.serialize(&mut serializer);
|
||||
let s = serializer.into_string();
|
||||
assert_eq!(&s, r#"{"none":[],"one":["one"],"two":["two one","two two"]}"#);
|
||||
|
||||
let mut serializer = PrettySerializer::new();
|
||||
foo.serialize(&mut serializer);
|
||||
let s = serializer.into_string();
|
||||
assert_eq!(
|
||||
&s,
|
||||
r#"{
|
||||
"none": [],
|
||||
"one": [
|
||||
"one"
|
||||
],
|
||||
"two": [
|
||||
"two one",
|
||||
"two two"
|
||||
]
|
||||
}"#
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
use super::{Buffer, ESTree, Serializer};
|
||||
|
||||
/// [`ESTree`] implementation for string slice.
|
||||
impl ESTree for str {
|
||||
fn serialize<S: Serializer>(&self, mut serializer: S) {
|
||||
write_str(self, serializer.buffer_mut());
|
||||
}
|
||||
}
|
||||
|
||||
/// [`ESTree`] implementation for `String`.
|
||||
impl ESTree for String {
|
||||
fn serialize<S: Serializer>(&self, mut serializer: S) {
|
||||
write_str(self.as_str(), serializer.buffer_mut());
|
||||
}
|
||||
}
|
||||
|
||||
/// Escapes
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
enum Escape {
|
||||
__ = 0,
|
||||
BB = b'b', // \x08
|
||||
TT = b't', // \x09
|
||||
NN = b'n', // \x0A
|
||||
FF = b'f', // \x0C
|
||||
RR = b'r', // \x0D
|
||||
QU = b'"', // \x22
|
||||
BS = b'\\', // \x5C
|
||||
UU = b'u', // \x00...\x1F except the ones above
|
||||
}
|
||||
|
||||
/// Lookup table of escape sequences. A value of `b'x'` at index `i` means that byte `i`
|
||||
/// is escaped as "\x" in JSON. A value of 0 means that byte `i` is not escaped.
|
||||
///
|
||||
/// A value of `UU` means that byte is escaped as `\u00xx`, where `xx` is the hex code of the byte.
|
||||
/// e.g. `0x1F` is output as `\u001F`.
|
||||
static ESCAPE: [Escape; 256] = {
|
||||
#[allow(clippy::enum_glob_use, clippy::allow_attributes)]
|
||||
use Escape::*;
|
||||
|
||||
[
|
||||
// 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
UU, UU, UU, UU, UU, UU, UU, UU, BB, TT, NN, UU, FF, RR, UU, UU, // 0
|
||||
UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, // 1
|
||||
__, __, QU, __, __, __, __, __, __, __, __, __, __, __, __, __, // 2
|
||||
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 3
|
||||
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 4
|
||||
__, __, __, __, __, __, __, __, __, __, __, __, BS, __, __, __, // 5
|
||||
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 6
|
||||
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 7
|
||||
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 8
|
||||
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 9
|
||||
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // A
|
||||
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // B
|
||||
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // C
|
||||
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // D
|
||||
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // E
|
||||
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // F
|
||||
]
|
||||
};
|
||||
|
||||
/// Write string to buffer.
|
||||
/// String is wrapped in `"`s, and with any characters which are not valid in JSON escaped.
|
||||
#[inline(always)]
|
||||
fn write_str(s: &str, buffer: &mut Buffer) {
|
||||
buffer.push_ascii_byte(b'"');
|
||||
|
||||
let bytes = s.as_bytes();
|
||||
|
||||
let mut start = 0;
|
||||
for (index, &byte) in bytes.iter().enumerate() {
|
||||
let escape = ESCAPE[byte as usize];
|
||||
if escape == Escape::__ {
|
||||
continue;
|
||||
}
|
||||
|
||||
if start < index {
|
||||
// SAFETY: `bytes` is derived from a `&str`.
|
||||
// `escape` is only non-zero for ASCII bytes.
|
||||
// Therefore current `index` must mark the end of a valid UTF8 character sequence.
|
||||
// `start` is either the start of string, or after an ASCII character,
|
||||
// therefore always the start of a valid UTF8 character sequence.
|
||||
unsafe { buffer.push_bytes(&bytes[start..index]) };
|
||||
}
|
||||
|
||||
write_char_escape(escape, byte, buffer);
|
||||
|
||||
start = index + 1;
|
||||
}
|
||||
|
||||
if start < bytes.len() {
|
||||
// SAFETY: `bytes` is derived from a `&str`.
|
||||
// `start` is either the start of string, or after an ASCII character,
|
||||
// therefore always the start of a valid UTF8 character sequence.
|
||||
unsafe { buffer.push_bytes(&bytes[start..]) };
|
||||
}
|
||||
|
||||
buffer.push_ascii_byte(b'"');
|
||||
}
|
||||
|
||||
fn write_char_escape(escape: Escape, byte: u8, buffer: &mut Buffer) {
|
||||
#[expect(clippy::if_not_else)]
|
||||
if escape != Escape::UU {
|
||||
buffer.push_ascii_byte(b'\\');
|
||||
// SAFETY: All values of `Escape` are ASCII
|
||||
unsafe { buffer.push_ascii_byte_unchecked(escape as u8) };
|
||||
} else {
|
||||
static HEX_DIGITS: [u8; 16] = *b"0123456789abcdef";
|
||||
let bytes = [
|
||||
b'\\',
|
||||
b'u',
|
||||
b'0',
|
||||
b'0',
|
||||
HEX_DIGITS[(byte >> 4) as usize],
|
||||
HEX_DIGITS[(byte & 0xF) as usize],
|
||||
];
|
||||
// SAFETY: `bytes` contains only ASCII bytes
|
||||
unsafe { buffer.push_bytes(&bytes) }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::CompactSerializer;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn serialize_string_slice() {
|
||||
let cases = [
|
||||
("", r#""""#),
|
||||
("foobar", r#""foobar""#),
|
||||
("\n", r#""\n""#),
|
||||
("\nfoobar", r#""\nfoobar""#),
|
||||
("foo\nbar", r#""foo\nbar""#),
|
||||
("foobar\n", r#""foobar\n""#),
|
||||
(
|
||||
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
|
||||
r#""\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f""#,
|
||||
),
|
||||
(
|
||||
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F",
|
||||
r#""\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f""#,
|
||||
),
|
||||
(
|
||||
r#"They call me "Bob" but I prefer "Dennis", innit?"#,
|
||||
r#""They call me \"Bob\" but I prefer \"Dennis\", innit?""#,
|
||||
),
|
||||
];
|
||||
|
||||
for (input, output) in cases {
|
||||
let mut serializer = CompactSerializer::new();
|
||||
input.serialize(&mut serializer);
|
||||
let s = serializer.into_string();
|
||||
assert_eq!(&s, output);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_string() {
|
||||
let cases = [(String::new(), r#""""#), ("foobar".to_string(), r#""foobar""#)];
|
||||
|
||||
for (input, output) in cases {
|
||||
let mut serializer = CompactSerializer::new();
|
||||
input.serialize(&mut serializer);
|
||||
let s = serializer.into_string();
|
||||
assert_eq!(&s, output);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,342 @@
|
||||
use super::{
|
||||
Buffer, ESTree, ESTreeSequenceSerializer, ESTreeSerializer, Formatter, Serializer,
|
||||
SerializerPrivate,
|
||||
};
|
||||
|
||||
/// Trait for struct serializers.
|
||||
pub trait StructSerializer {
|
||||
type Formatter: Formatter;
|
||||
|
||||
/// Serialize struct field.
|
||||
///
|
||||
/// `key` must not contain any characters which require escaping in JSON.
|
||||
fn serialize_field<T: ESTree + ?Sized>(&mut self, key: &str, value: &T);
|
||||
|
||||
/// Finish serializing struct.
|
||||
fn end(self);
|
||||
}
|
||||
|
||||
/// Serializer for structs.
|
||||
///
|
||||
/// This is returned by `ESTreeSerializer::serialize_struct`.
|
||||
pub struct ESTreeStructSerializer<'s, F: Formatter> {
|
||||
/// Serializer
|
||||
serializer: &'s mut ESTreeSerializer<F>,
|
||||
/// State of struct.
|
||||
/// Starts as `StructState::Empty`, transitions to `StructState::HasFields` on first field.
|
||||
state: StructState,
|
||||
}
|
||||
|
||||
impl<'s, F: Formatter> ESTreeStructSerializer<'s, F> {
|
||||
/// Create new [`ESTreeStructSerializer`].
|
||||
pub(super) fn new(mut serializer: &'s mut ESTreeSerializer<F>) -> Self {
|
||||
serializer.buffer_mut().push_ascii_byte(b'{');
|
||||
Self { serializer, state: StructState::Empty }
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Formatter> StructSerializer for ESTreeStructSerializer<'_, F> {
|
||||
type Formatter = F;
|
||||
|
||||
/// Serialize struct field.
|
||||
///
|
||||
/// `key` must not contain any characters which require escaping in JSON.
|
||||
fn serialize_field<T: ESTree + ?Sized>(&mut self, key: &str, value: &T) {
|
||||
let (buffer, formatter) = self.serializer.buffer_and_formatter_mut();
|
||||
if self.state == StructState::Empty {
|
||||
self.state = StructState::HasFields;
|
||||
formatter.before_first_element(buffer);
|
||||
} else {
|
||||
buffer.push_ascii_byte(b',');
|
||||
formatter.before_later_element(buffer);
|
||||
}
|
||||
|
||||
buffer.push_ascii_byte(b'"');
|
||||
buffer.push_str(key);
|
||||
buffer.push_str("\":");
|
||||
formatter.before_field_value(buffer);
|
||||
value.serialize(&mut *self.serializer);
|
||||
}
|
||||
|
||||
/// Finish serializing struct.
|
||||
fn end(self) {
|
||||
let mut serializer = self.serializer;
|
||||
let (buffer, formatter) = serializer.buffer_and_formatter_mut();
|
||||
if self.state == StructState::HasFields {
|
||||
formatter.after_last_element(buffer);
|
||||
}
|
||||
buffer.push_ascii_byte(b'}');
|
||||
}
|
||||
}
|
||||
|
||||
/// State of [`StructSerializer`].
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub(super) enum StructState {
|
||||
Empty,
|
||||
HasFields,
|
||||
}
|
||||
|
||||
/// Flattening struct serializer.
|
||||
///
|
||||
/// The only method it's legal to call on a `FlatStructSerializer` is `serialize_struct`.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// struct SomeNode {
|
||||
/// // We want to flatten `Span` as if its fields were on `SomeNode`
|
||||
/// span: Span,
|
||||
/// whatever: u32,
|
||||
/// }
|
||||
///
|
||||
/// struct Span {
|
||||
/// start: u32,
|
||||
/// end: u32,
|
||||
/// }
|
||||
///
|
||||
/// impl ESTree for SomeNode {
|
||||
/// fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
/// let mut state = serializer.serialize_struct();
|
||||
/// self.inner.serialize(FlatStructSerializer(&mut state));
|
||||
/// state.serialize_field("whatever", &self.whatever);
|
||||
/// state.end();
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl ESTree for Span {
|
||||
/// fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
/// let mut state = serializer.serialize_struct();
|
||||
/// state.serialize_field("start", &self.start);
|
||||
/// state.serialize_field("end", &self.end);
|
||||
/// state.end();
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub struct FlatStructSerializer<'p, P: StructSerializer>(pub &'p mut P);
|
||||
|
||||
impl<'p, P: StructSerializer> Serializer for FlatStructSerializer<'p, P> {
|
||||
type StructSerializer = Self;
|
||||
type SequenceSerializer = ESTreeSequenceSerializer<'p, P::Formatter>;
|
||||
|
||||
/// Serialize struct.
|
||||
fn serialize_struct(self) -> Self {
|
||||
self
|
||||
}
|
||||
|
||||
fn serialize_sequence(self) -> ESTreeSequenceSerializer<'p, P::Formatter> {
|
||||
const {
|
||||
panic!("Cannot flatten a sequence into a struct");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: StructSerializer> SerializerPrivate for FlatStructSerializer<'_, P> {
|
||||
type Formatter = P::Formatter;
|
||||
|
||||
fn buffer_mut(&mut self) -> &mut Buffer {
|
||||
const {
|
||||
panic!("Cannot flatten anything but a struct into another struct");
|
||||
}
|
||||
}
|
||||
|
||||
fn buffer_and_formatter_mut(&mut self) -> (&mut Buffer, &mut P::Formatter) {
|
||||
const {
|
||||
panic!("Cannot flatten anything but a struct into another struct");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: StructSerializer> StructSerializer for FlatStructSerializer<'_, P> {
|
||||
type Formatter = P::Formatter;
|
||||
|
||||
/// Serialize struct field.
|
||||
///
|
||||
/// `key` must not contain any characters which require escaping in JSON.
|
||||
#[inline(always)]
|
||||
fn serialize_field<T: ESTree + ?Sized>(&mut self, key: &str, value: &T) {
|
||||
// Delegate to parent `StructSerializer`
|
||||
self.0.serialize_field(key, value);
|
||||
}
|
||||
|
||||
/// Finish serializing struct.
|
||||
fn end(self) {
|
||||
// No-op - there may be more fields to be added to the struct in the parent
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::{CompactSerializer, FlatStructSerializer, PrettySerializer, Serializer};
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn serialize_struct() {
|
||||
struct Foo<'a> {
|
||||
n: f64,
|
||||
u: u32,
|
||||
bar: Bar<'a>,
|
||||
empty: Null,
|
||||
hello: Option<&'a str>,
|
||||
maybe_bar: Option<Bar<'a>>,
|
||||
maybe_not_bar: Option<Bar<'a>>,
|
||||
}
|
||||
|
||||
struct Bar<'a> {
|
||||
yes: &'a str,
|
||||
no: &'a str,
|
||||
}
|
||||
|
||||
struct Null;
|
||||
|
||||
impl ESTree for Foo<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("n", &self.n);
|
||||
state.serialize_field("u", &self.u);
|
||||
state.serialize_field("bar", &self.bar);
|
||||
state.serialize_field("empty", &self.empty);
|
||||
state.serialize_field("hello", &self.hello);
|
||||
state.serialize_field("maybe_bar", &self.maybe_bar);
|
||||
state.serialize_field("maybe_not_bar", &self.maybe_not_bar);
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
|
||||
impl ESTree for Bar<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("yes", &self.yes);
|
||||
state.serialize_field("no", &self.no);
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
|
||||
impl ESTree for Null {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let state = serializer.serialize_struct();
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
|
||||
let foo = Foo {
|
||||
n: 123.0,
|
||||
u: 12345,
|
||||
bar: Bar { yes: "yup", no: "nope" },
|
||||
empty: Null,
|
||||
hello: Some("hi!"),
|
||||
maybe_bar: Some(Bar { yes: "hell yeah!", no: "not a chance in a million, mate" }),
|
||||
maybe_not_bar: None,
|
||||
};
|
||||
|
||||
let mut serializer = CompactSerializer::new();
|
||||
foo.serialize(&mut serializer);
|
||||
let s = serializer.into_string();
|
||||
assert_eq!(
|
||||
&s,
|
||||
r#"{"n":123,"u":12345,"bar":{"yes":"yup","no":"nope"},"empty":{},"hello":"hi!","maybe_bar":{"yes":"hell yeah!","no":"not a chance in a million, mate"},"maybe_not_bar":null}"#
|
||||
);
|
||||
|
||||
let mut serializer = PrettySerializer::new();
|
||||
foo.serialize(&mut serializer);
|
||||
let s = serializer.into_string();
|
||||
assert_eq!(
|
||||
&s,
|
||||
r#"{
|
||||
"n": 123,
|
||||
"u": 12345,
|
||||
"bar": {
|
||||
"yes": "yup",
|
||||
"no": "nope"
|
||||
},
|
||||
"empty": {},
|
||||
"hello": "hi!",
|
||||
"maybe_bar": {
|
||||
"yes": "hell yeah!",
|
||||
"no": "not a chance in a million, mate"
|
||||
},
|
||||
"maybe_not_bar": null
|
||||
}"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_flattened_struct() {
|
||||
struct Outer {
|
||||
outer1: &'static str,
|
||||
inner: Inner,
|
||||
outer2: &'static str,
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
inner1: &'static str,
|
||||
innermost: Innermost,
|
||||
inner2: &'static str,
|
||||
}
|
||||
|
||||
struct Innermost {
|
||||
innermost1: &'static str,
|
||||
innermost2: &'static str,
|
||||
}
|
||||
|
||||
impl ESTree for Outer {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("outer1", &self.outer1);
|
||||
self.inner.serialize(FlatStructSerializer(&mut state));
|
||||
state.serialize_field("outer2", &self.outer2);
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
|
||||
impl ESTree for Inner {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("inner1", &self.inner1);
|
||||
self.innermost.serialize(FlatStructSerializer(&mut state));
|
||||
state.serialize_field("inner2", &self.inner2);
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
|
||||
impl ESTree for Innermost {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("innermost1", &self.innermost1);
|
||||
state.serialize_field("innermost2", &self.innermost2);
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
|
||||
let outer = Outer {
|
||||
outer1: "out1",
|
||||
inner: Inner {
|
||||
inner1: "in1",
|
||||
innermost: Innermost { innermost1: "inin1", innermost2: "inin2" },
|
||||
inner2: "in2",
|
||||
},
|
||||
outer2: "out2",
|
||||
};
|
||||
|
||||
let mut serializer = CompactSerializer::new();
|
||||
outer.serialize(&mut serializer);
|
||||
let s = serializer.into_string();
|
||||
assert_eq!(
|
||||
&s,
|
||||
r#"{"outer1":"out1","inner1":"in1","innermost1":"inin1","innermost2":"inin2","inner2":"in2","outer2":"out2"}"#
|
||||
);
|
||||
|
||||
let mut serializer = PrettySerializer::new();
|
||||
outer.serialize(&mut serializer);
|
||||
let s = serializer.into_string();
|
||||
assert_eq!(
|
||||
&s,
|
||||
r#"{
|
||||
"outer1": "out1",
|
||||
"inner1": "in1",
|
||||
"innermost1": "inin1",
|
||||
"innermost2": "inin2",
|
||||
"inner2": "in2",
|
||||
"outer2": "out2"
|
||||
}"#
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,6 @@ memchr = { workspace = true }
|
||||
[dev-dependencies]
|
||||
oxc_ast = { workspace = true, features = ["serialize"] }
|
||||
pico-args = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
|
||||
[features]
|
||||
# Expose Lexer for benchmarks
|
||||
|
||||
@@ -43,7 +43,7 @@ fn main() -> Result<(), String> {
|
||||
if show_estree {
|
||||
Utf8ToUtf16::new().convert(&mut program);
|
||||
}
|
||||
println!("{}", serde_json::to_string_pretty(&program).unwrap());
|
||||
println!("{}", program.to_pretty_json());
|
||||
}
|
||||
|
||||
if ret.errors.is_empty() {
|
||||
|
||||
@@ -30,12 +30,9 @@ phf = { workspace = true, features = ["macros"] }
|
||||
rustc-hash = { workspace = true }
|
||||
unicode-id-start = { workspace = true }
|
||||
|
||||
serde = { workspace = true, optional = true }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
serialize = [
|
||||
"dep:serde",
|
||||
"oxc_allocator/serialize",
|
||||
"oxc_span/serialize",
|
||||
"oxc_estree/serialize",
|
||||
|
||||
@@ -1,49 +1,50 @@
|
||||
// Auto-generated code, DO NOT EDIT DIRECTLY!
|
||||
// To edit this generated file you have to edit `tasks/ast_tools/src/derives/estree.rs`
|
||||
|
||||
#![allow(unused_imports, clippy::match_same_arms)]
|
||||
#![allow(unused_imports, clippy::match_same_arms, clippy::semicolon_if_nothing_returned)]
|
||||
|
||||
use serde::{__private::ser::FlatMapSerializer, ser::SerializeMap, Serialize, Serializer};
|
||||
|
||||
use oxc_estree::ser::{AppendTo, AppendToConcat};
|
||||
use oxc_estree::{
|
||||
ser::{AppendTo, AppendToConcat},
|
||||
ESTree, FlatStructSerializer, Serializer, StructSerializer,
|
||||
};
|
||||
|
||||
use crate::ast::*;
|
||||
|
||||
impl Serialize for Pattern<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
map.serialize_entry("type", "Pattern")?;
|
||||
map.serialize_entry("start", &self.span.start)?;
|
||||
map.serialize_entry("end", &self.span.end)?;
|
||||
map.serialize_entry("body", &self.body)?;
|
||||
map.end()
|
||||
impl ESTree for Pattern<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "Pattern");
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("body", &self.body);
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Disjunction<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
map.serialize_entry("type", "Disjunction")?;
|
||||
map.serialize_entry("start", &self.span.start)?;
|
||||
map.serialize_entry("end", &self.span.end)?;
|
||||
map.serialize_entry("body", &self.body)?;
|
||||
map.end()
|
||||
impl ESTree for Disjunction<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "Disjunction");
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("body", &self.body);
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Alternative<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
map.serialize_entry("type", "Alternative")?;
|
||||
map.serialize_entry("start", &self.span.start)?;
|
||||
map.serialize_entry("end", &self.span.end)?;
|
||||
map.serialize_entry("body", &self.body)?;
|
||||
map.end()
|
||||
impl ESTree for Alternative<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "Alternative");
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("body", &self.body);
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Term<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
impl ESTree for Term<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
match self {
|
||||
Term::BoundaryAssertion(it) => it.serialize(serializer),
|
||||
Term::LookAroundAssertion(it) => it.serialize(serializer),
|
||||
@@ -61,222 +62,170 @@ impl Serialize for Term<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for BoundaryAssertion {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
map.serialize_entry("type", "BoundaryAssertion")?;
|
||||
map.serialize_entry("start", &self.span.start)?;
|
||||
map.serialize_entry("end", &self.span.end)?;
|
||||
map.serialize_entry("kind", &self.kind)?;
|
||||
map.end()
|
||||
impl ESTree for BoundaryAssertion {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "BoundaryAssertion");
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("kind", &self.kind);
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for BoundaryAssertionKind {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
impl ESTree for BoundaryAssertionKind {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
match self {
|
||||
BoundaryAssertionKind::Start => {
|
||||
serializer.serialize_unit_variant("BoundaryAssertionKind", 0, "start")
|
||||
}
|
||||
BoundaryAssertionKind::End => {
|
||||
serializer.serialize_unit_variant("BoundaryAssertionKind", 1, "end")
|
||||
}
|
||||
BoundaryAssertionKind::Boundary => {
|
||||
serializer.serialize_unit_variant("BoundaryAssertionKind", 2, "boundary")
|
||||
}
|
||||
BoundaryAssertionKind::NegativeBoundary => {
|
||||
serializer.serialize_unit_variant("BoundaryAssertionKind", 3, "negativeBoundary")
|
||||
BoundaryAssertionKind::Start => "start".serialize(serializer),
|
||||
BoundaryAssertionKind::End => "end".serialize(serializer),
|
||||
BoundaryAssertionKind::Boundary => "boundary".serialize(serializer),
|
||||
BoundaryAssertionKind::NegativeBoundary => "negativeBoundary".serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ESTree for LookAroundAssertion<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "LookAroundAssertion");
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("kind", &self.kind);
|
||||
state.serialize_field("body", &self.body);
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
|
||||
impl ESTree for LookAroundAssertionKind {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
match self {
|
||||
LookAroundAssertionKind::Lookahead => "lookahead".serialize(serializer),
|
||||
LookAroundAssertionKind::NegativeLookahead => "negativeLookahead".serialize(serializer),
|
||||
LookAroundAssertionKind::Lookbehind => "lookbehind".serialize(serializer),
|
||||
LookAroundAssertionKind::NegativeLookbehind => {
|
||||
"negativeLookbehind".serialize(serializer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for LookAroundAssertion<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
map.serialize_entry("type", "LookAroundAssertion")?;
|
||||
map.serialize_entry("start", &self.span.start)?;
|
||||
map.serialize_entry("end", &self.span.end)?;
|
||||
map.serialize_entry("kind", &self.kind)?;
|
||||
map.serialize_entry("body", &self.body)?;
|
||||
map.end()
|
||||
impl ESTree for Quantifier<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "Quantifier");
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("min", &self.min);
|
||||
state.serialize_field("max", &self.max);
|
||||
state.serialize_field("greedy", &self.greedy);
|
||||
state.serialize_field("body", &self.body);
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for LookAroundAssertionKind {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
impl ESTree for Character {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "Character");
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("kind", &self.kind);
|
||||
state.serialize_field("value", &self.value);
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
|
||||
impl ESTree for CharacterKind {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
match self {
|
||||
LookAroundAssertionKind::Lookahead => {
|
||||
serializer.serialize_unit_variant("LookAroundAssertionKind", 0, "lookahead")
|
||||
}
|
||||
LookAroundAssertionKind::NegativeLookahead => {
|
||||
serializer.serialize_unit_variant("LookAroundAssertionKind", 1, "negativeLookahead")
|
||||
}
|
||||
LookAroundAssertionKind::Lookbehind => {
|
||||
serializer.serialize_unit_variant("LookAroundAssertionKind", 2, "lookbehind")
|
||||
}
|
||||
LookAroundAssertionKind::NegativeLookbehind => serializer.serialize_unit_variant(
|
||||
"LookAroundAssertionKind",
|
||||
3,
|
||||
"negativeLookbehind",
|
||||
),
|
||||
CharacterKind::ControlLetter => "controlLetter".serialize(serializer),
|
||||
CharacterKind::HexadecimalEscape => "hexadecimalEscape".serialize(serializer),
|
||||
CharacterKind::Identifier => "identifier".serialize(serializer),
|
||||
CharacterKind::Null => "null".serialize(serializer),
|
||||
CharacterKind::Octal1 => "octal1".serialize(serializer),
|
||||
CharacterKind::Octal2 => "octal2".serialize(serializer),
|
||||
CharacterKind::Octal3 => "octal3".serialize(serializer),
|
||||
CharacterKind::SingleEscape => "singleEscape".serialize(serializer),
|
||||
CharacterKind::Symbol => "symbol".serialize(serializer),
|
||||
CharacterKind::UnicodeEscape => "unicodeEscape".serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Quantifier<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
map.serialize_entry("type", "Quantifier")?;
|
||||
map.serialize_entry("start", &self.span.start)?;
|
||||
map.serialize_entry("end", &self.span.end)?;
|
||||
map.serialize_entry("min", &self.min)?;
|
||||
map.serialize_entry("max", &self.max)?;
|
||||
map.serialize_entry("greedy", &self.greedy)?;
|
||||
map.serialize_entry("body", &self.body)?;
|
||||
map.end()
|
||||
impl ESTree for CharacterClassEscape {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "CharacterClassEscape");
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("kind", &self.kind);
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Character {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
map.serialize_entry("type", "Character")?;
|
||||
map.serialize_entry("start", &self.span.start)?;
|
||||
map.serialize_entry("end", &self.span.end)?;
|
||||
map.serialize_entry("kind", &self.kind)?;
|
||||
map.serialize_entry("value", &self.value)?;
|
||||
map.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for CharacterKind {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
impl ESTree for CharacterClassEscapeKind {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
match self {
|
||||
CharacterKind::ControlLetter => {
|
||||
serializer.serialize_unit_variant("CharacterKind", 0, "controlLetter")
|
||||
}
|
||||
CharacterKind::HexadecimalEscape => {
|
||||
serializer.serialize_unit_variant("CharacterKind", 1, "hexadecimalEscape")
|
||||
}
|
||||
CharacterKind::Identifier => {
|
||||
serializer.serialize_unit_variant("CharacterKind", 2, "identifier")
|
||||
}
|
||||
CharacterKind::Null => serializer.serialize_unit_variant("CharacterKind", 3, "null"),
|
||||
CharacterKind::Octal1 => {
|
||||
serializer.serialize_unit_variant("CharacterKind", 4, "octal1")
|
||||
}
|
||||
CharacterKind::Octal2 => {
|
||||
serializer.serialize_unit_variant("CharacterKind", 5, "octal2")
|
||||
}
|
||||
CharacterKind::Octal3 => {
|
||||
serializer.serialize_unit_variant("CharacterKind", 6, "octal3")
|
||||
}
|
||||
CharacterKind::SingleEscape => {
|
||||
serializer.serialize_unit_variant("CharacterKind", 7, "singleEscape")
|
||||
}
|
||||
CharacterKind::Symbol => {
|
||||
serializer.serialize_unit_variant("CharacterKind", 8, "symbol")
|
||||
}
|
||||
CharacterKind::UnicodeEscape => {
|
||||
serializer.serialize_unit_variant("CharacterKind", 9, "unicodeEscape")
|
||||
}
|
||||
CharacterClassEscapeKind::D => "d".serialize(serializer),
|
||||
CharacterClassEscapeKind::NegativeD => "negativeD".serialize(serializer),
|
||||
CharacterClassEscapeKind::S => "s".serialize(serializer),
|
||||
CharacterClassEscapeKind::NegativeS => "negativeS".serialize(serializer),
|
||||
CharacterClassEscapeKind::W => "w".serialize(serializer),
|
||||
CharacterClassEscapeKind::NegativeW => "negativeW".serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for CharacterClassEscape {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
map.serialize_entry("type", "CharacterClassEscape")?;
|
||||
map.serialize_entry("start", &self.span.start)?;
|
||||
map.serialize_entry("end", &self.span.end)?;
|
||||
map.serialize_entry("kind", &self.kind)?;
|
||||
map.end()
|
||||
impl ESTree for UnicodePropertyEscape<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "UnicodePropertyEscape");
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("negative", &self.negative);
|
||||
state.serialize_field("strings", &self.strings);
|
||||
state.serialize_field("name", &self.name);
|
||||
state.serialize_field("value", &self.value);
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for CharacterClassEscapeKind {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
impl ESTree for Dot {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "Dot");
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
|
||||
impl ESTree for CharacterClass<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "CharacterClass");
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("negative", &self.negative);
|
||||
state.serialize_field("strings", &self.strings);
|
||||
state.serialize_field("kind", &self.kind);
|
||||
state.serialize_field("body", &self.body);
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
|
||||
impl ESTree for CharacterClassContentsKind {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
match self {
|
||||
CharacterClassEscapeKind::D => {
|
||||
serializer.serialize_unit_variant("CharacterClassEscapeKind", 0, "d")
|
||||
}
|
||||
CharacterClassEscapeKind::NegativeD => {
|
||||
serializer.serialize_unit_variant("CharacterClassEscapeKind", 1, "negativeD")
|
||||
}
|
||||
CharacterClassEscapeKind::S => {
|
||||
serializer.serialize_unit_variant("CharacterClassEscapeKind", 2, "s")
|
||||
}
|
||||
CharacterClassEscapeKind::NegativeS => {
|
||||
serializer.serialize_unit_variant("CharacterClassEscapeKind", 3, "negativeS")
|
||||
}
|
||||
CharacterClassEscapeKind::W => {
|
||||
serializer.serialize_unit_variant("CharacterClassEscapeKind", 4, "w")
|
||||
}
|
||||
CharacterClassEscapeKind::NegativeW => {
|
||||
serializer.serialize_unit_variant("CharacterClassEscapeKind", 5, "negativeW")
|
||||
}
|
||||
CharacterClassContentsKind::Union => "union".serialize(serializer),
|
||||
CharacterClassContentsKind::Intersection => "intersection".serialize(serializer),
|
||||
CharacterClassContentsKind::Subtraction => "subtraction".serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for UnicodePropertyEscape<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
map.serialize_entry("type", "UnicodePropertyEscape")?;
|
||||
map.serialize_entry("start", &self.span.start)?;
|
||||
map.serialize_entry("end", &self.span.end)?;
|
||||
map.serialize_entry("negative", &self.negative)?;
|
||||
map.serialize_entry("strings", &self.strings)?;
|
||||
map.serialize_entry("name", &self.name)?;
|
||||
map.serialize_entry("value", &self.value)?;
|
||||
map.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Dot {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
map.serialize_entry("type", "Dot")?;
|
||||
map.serialize_entry("start", &self.span.start)?;
|
||||
map.serialize_entry("end", &self.span.end)?;
|
||||
map.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for CharacterClass<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
map.serialize_entry("type", "CharacterClass")?;
|
||||
map.serialize_entry("start", &self.span.start)?;
|
||||
map.serialize_entry("end", &self.span.end)?;
|
||||
map.serialize_entry("negative", &self.negative)?;
|
||||
map.serialize_entry("strings", &self.strings)?;
|
||||
map.serialize_entry("kind", &self.kind)?;
|
||||
map.serialize_entry("body", &self.body)?;
|
||||
map.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for CharacterClassContentsKind {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
match self {
|
||||
CharacterClassContentsKind::Union => {
|
||||
serializer.serialize_unit_variant("CharacterClassContentsKind", 0, "union")
|
||||
}
|
||||
CharacterClassContentsKind::Intersection => {
|
||||
serializer.serialize_unit_variant("CharacterClassContentsKind", 1, "intersection")
|
||||
}
|
||||
CharacterClassContentsKind::Subtraction => {
|
||||
serializer.serialize_unit_variant("CharacterClassContentsKind", 2, "subtraction")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for CharacterClassContents<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
impl ESTree for CharacterClassContents<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
match self {
|
||||
CharacterClassContents::CharacterClassRange(it) => it.serialize(serializer),
|
||||
CharacterClassContents::CharacterClassEscape(it) => it.serialize(serializer),
|
||||
@@ -288,107 +237,107 @@ impl Serialize for CharacterClassContents<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for CharacterClassRange {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
map.serialize_entry("type", "CharacterClassRange")?;
|
||||
map.serialize_entry("start", &self.span.start)?;
|
||||
map.serialize_entry("end", &self.span.end)?;
|
||||
map.serialize_entry("min", &self.min)?;
|
||||
map.serialize_entry("max", &self.max)?;
|
||||
map.end()
|
||||
impl ESTree for CharacterClassRange {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "CharacterClassRange");
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("min", &self.min);
|
||||
state.serialize_field("max", &self.max);
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for ClassStringDisjunction<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
map.serialize_entry("type", "ClassStringDisjunction")?;
|
||||
map.serialize_entry("start", &self.span.start)?;
|
||||
map.serialize_entry("end", &self.span.end)?;
|
||||
map.serialize_entry("strings", &self.strings)?;
|
||||
map.serialize_entry("body", &self.body)?;
|
||||
map.end()
|
||||
impl ESTree for ClassStringDisjunction<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "ClassStringDisjunction");
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("strings", &self.strings);
|
||||
state.serialize_field("body", &self.body);
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for ClassString<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
map.serialize_entry("type", "ClassString")?;
|
||||
map.serialize_entry("start", &self.span.start)?;
|
||||
map.serialize_entry("end", &self.span.end)?;
|
||||
map.serialize_entry("strings", &self.strings)?;
|
||||
map.serialize_entry("body", &self.body)?;
|
||||
map.end()
|
||||
impl ESTree for ClassString<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "ClassString");
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("strings", &self.strings);
|
||||
state.serialize_field("body", &self.body);
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for CapturingGroup<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
map.serialize_entry("type", "CapturingGroup")?;
|
||||
map.serialize_entry("start", &self.span.start)?;
|
||||
map.serialize_entry("end", &self.span.end)?;
|
||||
map.serialize_entry("name", &self.name)?;
|
||||
map.serialize_entry("body", &self.body)?;
|
||||
map.end()
|
||||
impl ESTree for CapturingGroup<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "CapturingGroup");
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("name", &self.name);
|
||||
state.serialize_field("body", &self.body);
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for IgnoreGroup<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
map.serialize_entry("type", "IgnoreGroup")?;
|
||||
map.serialize_entry("start", &self.span.start)?;
|
||||
map.serialize_entry("end", &self.span.end)?;
|
||||
map.serialize_entry("modifiers", &self.modifiers)?;
|
||||
map.serialize_entry("body", &self.body)?;
|
||||
map.end()
|
||||
impl ESTree for IgnoreGroup<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "IgnoreGroup");
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("modifiers", &self.modifiers);
|
||||
state.serialize_field("body", &self.body);
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Modifiers {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
map.serialize_entry("type", "Modifiers")?;
|
||||
map.serialize_entry("start", &self.span.start)?;
|
||||
map.serialize_entry("end", &self.span.end)?;
|
||||
map.serialize_entry("enabling", &self.enabling)?;
|
||||
map.serialize_entry("disabling", &self.disabling)?;
|
||||
map.end()
|
||||
impl ESTree for Modifiers {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "Modifiers");
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("enabling", &self.enabling);
|
||||
state.serialize_field("disabling", &self.disabling);
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Modifier {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
map.serialize_entry("type", "Modifier")?;
|
||||
map.serialize_entry("ignoreCase", &self.ignore_case)?;
|
||||
map.serialize_entry("multiline", &self.multiline)?;
|
||||
map.serialize_entry("sticky", &self.sticky)?;
|
||||
map.end()
|
||||
impl ESTree for Modifier {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "Modifier");
|
||||
state.serialize_field("ignoreCase", &self.ignore_case);
|
||||
state.serialize_field("multiline", &self.multiline);
|
||||
state.serialize_field("sticky", &self.sticky);
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for IndexedReference {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
map.serialize_entry("type", "IndexedReference")?;
|
||||
map.serialize_entry("start", &self.span.start)?;
|
||||
map.serialize_entry("end", &self.span.end)?;
|
||||
map.serialize_entry("index", &self.index)?;
|
||||
map.end()
|
||||
impl ESTree for IndexedReference {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "IndexedReference");
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("index", &self.index);
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for NamedReference<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
map.serialize_entry("type", "NamedReference")?;
|
||||
map.serialize_entry("start", &self.span.start)?;
|
||||
map.serialize_entry("end", &self.span.end)?;
|
||||
map.serialize_entry("name", &self.name)?;
|
||||
map.end()
|
||||
impl ESTree for NamedReference<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "NamedReference");
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("name", &self.name);
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,9 @@ use std::{
|
||||
|
||||
use oxc_allocator::{Allocator, CloneIn, FromIn};
|
||||
#[cfg(feature = "serialize")]
|
||||
use serde::{Serialize, Serializer};
|
||||
use oxc_estree::{ESTree, Serializer as ESTreeSerializer};
|
||||
#[cfg(feature = "serialize")]
|
||||
use serde::{Serialize, Serializer as SerdeSerializer};
|
||||
|
||||
use crate::{CompactStr, ContentEq};
|
||||
|
||||
@@ -208,7 +210,14 @@ impl fmt::Display for Atom<'_> {
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl Serialize for Atom<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
self.0.serialize(serializer)
|
||||
fn serialize<S: SerdeSerializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
Serialize::serialize(self.as_str(), serializer)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl ESTree for Atom<'_> {
|
||||
fn serialize<S: ESTreeSerializer>(&self, serializer: S) {
|
||||
ESTree::serialize(self.as_str(), serializer);
|
||||
}
|
||||
}
|
||||
@@ -1,40 +1,39 @@
|
||||
// Auto-generated code, DO NOT EDIT DIRECTLY!
|
||||
// To edit this generated file you have to edit `tasks/ast_tools/src/derives/estree.rs`
|
||||
|
||||
#![allow(unused_imports, clippy::match_same_arms)]
|
||||
#![allow(unused_imports, clippy::match_same_arms, clippy::semicolon_if_nothing_returned)]
|
||||
|
||||
use serde::{__private::ser::FlatMapSerializer, ser::SerializeMap, Serialize, Serializer};
|
||||
|
||||
use oxc_estree::ser::{AppendTo, AppendToConcat};
|
||||
use oxc_estree::{
|
||||
ser::{AppendTo, AppendToConcat},
|
||||
ESTree, FlatStructSerializer, Serializer, StructSerializer,
|
||||
};
|
||||
|
||||
use crate::source_type::*;
|
||||
use crate::span::*;
|
||||
|
||||
impl Serialize for Span {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
map.serialize_entry("start", &self.start)?;
|
||||
map.serialize_entry("end", &self.end)?;
|
||||
map.end()
|
||||
impl ESTree for Span {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("start", &self.start);
|
||||
state.serialize_field("end", &self.end);
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for SourceType {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
map.serialize_entry("sourceType", &self.module_kind)?;
|
||||
map.end()
|
||||
impl ESTree for SourceType {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("sourceType", &self.module_kind);
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for ModuleKind {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
impl ESTree for ModuleKind {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
match self {
|
||||
ModuleKind::Script => serializer.serialize_unit_variant("ModuleKind", 0, "script"),
|
||||
ModuleKind::Module => serializer.serialize_unit_variant("ModuleKind", 1, "module"),
|
||||
ModuleKind::Unambiguous => {
|
||||
serializer.serialize_unit_variant("ModuleKind", 2, "unambiguous")
|
||||
}
|
||||
ModuleKind::Script => "script".serialize(serializer),
|
||||
ModuleKind::Module => "module".serialize(serializer),
|
||||
ModuleKind::Unambiguous => "unambiguous".serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@ use std::{
|
||||
};
|
||||
|
||||
use miette::{LabeledSpan, SourceOffset, SourceSpan};
|
||||
#[cfg(feature = "serialize")]
|
||||
use serde::{ser::SerializeMap, Serialize, Serializer as SerdeSerializer};
|
||||
|
||||
use oxc_allocator::{Allocator, CloneIn};
|
||||
use oxc_ast_macros::ast;
|
||||
@@ -543,6 +545,16 @@ impl<'a> CloneIn<'a> for Span {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl Serialize for Span {
|
||||
fn serialize<S: SerdeSerializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
map.serialize_entry("start", &self.start)?;
|
||||
map.serialize_entry("end", &self.end)?;
|
||||
map.end()
|
||||
}
|
||||
}
|
||||
|
||||
/// Zero-sized type which has pointer alignment (8 on 64-bit, 4 on 32-bit).
|
||||
#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[repr(transparent)]
|
||||
|
||||
@@ -1,177 +1,96 @@
|
||||
// Auto-generated code, DO NOT EDIT DIRECTLY!
|
||||
// To edit this generated file you have to edit `tasks/ast_tools/src/derives/estree.rs`
|
||||
|
||||
#![allow(unused_imports, clippy::match_same_arms)]
|
||||
#![allow(unused_imports, clippy::match_same_arms, clippy::semicolon_if_nothing_returned)]
|
||||
|
||||
use serde::{__private::ser::FlatMapSerializer, ser::SerializeMap, Serialize, Serializer};
|
||||
|
||||
use oxc_estree::ser::{AppendTo, AppendToConcat};
|
||||
use oxc_estree::{
|
||||
ser::{AppendTo, AppendToConcat},
|
||||
ESTree, FlatStructSerializer, Serializer, StructSerializer,
|
||||
};
|
||||
|
||||
use crate::operator::*;
|
||||
|
||||
impl Serialize for AssignmentOperator {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
impl ESTree for AssignmentOperator {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
match self {
|
||||
AssignmentOperator::Assign => {
|
||||
serializer.serialize_unit_variant("AssignmentOperator", 0, "=")
|
||||
}
|
||||
AssignmentOperator::Addition => {
|
||||
serializer.serialize_unit_variant("AssignmentOperator", 1, "+=")
|
||||
}
|
||||
AssignmentOperator::Subtraction => {
|
||||
serializer.serialize_unit_variant("AssignmentOperator", 2, "-=")
|
||||
}
|
||||
AssignmentOperator::Multiplication => {
|
||||
serializer.serialize_unit_variant("AssignmentOperator", 3, "*=")
|
||||
}
|
||||
AssignmentOperator::Division => {
|
||||
serializer.serialize_unit_variant("AssignmentOperator", 4, "/=")
|
||||
}
|
||||
AssignmentOperator::Remainder => {
|
||||
serializer.serialize_unit_variant("AssignmentOperator", 5, "%=")
|
||||
}
|
||||
AssignmentOperator::Exponential => {
|
||||
serializer.serialize_unit_variant("AssignmentOperator", 6, "**=")
|
||||
}
|
||||
AssignmentOperator::ShiftLeft => {
|
||||
serializer.serialize_unit_variant("AssignmentOperator", 7, "<<=")
|
||||
}
|
||||
AssignmentOperator::ShiftRight => {
|
||||
serializer.serialize_unit_variant("AssignmentOperator", 8, ">>=")
|
||||
}
|
||||
AssignmentOperator::ShiftRightZeroFill => {
|
||||
serializer.serialize_unit_variant("AssignmentOperator", 9, ">>>=")
|
||||
}
|
||||
AssignmentOperator::BitwiseOR => {
|
||||
serializer.serialize_unit_variant("AssignmentOperator", 10, "|=")
|
||||
}
|
||||
AssignmentOperator::BitwiseXOR => {
|
||||
serializer.serialize_unit_variant("AssignmentOperator", 11, "^=")
|
||||
}
|
||||
AssignmentOperator::BitwiseAnd => {
|
||||
serializer.serialize_unit_variant("AssignmentOperator", 12, "&=")
|
||||
}
|
||||
AssignmentOperator::LogicalOr => {
|
||||
serializer.serialize_unit_variant("AssignmentOperator", 13, "||=")
|
||||
}
|
||||
AssignmentOperator::LogicalAnd => {
|
||||
serializer.serialize_unit_variant("AssignmentOperator", 14, "&&=")
|
||||
}
|
||||
AssignmentOperator::LogicalNullish => {
|
||||
serializer.serialize_unit_variant("AssignmentOperator", 15, "??=")
|
||||
}
|
||||
AssignmentOperator::Assign => "=".serialize(serializer),
|
||||
AssignmentOperator::Addition => "+=".serialize(serializer),
|
||||
AssignmentOperator::Subtraction => "-=".serialize(serializer),
|
||||
AssignmentOperator::Multiplication => "*=".serialize(serializer),
|
||||
AssignmentOperator::Division => "/=".serialize(serializer),
|
||||
AssignmentOperator::Remainder => "%=".serialize(serializer),
|
||||
AssignmentOperator::Exponential => "**=".serialize(serializer),
|
||||
AssignmentOperator::ShiftLeft => "<<=".serialize(serializer),
|
||||
AssignmentOperator::ShiftRight => ">>=".serialize(serializer),
|
||||
AssignmentOperator::ShiftRightZeroFill => ">>>=".serialize(serializer),
|
||||
AssignmentOperator::BitwiseOR => "|=".serialize(serializer),
|
||||
AssignmentOperator::BitwiseXOR => "^=".serialize(serializer),
|
||||
AssignmentOperator::BitwiseAnd => "&=".serialize(serializer),
|
||||
AssignmentOperator::LogicalOr => "||=".serialize(serializer),
|
||||
AssignmentOperator::LogicalAnd => "&&=".serialize(serializer),
|
||||
AssignmentOperator::LogicalNullish => "??=".serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for BinaryOperator {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
impl ESTree for BinaryOperator {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
match self {
|
||||
BinaryOperator::Equality => {
|
||||
serializer.serialize_unit_variant("BinaryOperator", 0, "==")
|
||||
}
|
||||
BinaryOperator::Inequality => {
|
||||
serializer.serialize_unit_variant("BinaryOperator", 1, "!=")
|
||||
}
|
||||
BinaryOperator::StrictEquality => {
|
||||
serializer.serialize_unit_variant("BinaryOperator", 2, "===")
|
||||
}
|
||||
BinaryOperator::StrictInequality => {
|
||||
serializer.serialize_unit_variant("BinaryOperator", 3, "!==")
|
||||
}
|
||||
BinaryOperator::LessThan => serializer.serialize_unit_variant("BinaryOperator", 4, "<"),
|
||||
BinaryOperator::LessEqualThan => {
|
||||
serializer.serialize_unit_variant("BinaryOperator", 5, "<=")
|
||||
}
|
||||
BinaryOperator::GreaterThan => {
|
||||
serializer.serialize_unit_variant("BinaryOperator", 6, ">")
|
||||
}
|
||||
BinaryOperator::GreaterEqualThan => {
|
||||
serializer.serialize_unit_variant("BinaryOperator", 7, ">=")
|
||||
}
|
||||
BinaryOperator::Addition => serializer.serialize_unit_variant("BinaryOperator", 8, "+"),
|
||||
BinaryOperator::Subtraction => {
|
||||
serializer.serialize_unit_variant("BinaryOperator", 9, "-")
|
||||
}
|
||||
BinaryOperator::Multiplication => {
|
||||
serializer.serialize_unit_variant("BinaryOperator", 10, "*")
|
||||
}
|
||||
BinaryOperator::Division => {
|
||||
serializer.serialize_unit_variant("BinaryOperator", 11, "/")
|
||||
}
|
||||
BinaryOperator::Remainder => {
|
||||
serializer.serialize_unit_variant("BinaryOperator", 12, "%")
|
||||
}
|
||||
BinaryOperator::Exponential => {
|
||||
serializer.serialize_unit_variant("BinaryOperator", 13, "**")
|
||||
}
|
||||
BinaryOperator::ShiftLeft => {
|
||||
serializer.serialize_unit_variant("BinaryOperator", 14, "<<")
|
||||
}
|
||||
BinaryOperator::ShiftRight => {
|
||||
serializer.serialize_unit_variant("BinaryOperator", 15, ">>")
|
||||
}
|
||||
BinaryOperator::ShiftRightZeroFill => {
|
||||
serializer.serialize_unit_variant("BinaryOperator", 16, ">>>")
|
||||
}
|
||||
BinaryOperator::BitwiseOR => {
|
||||
serializer.serialize_unit_variant("BinaryOperator", 17, "|")
|
||||
}
|
||||
BinaryOperator::BitwiseXOR => {
|
||||
serializer.serialize_unit_variant("BinaryOperator", 18, "^")
|
||||
}
|
||||
BinaryOperator::BitwiseAnd => {
|
||||
serializer.serialize_unit_variant("BinaryOperator", 19, "&")
|
||||
}
|
||||
BinaryOperator::In => serializer.serialize_unit_variant("BinaryOperator", 20, "in"),
|
||||
BinaryOperator::Instanceof => {
|
||||
serializer.serialize_unit_variant("BinaryOperator", 21, "instanceof")
|
||||
}
|
||||
BinaryOperator::Equality => "==".serialize(serializer),
|
||||
BinaryOperator::Inequality => "!=".serialize(serializer),
|
||||
BinaryOperator::StrictEquality => "===".serialize(serializer),
|
||||
BinaryOperator::StrictInequality => "!==".serialize(serializer),
|
||||
BinaryOperator::LessThan => "<".serialize(serializer),
|
||||
BinaryOperator::LessEqualThan => "<=".serialize(serializer),
|
||||
BinaryOperator::GreaterThan => ">".serialize(serializer),
|
||||
BinaryOperator::GreaterEqualThan => ">=".serialize(serializer),
|
||||
BinaryOperator::Addition => "+".serialize(serializer),
|
||||
BinaryOperator::Subtraction => "-".serialize(serializer),
|
||||
BinaryOperator::Multiplication => "*".serialize(serializer),
|
||||
BinaryOperator::Division => "/".serialize(serializer),
|
||||
BinaryOperator::Remainder => "%".serialize(serializer),
|
||||
BinaryOperator::Exponential => "**".serialize(serializer),
|
||||
BinaryOperator::ShiftLeft => "<<".serialize(serializer),
|
||||
BinaryOperator::ShiftRight => ">>".serialize(serializer),
|
||||
BinaryOperator::ShiftRightZeroFill => ">>>".serialize(serializer),
|
||||
BinaryOperator::BitwiseOR => "|".serialize(serializer),
|
||||
BinaryOperator::BitwiseXOR => "^".serialize(serializer),
|
||||
BinaryOperator::BitwiseAnd => "&".serialize(serializer),
|
||||
BinaryOperator::In => "in".serialize(serializer),
|
||||
BinaryOperator::Instanceof => "instanceof".serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for LogicalOperator {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
impl ESTree for LogicalOperator {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
match self {
|
||||
LogicalOperator::Or => serializer.serialize_unit_variant("LogicalOperator", 0, "||"),
|
||||
LogicalOperator::And => serializer.serialize_unit_variant("LogicalOperator", 1, "&&"),
|
||||
LogicalOperator::Coalesce => {
|
||||
serializer.serialize_unit_variant("LogicalOperator", 2, "??")
|
||||
}
|
||||
LogicalOperator::Or => "||".serialize(serializer),
|
||||
LogicalOperator::And => "&&".serialize(serializer),
|
||||
LogicalOperator::Coalesce => "??".serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for UnaryOperator {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
impl ESTree for UnaryOperator {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
match self {
|
||||
UnaryOperator::UnaryPlus => serializer.serialize_unit_variant("UnaryOperator", 0, "+"),
|
||||
UnaryOperator::UnaryNegation => {
|
||||
serializer.serialize_unit_variant("UnaryOperator", 1, "-")
|
||||
}
|
||||
UnaryOperator::LogicalNot => serializer.serialize_unit_variant("UnaryOperator", 2, "!"),
|
||||
UnaryOperator::BitwiseNot => serializer.serialize_unit_variant("UnaryOperator", 3, "~"),
|
||||
UnaryOperator::Typeof => {
|
||||
serializer.serialize_unit_variant("UnaryOperator", 4, "typeof")
|
||||
}
|
||||
UnaryOperator::Void => serializer.serialize_unit_variant("UnaryOperator", 5, "void"),
|
||||
UnaryOperator::Delete => {
|
||||
serializer.serialize_unit_variant("UnaryOperator", 6, "delete")
|
||||
}
|
||||
UnaryOperator::UnaryPlus => "+".serialize(serializer),
|
||||
UnaryOperator::UnaryNegation => "-".serialize(serializer),
|
||||
UnaryOperator::LogicalNot => "!".serialize(serializer),
|
||||
UnaryOperator::BitwiseNot => "~".serialize(serializer),
|
||||
UnaryOperator::Typeof => "typeof".serialize(serializer),
|
||||
UnaryOperator::Void => "void".serialize(serializer),
|
||||
UnaryOperator::Delete => "delete".serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for UpdateOperator {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
impl ESTree for UpdateOperator {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
match self {
|
||||
UpdateOperator::Increment => {
|
||||
serializer.serialize_unit_variant("UpdateOperator", 0, "++")
|
||||
}
|
||||
UpdateOperator::Decrement => {
|
||||
serializer.serialize_unit_variant("UpdateOperator", 1, "--")
|
||||
}
|
||||
UpdateOperator::Increment => "++".serialize(serializer),
|
||||
UpdateOperator::Decrement => "--".serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,5 +28,6 @@ oxc_prettier = { workspace = true }
|
||||
console_error_panic_hook = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde-wasm-bindgen = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
tsify = { workspace = true }
|
||||
wasm-bindgen = { workspace = true }
|
||||
@@ -466,8 +466,22 @@ impl Oxc {
|
||||
}
|
||||
|
||||
fn convert_ast(&mut self, program: &mut Program) {
|
||||
use serde::Deserialize;
|
||||
|
||||
Utf8ToUtf16::new().convert(program);
|
||||
self.ast = program.serialize(&self.serializer).unwrap();
|
||||
|
||||
// Convert:
|
||||
// 1. `Program` to JSON string using `ESTree`.
|
||||
// 2. JSON string to `serde_json::Value`.
|
||||
// 3. `serde_json::Value` to `wasm_bindgen::JsValue`.
|
||||
// TODO: There has to be a better way!
|
||||
let json = program.to_json();
|
||||
let s = serde_json::de::StrRead::new(&json);
|
||||
let mut deserializer = serde_json::Deserializer::new(s);
|
||||
let value = serde_json::Value::deserialize(&mut deserializer).unwrap();
|
||||
deserializer.end().unwrap();
|
||||
self.ast = value.serialize(&self.serializer).unwrap();
|
||||
|
||||
self.comments = Self::map_comments(program.source_text, &program.comments);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ oxc_sourcemap = { workspace = true, features = ["napi"] }
|
||||
|
||||
rustc-hash = { workspace = true }
|
||||
self_cell = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
string_wizard = { workspace = true, features = ["sourcemap", "serde"] }
|
||||
|
||||
ctor = { workspace = true }
|
||||
|
||||
@@ -106,7 +106,7 @@ fn parse_with_return(filename: &str, source_text: String, options: &ParserOption
|
||||
}
|
||||
}
|
||||
}
|
||||
let program = serde_json::to_string(&ret.program).unwrap();
|
||||
let program = ret.program.to_json();
|
||||
let module = EcmaScriptModule::from(&ret.module_record);
|
||||
ParseResult { source_text, program, module, comments, errors }
|
||||
}
|
||||
|
||||
@@ -27,6 +27,24 @@ describe('parse', () => {
|
||||
expect(code.substring(comment.start, comment.end)).toBe('/*' + comment.value + '*/');
|
||||
});
|
||||
|
||||
it('`Infinity` is represented as `Infinity` number', () => {
|
||||
const ret = parseSync('test.js', '1e+350');
|
||||
expect(ret.errors.length).toBe(0);
|
||||
expect(ret.program.body.length).toBe(1);
|
||||
expect(ret.program.body[0]).toEqual({
|
||||
type: 'ExpressionStatement',
|
||||
start: 0,
|
||||
end: 6,
|
||||
expression: {
|
||||
type: 'Literal',
|
||||
start: 0,
|
||||
end: 6,
|
||||
value: Infinity,
|
||||
raw: '1e+350',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('`BigIntLiteral` has `value` as `BigInt`', () => {
|
||||
const ret = parseSync('test.js', '123_456n');
|
||||
expect(ret.errors.length).toBe(0);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Derive for `Serialize` impls, which serialize AST to ESTree format in JSON.
|
||||
//! Derive for `ESTree` impls, which serialize AST to ESTree format in JSON.
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
@@ -8,7 +8,7 @@ use syn::{parse_str, Expr};
|
||||
|
||||
use crate::{
|
||||
schema::{Def, EnumDef, FieldDef, Schema, StructDef, TypeDef, VariantDef, Visibility},
|
||||
utils::{create_safe_ident, number_lit},
|
||||
utils::create_safe_ident,
|
||||
Result,
|
||||
};
|
||||
|
||||
@@ -16,7 +16,7 @@ use super::{
|
||||
attr_positions, define_derive, AttrLocation, AttrPart, AttrPositions, Derive, StructOrEnum,
|
||||
};
|
||||
|
||||
/// Derive for `Serialize` impls, which serialize AST to ESTree format in JSON.
|
||||
/// Derive for `ESTree` impls, which serialize AST to ESTree format in JSON.
|
||||
pub struct DeriveESTree;
|
||||
|
||||
define_derive!(DeriveESTree);
|
||||
@@ -61,21 +61,17 @@ impl Derive for DeriveESTree {
|
||||
|
||||
fn prelude(&self) -> TokenStream {
|
||||
quote! {
|
||||
#![allow(unused_imports, clippy::match_same_arms)]
|
||||
#![allow(unused_imports, clippy::match_same_arms, clippy::semicolon_if_nothing_returned)]
|
||||
|
||||
///@@line_break
|
||||
use serde::{
|
||||
__private::ser::FlatMapSerializer,
|
||||
ser::SerializeMap,
|
||||
Serialize, Serializer
|
||||
use oxc_estree::{
|
||||
ser::{AppendTo, AppendToConcat},
|
||||
ESTree, FlatStructSerializer, Serializer, StructSerializer,
|
||||
};
|
||||
|
||||
///@@line_break
|
||||
use oxc_estree::ser::{AppendTo, AppendToConcat};
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate implementation of `Serialize` for a struct or enum.
|
||||
/// Generate implementation of `ESTree` for a struct or enum.
|
||||
fn derive(&self, type_def: StructOrEnum, schema: &Schema) -> TokenStream {
|
||||
let body = match type_def {
|
||||
StructOrEnum::Struct(struct_def) => {
|
||||
@@ -95,8 +91,8 @@ impl Derive for DeriveESTree {
|
||||
let ty = type_def.ty_anon(schema);
|
||||
|
||||
quote! {
|
||||
impl Serialize for #ty {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
impl ESTree for #ty {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
#body
|
||||
}
|
||||
}
|
||||
@@ -255,7 +251,7 @@ fn generate_body_for_struct(struct_def: &StructDef, schema: &Schema) -> TokenStr
|
||||
let type_field = if gen.add_type_field {
|
||||
let type_name = struct_def.estree.rename.as_deref().unwrap_or_else(|| struct_def.name());
|
||||
quote! {
|
||||
map.serialize_entry("type", #type_name)?;
|
||||
state.serialize_field("type", #type_name);
|
||||
}
|
||||
} else {
|
||||
quote!()
|
||||
@@ -263,10 +259,10 @@ fn generate_body_for_struct(struct_def: &StructDef, schema: &Schema) -> TokenStr
|
||||
|
||||
let stmts = gen.stmts;
|
||||
quote! {
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
let mut state = serializer.serialize_struct();
|
||||
#type_field
|
||||
#stmts
|
||||
map.end()
|
||||
state.end();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,7 +278,7 @@ struct StructSerializerGenerator<'s> {
|
||||
/// `true` if a `type` field should be added.
|
||||
/// `false` one already exists (or if `#[estree(no_type)]` attr on struct).
|
||||
add_type_field: bool,
|
||||
/// Crate in which the `Serialize` impl for the type will be generated
|
||||
/// Crate in which the `ESTree` impl for the type will be generated
|
||||
krate: &'s str,
|
||||
/// Schema
|
||||
schema: &'s Schema,
|
||||
@@ -352,7 +348,7 @@ impl<'s> StructSerializerGenerator<'s> {
|
||||
);
|
||||
|
||||
self.stmts.extend(quote! {
|
||||
#self_path.#field_name_ident.serialize(FlatMapSerializer(&mut map))?;
|
||||
#self_path.#field_name_ident.serialize(FlatStructSerializer(&mut state));
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -384,7 +380,7 @@ impl<'s> StructSerializerGenerator<'s> {
|
||||
};
|
||||
|
||||
self.stmts.extend(quote! {
|
||||
map.serialize_entry(#field_camel_name, &#value)?;
|
||||
state.serialize_field(#field_camel_name, &#value);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -397,7 +393,7 @@ impl<'s> StructSerializerGenerator<'s> {
|
||||
let converter = self.schema.meta_by_name(converter_name);
|
||||
let converter_path = converter.import_path_from_crate(self.krate, self.schema);
|
||||
self.stmts.extend(quote! {
|
||||
map.serialize_entry(#field_name, &#converter_path(#self_path))?;
|
||||
state.serialize_field(#field_name, &#converter_path(#self_path));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -409,11 +405,9 @@ fn generate_body_for_enum(enum_def: &EnumDef, schema: &Schema) -> TokenStream {
|
||||
let match_branches = enum_def.all_variants(schema).map(|variant| {
|
||||
let variant_ident = variant.ident();
|
||||
if variant.is_fieldless() {
|
||||
let enum_name = enum_def.name();
|
||||
let discriminant = number_lit(variant.discriminant);
|
||||
let value = get_fieldless_variant_value(enum_def, variant);
|
||||
quote! {
|
||||
#enum_ident::#variant_ident => serializer.serialize_unit_variant(#enum_name, #discriminant, #value),
|
||||
#enum_ident::#variant_ident => #value.serialize(serializer),
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
@@ -469,7 +463,7 @@ pub fn should_flatten_field(field: &FieldDef, schema: &Schema) -> bool {
|
||||
///
|
||||
/// If the field's type is a struct, then usually it can.
|
||||
/// But it can't in the case where that type is defined in a different crate from where
|
||||
/// the `Serialize` impl will be generated, and one of the flattened fields is not public.
|
||||
/// the `ESTree` impl will be generated, and one of the flattened fields is not public.
|
||||
pub fn can_flatten_field_inline(field: &FieldDef, krate: &str, schema: &Schema) -> bool {
|
||||
let field_type = field.type_def(schema);
|
||||
let TypeDef::Struct(struct_def) = field_type else { return false };
|
||||
|
||||
@@ -2,4 +2,19 @@ commit: bc5c1417
|
||||
|
||||
estree_test262 Summary:
|
||||
AST Parsed : 48767/48767 (100.00%)
|
||||
Positive Passed: 48767/48767 (100.00%)
|
||||
Positive Passed: 48762/48767 (99.99%)
|
||||
tasks/coverage/test262/test/built-ins/Array/prototype/indexOf/15.4.4.14-10-1.js
|
||||
serde_json error: number out of range at line 1 column 3184
|
||||
|
||||
tasks/coverage/test262/test/built-ins/Array/prototype/lastIndexOf/15.4.4.15-9-1.js
|
||||
serde_json error: number out of range at line 1 column 3184
|
||||
|
||||
tasks/coverage/test262/test/built-ins/Number/S9.3.1_A6_T1.js
|
||||
serde_json error: number out of range at line 1 column 1792
|
||||
|
||||
tasks/coverage/test262/test/built-ins/Number/S9.3.1_A6_T2.js
|
||||
serde_json error: number out of range at line 1 column 3552
|
||||
|
||||
tasks/coverage/test262/test/language/types/number/8.5.1.js
|
||||
serde_json error: number out of range at line 1 column 9723
|
||||
|
||||
@@ -76,7 +76,7 @@ impl CompilerInterface for Driver {
|
||||
self.errors.push(OxcDiagnostic::error("SourceType must not be unambiguous."));
|
||||
}
|
||||
// Make sure serialization doesn't crash; also for code coverage.
|
||||
program.test_to_json().unwrap();
|
||||
program.to_json();
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import assert from 'assert';
|
||||
import { parseSync } from '../../npm/parser-wasm/node/oxc_parser_wasm.js';
|
||||
|
||||
const code = '/abc/gu; 123n;';
|
||||
const code = '/abc/gu; 123n; 1e+350;';
|
||||
const result = parseSync(code, { sourceFilename: 'test.ts' });
|
||||
|
||||
assert(result.errors.length === 0);
|
||||
@@ -12,8 +12,11 @@ assert(result.program === program);
|
||||
|
||||
// Check output is correct
|
||||
assert(program.type === 'Program');
|
||||
assert(program.body.length === 2);
|
||||
assert(program.body.length === 3);
|
||||
|
||||
// Check `RegExp`s and `BigInt`s are deserialized correctly
|
||||
// Check `RegExp`s, `BigInt`s and `Infinity` are deserialized correctly
|
||||
assert(program.body[0].expression.value instanceof RegExp);
|
||||
assert(typeof program.body[1].expression.value === 'bigint');
|
||||
const inf = program.body[2].expression.value;
|
||||
assert(typeof inf === 'number');
|
||||
assert(inf === Infinity);
|
||||
Reference in new issue
Block a user