serser/test/
printsink.rs

1use std::fmt;
2
3use crate::error::*;
4use crate::token::*;
5use crate::TokenSink;
6
7/// The error type used in [PrintingTokenSink].
8#[derive(Debug)]
9pub enum PrintError<DE: Error> {
10    /// The downstream sink returned an error.
11    Downstream(DE),
12
13    /// The [writer](std::io::Write) returned an error.
14    Print(std::io::Error),
15}
16
17impl<DE: Error> Error for PrintError<DE> {
18    fn invalid_token(token: Token<'_>, expected: Option<TokenTypes>) -> Self {
19        Self::Downstream(DE::invalid_token(token, expected))
20    }
21
22    fn invalid_field(field: &str) -> Self {
23        Self::Downstream(DE::invalid_field(field))
24    }
25
26    fn missing_fields(fields: &[&str]) -> Self {
27        Self::Downstream(DE::missing_fields(fields))
28    }
29
30    fn invalid_variant(variant: EnumVariant<'_>) -> Self {
31        Self::Downstream(DE::invalid_variant(variant))
32    }
33
34    fn unexpected_end(expected: Option<TokenTypes>) -> Self {
35        Self::Downstream(DE::unexpected_end(expected))
36    }
37}
38
39impl<DE: Error> fmt::Display for PrintError<DE> {
40    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
41        match self {
42            Self::Downstream(err) => fmt::Display::fmt(err, f),
43            Self::Print(err) => fmt::Display::fmt(err, f),
44        }
45    }
46}
47
48impl<DE: Error> std::error::Error for PrintError<DE> {}
49
50/// A token sink that prints a description of the tokens to a writer,
51/// and forwards them to another sink. For debugging.
52///
53/// Each token is output on one line. Start and end tokens adjust
54/// indentation.
55///
56/// ## Example
57///
58/// ```
59/// # use crate::serser::*;
60/// # use crate::serser::test::*;
61/// let mut got = TokenVec::new();
62/// let mut output = Vec::new();
63/// vec![42u32]
64///     .into_tokens(&mut PrintingTokenSink::new(&mut got, &mut output, "T: "))
65///     .unwrap();
66/// assert_eq!(
67///     String::from_utf8(output).unwrap(),
68///     concat!(
69///         "T: Seq(SeqMeta { size_hint: Some(1) })\n",
70///         "T:   U32(42)\n",
71///         "T: EndSeq\n")
72/// );
73/// ```
74pub struct PrintingTokenSink<'a: 'b, 'b, S: TokenSink, W: std::io::Write> {
75    sink: &'b mut S,
76    prefix: &'a str,
77    writer: W,
78    indent: usize,
79}
80
81impl<'a: 'b, 'b, S: TokenSink, W: std::io::Write> PrintingTokenSink<'a, 'b, S, W> {
82    /// Creates a new printing sink. The prefix is used for each
83    /// output line, before the indentation.
84    pub fn new(sink: &'b mut S, writer: W, prefix: &'a str) -> Self {
85        Self {
86            sink,
87            prefix,
88            writer,
89            indent: 0,
90        }
91    }
92
93    fn print_token(&mut self, token: &Token<'_>, indent: usize) -> std::io::Result<()> {
94        let prefix = self.prefix;
95
96        writeln!(
97            self.writer,
98            "{}{:indent$}{:?}",
99            prefix,
100            "",
101            token,
102            indent = 2 * indent
103        )?;
104
105        Ok(())
106    }
107}
108
109impl<'a: 'b, 'b, S: TokenSink, W: std::io::Write> TokenSink for PrintingTokenSink<'a, 'b, S, W> {
110    type Error = PrintError<S::Error>;
111
112    fn yield_token(&mut self, token: Token<'_>) -> Result<bool, Self::Error> {
113        if token.is_end() {
114            self.indent -= 1;
115        }
116
117        self.print_token(&token, self.indent)
118            .map_err(|err| PrintError::Print(err))?;
119
120        if token.is_start() {
121            self.indent += 1;
122        }
123
124        self.sink
125            .yield_token(token)
126            .map_err(|err| PrintError::Downstream(err))
127    }
128
129    fn expect_tokens(&mut self) -> Option<TokenTypes> {
130        self.sink.expect_tokens()
131    }
132}
133
134#[cfg(test)]
135mod tests {
136    use super::*;
137    use crate::into::*;
138    use crate::vec::*;
139
140    #[test]
141    fn test_printing_token_sink() {
142        let mut got = TokenVec::new();
143        let mut output = Vec::new();
144        vec![42u32]
145            .into_tokens(&mut PrintingTokenSink::new(&mut got, &mut output, "T: "))
146            .unwrap();
147        assert_eq!(
148            String::from_utf8(output).unwrap(),
149            "T: Seq(SeqMeta { size_hint: Some(1) })\nT:   U32(42)\nT: EndSeq\n"
150        );
151    }
152}