use oxc_data_structures::code_buffer::CodeBuffer;
use super::{
Config, ESTree, ESTreeSequenceSerializer, ESTreeSerializer, Formatter, Serializer,
TracePathPart,
};
pub trait StructSerializer {
type Config: Config;
type Formatter: Formatter;
fn serialize_field<T: ESTree + ?Sized>(&mut self, key: &'static str, value: &T);
fn serialize_js_field<T: ESTree + ?Sized>(&mut self, key: &'static str, value: &T);
fn serialize_ts_field<T: ESTree + ?Sized>(&mut self, key: &'static str, value: &T);
fn serialize_span<S: ESTreeSpan>(&mut self, span: S);
fn end(self);
fn include_ts_fields(&self) -> bool;
fn ranges(&self) -> bool;
}
pub struct ESTreeStructSerializer<'s, C: Config, F: Formatter> {
serializer: &'s mut ESTreeSerializer<C, F>,
state: StructState,
}
impl<'s, C: Config, F: Formatter> ESTreeStructSerializer<'s, C, F> {
pub(super) fn new(mut serializer: &'s mut ESTreeSerializer<C, F>) -> Self {
if serializer.config.fixes() {
serializer.trace_path.push(TracePathPart::DUMMY);
}
serializer.buffer_mut().print_ascii_byte(b'{');
Self { serializer, state: StructState::Empty }
}
}
impl<C: Config, F: Formatter> StructSerializer for ESTreeStructSerializer<'_, C, F> {
type Config = C;
type Formatter = F;
fn serialize_field<T: ESTree + ?Sized>(&mut self, key: &'static str, value: &T) {
if self.serializer.config.fixes() {
*self.serializer.trace_path.last_mut() = TracePathPart::Key(key);
}
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.print_ascii_byte(b',');
formatter.before_later_element(buffer);
}
buffer.print_strs_array(["\"", key, "\":"]);
formatter.before_field_value(buffer);
value.serialize(&mut *self.serializer);
}
#[inline(always)]
fn serialize_js_field<T: ESTree + ?Sized>(&mut self, key: &'static str, value: &T) {
if !self.include_ts_fields() {
self.serialize_field(key, value);
}
}
#[inline(always)]
fn serialize_ts_field<T: ESTree + ?Sized>(&mut self, key: &'static str, value: &T) {
if self.include_ts_fields() {
self.serialize_field(key, value);
}
}
fn serialize_span<S: ESTreeSpan>(&mut self, span: S) {
let range = span.range();
self.serialize_field("start", &range[0]);
self.serialize_field("end", &range[1]);
if self.serializer.ranges() {
self.serialize_field("range", &range);
}
}
fn end(self) {
let mut serializer = self.serializer;
if serializer.config.fixes() {
unsafe { serializer.trace_path.pop_unchecked() };
}
let (buffer, formatter) = serializer.buffer_and_formatter_mut();
if self.state == StructState::HasFields {
formatter.after_last_element(buffer);
}
buffer.print_ascii_byte(b'}');
}
#[inline(always)]
fn include_ts_fields(&self) -> bool {
self.serializer.include_ts_fields()
}
#[inline(always)]
fn ranges(&self) -> bool {
self.serializer.ranges()
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub(super) enum StructState {
Empty,
HasFields,
}
pub struct FlatStructSerializer<'p, P: StructSerializer>(pub &'p mut P);
impl<'p, P: StructSerializer> Serializer for FlatStructSerializer<'p, P> {
type Formatter = P::Formatter;
type StructSerializer = Self;
type SequenceSerializer = ESTreeSequenceSerializer<'p, P::Config, P::Formatter>;
fn serialize_struct(self) -> Self {
self
}
fn serialize_sequence(self) -> ESTreeSequenceSerializer<'p, P::Config, P::Formatter> {
const {
panic!("Cannot flatten a sequence into a struct");
}
}
fn record_fix_path(&mut self) {
const {
panic!("Cannot call `record_fix_path` on a `FlatStructSerializer`");
}
}
#[inline(always)]
fn include_ts_fields(&self) -> bool {
self.0.include_ts_fields()
}
#[inline(always)]
fn ranges(&self) -> bool {
self.0.ranges()
}
fn buffer_mut(&mut self) -> &mut CodeBuffer {
const {
panic!("Cannot flatten anything but a struct into another struct");
}
}
fn buffer_and_formatter_mut(&mut self) -> (&mut CodeBuffer, &mut P::Formatter) {
const {
panic!("Cannot flatten anything but a struct into another struct");
}
}
}
impl<P: StructSerializer> StructSerializer for FlatStructSerializer<'_, P> {
type Config = P::Config;
type Formatter = P::Formatter;
#[inline(always)]
fn serialize_field<T: ESTree + ?Sized>(&mut self, key: &'static str, value: &T) {
self.0.serialize_field(key, value);
}
#[inline(always)]
fn serialize_js_field<T: ESTree + ?Sized>(&mut self, key: &'static str, value: &T) {
self.0.serialize_js_field(key, value);
}
#[inline(always)]
fn serialize_ts_field<T: ESTree + ?Sized>(&mut self, key: &'static str, value: &T) {
self.0.serialize_ts_field(key, value);
}
fn serialize_span<S: ESTreeSpan>(&mut self, span: S) {
self.0.serialize_span(span);
}
fn end(self) {
}
#[inline(always)]
fn include_ts_fields(&self) -> bool {
self.0.include_ts_fields()
}
#[inline(always)]
fn ranges(&self) -> bool {
self.0.ranges()
}
}
pub trait ESTreeSpan: Copy {
fn range(self) -> [u32; 2];
}
#[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::default();
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::default();
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::default();
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::default();
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"
}"#
);
}
#[test]
fn serialize_struct_with_or_without_ts() {
struct Foo {
js: u32,
ts: u32,
js_only: u32,
more_js: u32,
}
impl ESTree for Foo {
fn serialize<S: Serializer>(&self, serializer: S) {
let mut state = serializer.serialize_struct();
state.serialize_field("js", &self.js);
state.serialize_ts_field("ts", &self.ts);
state.serialize_js_field("jsOnly", &self.js_only);
state.serialize_field("moreJs", &self.more_js);
state.end();
}
}
let foo = Foo { js: 1, ts: 2, js_only: 3, more_js: 4 };
let mut serializer = CompactSerializer::default();
foo.serialize(&mut serializer);
let s = serializer.into_string();
assert_eq!(&s, r#"{"js":1,"ts":2,"moreJs":4}"#);
let mut serializer = PrettySerializer::default();
foo.serialize(&mut serializer);
let s = serializer.into_string();
assert_eq!(
&s,
r#"{
"js": 1,
"ts": 2,
"moreJs": 4
}"#
);
let mut serializer = CompactSerializer::new(false, false);
foo.serialize(&mut serializer);
let s = serializer.into_string();
assert_eq!(&s, r#"{"js":1,"jsOnly":3,"moreJs":4}"#);
let mut serializer = PrettySerializer::new(false, false);
foo.serialize(&mut serializer);
let s = serializer.into_string();
assert_eq!(
&s,
r#"{
"js": 1,
"jsOnly": 3,
"moreJs": 4
}"#
);
}
}