ezk_sip_types/
print.rs

1//! Printing utilities for SIP message components
2
3use crate::method::Method;
4use std::fmt;
5use std::fmt::Formatter;
6use std::ops::Deref;
7
8/// Context in which an URI is being printed
9#[derive(Copy, Clone)]
10pub enum UriContext {
11    /// The URI is being printed inside the request-line
12    ReqUri,
13
14    /// The URI is being printed inside an From/To header
15    FromTo,
16
17    /// The URI is being printed inside an Contact header
18    Contact,
19
20    /// The URI is being printed inside an Route/RecordRoute etc header
21    Routing,
22}
23
24/// SIP message context for printing sip types
25#[derive(Default, Copy, Clone)]
26pub struct PrintCtx<'a> {
27    /// method of the request being printed
28    pub method: Option<&'a Method>,
29    pub uri: Option<UriContext>,
30}
31
32/// Implements [`fmt::Display`] where `T` implements [`Print`] and passes its context to [`Print::print`]
33///
34/// Constructed using [`AppendCtx::print_ctx`].
35pub struct WithPrintCtx<'a, T: ?Sized> {
36    pub ctx: PrintCtx<'a>,
37    _self: &'a T,
38}
39
40impl<T: Print + ?Sized> fmt::Display for WithPrintCtx<'_, T> {
41    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
42        Print::print(self._self, f, self.ctx)
43    }
44}
45
46impl<T> Deref for WithPrintCtx<'_, T> {
47    type Target = T;
48
49    fn deref(&self) -> &Self::Target {
50        self._self
51    }
52}
53
54/// Helper trait to wrap types that implement [`Print`] into [`WithPrintCtx`]
55pub trait AppendCtx {
56    /// Wrap a type inside a [`WithPrintCtx`] so it implements display
57    fn print_ctx<'a>(&'a self, ctx: PrintCtx<'a>) -> WithPrintCtx<'a, Self> {
58        WithPrintCtx { ctx, _self: self }
59    }
60
61    /// Wraps a type inside [`WithPrintCtx`] containing a 'empty' [`PrintCtx`].
62    ///
63    /// Useful for tests
64    fn default_print_ctx(&self) -> WithPrintCtx<'_, Self> {
65        self.print_ctx(Default::default())
66    }
67}
68
69impl<T: Print> AppendCtx for T {}
70
71/// Trait similar to [`fmt::Display`] with the difference that it also takes a [`PrintCtx`]
72///
73/// It is used to print types which require the context of the message they are printed in.
74pub trait Print {
75    fn print(&self, f: &mut fmt::Formatter<'_>, ctx: PrintCtx<'_>) -> fmt::Result;
76}
77
78impl<T: fmt::Display> Print for T {
79    fn print(&self, f: &mut fmt::Formatter<'_>, _: PrintCtx<'_>) -> fmt::Result {
80        fmt::Display::fmt(self, f)
81    }
82}
83
84/// Implements std::fmt::Debug for byte-slices.
85/// Useful to print ascii with special characters escaped
86// taken from bytes crate with some small changes
87pub struct BytesPrint<'b>(pub &'b [u8]);
88
89impl fmt::Debug for BytesPrint<'_> {
90    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91        for &b in self.0 {
92            if b == b'\n' {
93                writeln!(f, "\\n")?;
94            } else if b == b'\r' {
95                write!(f, "\\r")?;
96            } else if b == b'\t' {
97                write!(f, "\\t")?;
98            } else if b == b'\\' || b == b'"' {
99                write!(f, "\\{}", b as char)?;
100            } else if b == b'\0' {
101                write!(f, "\\0")?;
102            // ASCII printable
103            } else if (0x20..0x7f).contains(&b) {
104                write!(f, "{}", b as char)?;
105            } else {
106                write!(f, "\\x{b:02x}")?;
107            }
108        }
109        Ok(())
110    }
111}