diff options
Diffstat (limited to 'crates/printer/src/jsont.rs')
-rw-r--r-- | crates/printer/src/jsont.rs | 184 |
1 files changed, 129 insertions, 55 deletions
diff --git a/crates/printer/src/jsont.rs b/crates/printer/src/jsont.rs index 5d901041..6e5e85df 100644 --- a/crates/printer/src/jsont.rs +++ b/crates/printer/src/jsont.rs @@ -8,13 +8,6 @@ use std::{borrow::Cow, path::Path}; -use {base64, serde::Serializer, serde_derive::Serialize}; - -use crate::stats::Stats; - -#[derive(Serialize)] -#[serde(tag = "type", content = "data")] -#[serde(rename_all = "snake_case")] pub(crate) enum Message<'a> { Begin(Begin<'a>), End(End<'a>), @@ -22,51 +15,145 @@ pub(crate) enum Message<'a> { Context(Context<'a>), } -#[derive(Serialize)] +impl<'a> serde::Serialize for Message<'a> { + fn serialize<S: serde::Serializer>( + &self, + s: S, + ) -> Result<S::Ok, S::Error> { + use serde::ser::SerializeStruct; + + let mut state = s.serialize_struct("Message", 2)?; + match *self { + Message::Begin(ref msg) => { + state.serialize_field("type", &"begin")?; + state.serialize_field("data", msg)?; + } + Message::End(ref msg) => { + state.serialize_field("type", &"end")?; + state.serialize_field("data", msg)?; + } + Message::Match(ref msg) => { + state.serialize_field("type", &"match")?; + state.serialize_field("data", msg)?; + } + Message::Context(ref msg) => { + state.serialize_field("type", &"context")?; + state.serialize_field("data", msg)?; + } + } + state.end() + } +} + pub(crate) struct Begin<'a> { - #[serde(serialize_with = "ser_path")] pub(crate) path: Option<&'a Path>, } -#[derive(Serialize)] +impl<'a> serde::Serialize for Begin<'a> { + fn serialize<S: serde::Serializer>( + &self, + s: S, + ) -> Result<S::Ok, S::Error> { + use serde::ser::SerializeStruct; + + let mut state = s.serialize_struct("Begin", 1)?; + state.serialize_field("path", &self.path.map(Data::from_path))?; + state.end() + } +} + pub(crate) struct End<'a> { - #[serde(serialize_with = "ser_path")] pub(crate) path: Option<&'a Path>, pub(crate) binary_offset: Option<u64>, - pub(crate) stats: Stats, + pub(crate) stats: crate::stats::Stats, +} + +impl<'a> serde::Serialize for End<'a> { + fn serialize<S: serde::Serializer>( + &self, + s: S, + ) -> Result<S::Ok, S::Error> { + use serde::ser::SerializeStruct; + + let mut state = s.serialize_struct("End", 3)?; + state.serialize_field("path", &self.path.map(Data::from_path))?; + state.serialize_field("binary_offset", &self.binary_offset)?; + state.serialize_field("stats", &self.stats)?; + state.end() + } } -#[derive(Serialize)] pub(crate) struct Match<'a> { - #[serde(serialize_with = "ser_path")] pub(crate) path: Option<&'a Path>, - #[serde(serialize_with = "ser_bytes")] pub(crate) lines: &'a [u8], pub(crate) line_number: Option<u64>, pub(crate) absolute_offset: u64, pub(crate) submatches: &'a [SubMatch<'a>], } -#[derive(Serialize)] +impl<'a> serde::Serialize for Match<'a> { + fn serialize<S: serde::Serializer>( + &self, + s: S, + ) -> Result<S::Ok, S::Error> { + use serde::ser::SerializeStruct; + + let mut state = s.serialize_struct("Match", 5)?; + state.serialize_field("path", &self.path.map(Data::from_path))?; + state.serialize_field("lines", &Data::from_bytes(self.lines))?; + state.serialize_field("line_number", &self.line_number)?; + state.serialize_field("absolute_offset", &self.absolute_offset)?; + state.serialize_field("submatches", &self.submatches)?; + state.end() + } +} + pub(crate) struct Context<'a> { - #[serde(serialize_with = "ser_path")] pub(crate) path: Option<&'a Path>, - #[serde(serialize_with = "ser_bytes")] pub(crate) lines: &'a [u8], pub(crate) line_number: Option<u64>, pub(crate) absolute_offset: u64, pub(crate) submatches: &'a [SubMatch<'a>], } -#[derive(Serialize)] +impl<'a> serde::Serialize for Context<'a> { + fn serialize<S: serde::Serializer>( + &self, + s: S, + ) -> Result<S::Ok, S::Error> { + use serde::ser::SerializeStruct; + + let mut state = s.serialize_struct("Context", 5)?; + state.serialize_field("path", &self.path.map(Data::from_path))?; + state.serialize_field("lines", &Data::from_bytes(self.lines))?; + state.serialize_field("line_number", &self.line_number)?; + state.serialize_field("absolute_offset", &self.absolute_offset)?; + state.serialize_field("submatches", &self.submatches)?; + state.end() + } +} + pub(crate) struct SubMatch<'a> { - #[serde(rename = "match")] - #[serde(serialize_with = "ser_bytes")] pub(crate) m: &'a [u8], pub(crate) start: usize, pub(crate) end: usize, } +impl<'a> serde::Serialize for SubMatch<'a> { + fn serialize<S: serde::Serializer>( + &self, + s: S, + ) -> Result<S::Ok, S::Error> { + use serde::ser::SerializeStruct; + + let mut state = s.serialize_struct("SubMatch", 3)?; + state.serialize_field("match", &Data::from_bytes(self.m))?; + state.serialize_field("start", &self.start)?; + state.serialize_field("end", &self.end)?; + state.end() + } +} + /// Data represents things that look like strings, but may actually not be /// valid UTF-8. To handle this, `Data` is serialized as an object with one /// of two keys: `text` (for valid UTF-8) or `bytes` (for invalid UTF-8). @@ -74,16 +161,10 @@ pub(crate) struct SubMatch<'a> { /// The happy path is valid UTF-8, which streams right through as-is, since /// it is natively supported by JSON. When invalid UTF-8 is found, then it is /// represented as arbitrary bytes and base64 encoded. -#[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize)] -#[serde(untagged)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] enum Data<'a> { - Text { - text: Cow<'a, str>, - }, - Bytes { - #[serde(serialize_with = "to_base64")] - bytes: &'a [u8], - }, + Text { text: Cow<'a, str> }, + Bytes { bytes: &'a [u8] }, } impl<'a> Data<'a> { @@ -115,29 +196,22 @@ impl<'a> Data<'a> { } } -fn to_base64<T, S>(bytes: T, ser: S) -> Result<S::Ok, S::Error> -where - T: AsRef<[u8]>, - S: Serializer, -{ - use base64::engine::{general_purpose::STANDARD, Engine}; - ser.serialize_str(&STANDARD.encode(&bytes)) -} - -fn ser_bytes<T, S>(bytes: T, ser: S) -> Result<S::Ok, S::Error> -where - T: AsRef<[u8]>, - S: Serializer, -{ - use serde::Serialize; - Data::from_bytes(bytes.as_ref()).serialize(ser) -} - -fn ser_path<P, S>(path: &Option<P>, ser: S) -> Result<S::Ok, S::Error> -where - P: AsRef<Path>, - S: Serializer, -{ - use serde::Serialize; - path.as_ref().map(|p| Data::from_path(p.as_ref())).serialize(ser) +impl<'a> serde::Serialize for Data<'a> { + fn serialize<S: serde::Serializer>( + &self, + s: S, + ) -> Result<S::Ok, S::Error> { + use serde::ser::SerializeStruct; + + let mut state = s.serialize_struct("Data", 1)?; + match *self { + Data::Text { ref text } => state.serialize_field("text", text)?, + Data::Bytes { bytes } => { + use base64::engine::{general_purpose::STANDARD, Engine}; + let encoded = STANDARD.encode(bytes); + state.serialize_field("bytes", &encoded)?; + } + } + state.end() + } } |