1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
use serde::Serializer;

#[cfg(doc)]
use super::Ui;

const JSON_SKIP_MESSAGE: &str = "UI_INTERNAL_SKIP";

/// A typed object that can be either printed as a human-readable message or serialized as JSON.
///
/// The [`TypedMessage`][crate::components::TypedMessage] and [`Status`][crate::components::Status]
/// structs are the most frequently used kinds of messages.
pub trait Message {
    // NOTE: The `print_*` low-level methods functions are doc hidden,
    //   because they are not considered stable.

    /// Return textual representation of this message.
    ///
    /// Default implementation returns empty string, making [`Ui`] skip printing this message.
    fn text(self) -> String
    where
        Self: Sized,
    {
        String::new()
    }

    #[doc(hidden)]
    fn print_text(self)
    where
        Self: Sized,
    {
        let text = self.text();
        if !text.is_empty() {
            println!("{text}");
        }
    }

    /// Serialize this structured message to a serializer which is routed to [`Ui`] output stream.
    ///
    /// Default implementation does not serialize anything, making [`Ui`] skip printing
    /// this message.
    fn structured<S: Serializer>(self, ser: S) -> Result<S::Ok, S::Error>
    where
        Self: Sized,
    {
        // Silence unused warning without using underscore in variable name,
        // so that it will not be populated by editors.
        let _ = ser;
        Err(serde::ser::Error::custom(JSON_SKIP_MESSAGE))
    }

    #[doc(hidden)]
    fn print_json(self)
    where
        Self: Sized,
    {
        let mut buf = Vec::with_capacity(128);
        let mut serializer = serde_json::Serializer::new(&mut buf);
        match self.structured(&mut serializer) {
            Ok(_) => {
                let string = unsafe {
                    // UNSAFE: JSON is always UTF-8 encoded.
                    String::from_utf8_unchecked(buf)
                };
                println!("{string}");
            }
            Err(err) => {
                if err.to_string() != JSON_SKIP_MESSAGE {
                    panic!("JSON serialization of UI message must not fail: {err}")
                }
            }
        }
    }
}

impl Message for &str {
    fn text(self) -> String {
        self.to_string()
    }
}

impl Message for String {
    fn text(self) -> String {
        self
    }
}