scarb_ui/
message.rs

1use serde::Serializer;
2
3#[cfg(doc)]
4use super::Ui;
5
6const JSON_SKIP_MESSAGE: &str = "UI_INTERNAL_SKIP";
7
8/// A typed object that can be either printed as a human-readable message or serialized as JSON.
9///
10/// The [`TypedMessage`][crate::components::TypedMessage] and [`Status`][crate::components::Status]
11/// structs are the most frequently used kinds of messages.
12pub trait Message {
13    // NOTE: The `print_*` low-level methods functions are doc hidden,
14    //   because they are not considered stable.
15
16    /// Return textual representation of this message.
17    ///
18    /// Default implementation returns empty string, making [`Ui`] skip printing this message.
19    fn text(self) -> String
20    where
21        Self: Sized,
22    {
23        String::new()
24    }
25
26    #[doc(hidden)]
27    fn print_text(self)
28    where
29        Self: Sized,
30    {
31        let text = self.text();
32        if !text.is_empty() {
33            println!("{text}");
34        }
35    }
36
37    /// Serialize this structured message to a serializer which is routed to [`Ui`] output stream.
38    ///
39    /// Default implementation does not serialize anything, making [`Ui`] skip printing
40    /// this message.
41    fn structured<S: Serializer>(self, ser: S) -> Result<S::Ok, S::Error>
42    where
43        Self: Sized,
44    {
45        Self::skip_structured(ser)
46    }
47
48    #[doc(hidden)]
49    fn skip_structured<S: Serializer>(_ser: S) -> Result<S::Ok, S::Error> {
50        Err(serde::ser::Error::custom(JSON_SKIP_MESSAGE))
51    }
52
53    #[doc(hidden)]
54    fn print_json(self)
55    where
56        Self: Sized,
57    {
58        let mut buf = Vec::with_capacity(128);
59        let mut serializer = serde_json::Serializer::new(&mut buf);
60        match self.structured(&mut serializer) {
61            Ok(_) => {
62                let string = unsafe {
63                    // UNSAFE: JSON is always UTF-8 encoded.
64                    String::from_utf8_unchecked(buf)
65                };
66                println!("{string}");
67            }
68            Err(err) => {
69                if err.to_string() != JSON_SKIP_MESSAGE {
70                    panic!("JSON serialization of UI message must not fail: {err}")
71                }
72            }
73        }
74    }
75}
76
77impl Message for &str {
78    fn text(self) -> String {
79        self.to_string()
80    }
81}
82
83impl Message for String {
84    fn text(self) -> String {
85        self
86    }
87}