diff options
Diffstat (limited to 'crates/printer/src')
-rw-r--r-- | crates/printer/src/jsont.rs | 184 | ||||
-rw-r--r-- | crates/printer/src/stats.rs | 110 | ||||
-rw-r--r-- | crates/printer/src/util.rs | 12 |
3 files changed, 201 insertions, 105 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() + } } diff --git a/crates/printer/src/stats.rs b/crates/printer/src/stats.rs index f1898c0b..555401b3 100644 --- a/crates/printer/src/stats.rs +++ b/crates/printer/src/stats.rs @@ -10,7 +10,6 @@ use crate::util::NiceDuration; /// When statistics are reported by a printer, they correspond to all searches /// executed with that printer. #[derive(Clone, Debug, Default, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde_derive::Serialize))] pub struct Stats { elapsed: NiceDuration, searches: u64, @@ -21,49 +20,6 @@ pub struct Stats { matches: u64, } -impl Add for Stats { - type Output = Stats; - - fn add(self, rhs: Stats) -> Stats { - self + &rhs - } -} - -impl<'a> Add<&'a Stats> for Stats { - type Output = Stats; - - fn add(self, rhs: &'a Stats) -> Stats { - Stats { - elapsed: NiceDuration(self.elapsed.0 + rhs.elapsed.0), - searches: self.searches + rhs.searches, - searches_with_match: self.searches_with_match - + rhs.searches_with_match, - bytes_searched: self.bytes_searched + rhs.bytes_searched, - bytes_printed: self.bytes_printed + rhs.bytes_printed, - matched_lines: self.matched_lines + rhs.matched_lines, - matches: self.matches + rhs.matches, - } - } -} - -impl AddAssign for Stats { - fn add_assign(&mut self, rhs: Stats) { - *self += &rhs; - } -} - -impl<'a> AddAssign<&'a Stats> for Stats { - fn add_assign(&mut self, rhs: &'a Stats) { - self.elapsed.0 += rhs.elapsed.0; - self.searches += rhs.searches; - self.searches_with_match += rhs.searches_with_match; - self.bytes_searched += rhs.bytes_searched; - self.bytes_printed += rhs.bytes_printed; - self.matched_lines += rhs.matched_lines; - self.matches += rhs.matches; - } -} - impl Stats { /// Return a new value for tracking aggregate statistics across searches. /// @@ -147,3 +103,69 @@ impl Stats { self.matches += n; } } + +impl Add for Stats { + type Output = Stats; + + fn add(self, rhs: Stats) -> Stats { + self + &rhs + } +} + +impl<'a> Add<&'a Stats> for Stats { + type Output = Stats; + + fn add(self, rhs: &'a Stats) -> Stats { + Stats { + elapsed: NiceDuration(self.elapsed.0 + rhs.elapsed.0), + searches: self.searches + rhs.searches, + searches_with_match: self.searches_with_match + + rhs.searches_with_match, + bytes_searched: self.bytes_searched + rhs.bytes_searched, + bytes_printed: self.bytes_printed + rhs.bytes_printed, + matched_lines: self.matched_lines + rhs.matched_lines, + matches: self.matches + rhs.matches, + } + } +} + +impl AddAssign for Stats { + fn add_assign(&mut self, rhs: Stats) { + *self += &rhs; + } +} + +impl<'a> AddAssign<&'a Stats> for Stats { + fn add_assign(&mut self, rhs: &'a Stats) { + self.elapsed.0 += rhs.elapsed.0; + self.searches += rhs.searches; + self.searches_with_match += rhs.searches_with_match; + self.bytes_searched += rhs.bytes_searched; + self.bytes_printed += rhs.bytes_printed; + self.matched_lines += rhs.matched_lines; + self.matches += rhs.matches; + } +} + +#[cfg(feature = "serde")] +impl serde::Serialize for Stats { + fn serialize<S: serde::Serializer>( + &self, + s: S, + ) -> Result<S::Ok, S::Error> { + use serde::ser::SerializeStruct; + + let mut state = s.serialize_struct("Stats", 7)?; + state.serialize_field("elapsed", &self.elapsed)?; + state.serialize_field("searches", &self.searches)?; + state.serialize_field( + "searches_with_match", + &self.searches_with_match, + )?; + state.serialize_field("bytes_searched", &self.bytes_searched)?; + state.serialize_field("bytes_printed", &self.bytes_printed)?; + state.serialize_field("matched_lines", &self.matched_lines)?; + state.serialize_field("matches", &self.matches)?; + state.end() + } +} diff --git a/crates/printer/src/util.rs b/crates/printer/src/util.rs index db19504c..04f9e129 100644 --- a/crates/printer/src/util.rs +++ b/crates/printer/src/util.rs @@ -8,9 +8,6 @@ use { }, }; -#[cfg(feature = "serde")] -use serde::{Serialize, Serializer}; - use crate::{hyperlink::HyperlinkPath, MAX_LOOK_AHEAD}; /// A type for handling replacements while amortizing allocation. @@ -385,11 +382,14 @@ impl NiceDuration { } #[cfg(feature = "serde")] -impl Serialize for NiceDuration { - fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> { +impl serde::Serialize for NiceDuration { + fn serialize<S: serde::Serializer>( + &self, + ser: S, + ) -> Result<S::Ok, S::Error> { use serde::ser::SerializeStruct; - let mut state = ser.serialize_struct("Duration", 2)?; + let mut state = ser.serialize_struct("Duration", 3)?; state.serialize_field("secs", &self.0.as_secs())?; state.serialize_field("nanos", &self.0.subsec_nanos())?; state.serialize_field("human", &format!("{}", self))?; |