evobench_tools/utillib/
escaped_display.rs

1//! Trait and temporary wrapper around strings and paths to `Display`
2//! them escaped.
3//!
4//! The motivation is to avoid using `{:?}` in format strings--those
5//! are dangerous in that this is just *really* "the debug" format,
6//! and if e.g. a Path is replaced with a complex type, the complex'
7//! type internals are dumped instead of just an escaped path. I've
8//! run into this enough times now to be tired of it.
9
10use std::{
11    fmt::{Debug, Display},
12    path::{Path, PathBuf},
13    sync::Arc,
14};
15
16pub struct DebugForDisplay<T: Debug>(pub T);
17
18impl<T: Debug> Display for DebugForDisplay<T> {
19    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20        write!(f, "{:?}", self.0)
21    }
22}
23
24/// Also implement Debug so that DebugForDisplay is usable in
25/// e.g. tuples that are to be shown via `:?`
26impl<T: Debug> Debug for DebugForDisplay<T> {
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        write!(f, "{:?}", self.0)
29    }
30}
31
32pub trait AsEscapedString {
33    type ViewableType<'t>
34    where
35        Self: 't;
36
37    fn as_escaped_string<'s>(&'s self) -> Self::ViewableType<'s>;
38}
39
40impl<'u> AsEscapedString for &'u str {
41    type ViewableType<'t>
42        = DebugForDisplay<&'t str>
43    where
44        Self: 't;
45
46    fn as_escaped_string<'s>(&'s self) -> Self::ViewableType<'s> {
47        DebugForDisplay(*self)
48    }
49}
50
51impl AsEscapedString for String {
52    type ViewableType<'t> = DebugForDisplay<&'t str>;
53
54    fn as_escaped_string<'s>(&'s self) -> Self::ViewableType<'s> {
55        DebugForDisplay(&**self)
56    }
57}
58
59impl<'u> AsEscapedString for &'u Path {
60    type ViewableType<'t>
61        = DebugForDisplay<&'t Path>
62    where
63        Self: 't;
64
65    fn as_escaped_string<'s>(&'s self) -> Self::ViewableType<'s> {
66        DebugForDisplay(&**self)
67    }
68}
69
70impl<'u> AsEscapedString for &'u Arc<Path> {
71    type ViewableType<'t>
72        = DebugForDisplay<&'t Path>
73    where
74        Self: 't;
75
76    fn as_escaped_string<'s>(&'s self) -> Self::ViewableType<'s> {
77        DebugForDisplay(&**self)
78    }
79}
80
81impl AsEscapedString for PathBuf {
82    type ViewableType<'t> = DebugForDisplay<&'t Path>;
83
84    fn as_escaped_string<'s>(&'s self) -> Self::ViewableType<'s> {
85        DebugForDisplay(&**self)
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92
93    #[test]
94    fn t_s() {
95        let s1 = "hello world";
96        assert_eq!(format!("<{}>", s1.as_escaped_string()), "<\"hello world\">");
97        let s2 = s1.to_owned() + " and so on";
98        assert_eq!(
99            format!("<{}>", s2.as_escaped_string()),
100            "<\"hello world and so on\">"
101        );
102    }
103
104    #[test]
105    fn t_p() {
106        let p1: &Path = "hi there".as_ref();
107        assert_eq!(format!("<{}>", p1.as_escaped_string()), "<\"hi there\">");
108        let p2 = p1.join("and more");
109        assert_eq!(
110            format!("<{}>", p2.as_escaped_string()),
111            "<\"hi there/and more\">"
112        );
113    }
114}