Skip to main content

clawless_tui/projection/
entry.rs

1use std::fmt;
2use std::sync::Arc;
3
4use clawless_core::event::Artifact;
5
6/// User-facing representation of a single event
7///
8/// `Entry` is the projection's public vocabulary for events. TUI consumers query the projection
9/// and receive entries, never raw events. Each variant carries the same data as its corresponding
10/// [`Event`] variant but uses [`Arc`] for artifact values so that entries can be cheaply cloned
11/// when returned from queries.
12///
13/// [`Event`]: clawless_core::event::Event
14// r[impl projection.entry.message]
15// r[impl projection.entry.detail]
16// r[impl projection.entry.artifact]
17#[derive(Clone, Debug)]
18pub enum Entry {
19    /// Informational message
20    Message(String),
21    /// Supplementary detail
22    Detail(String),
23    /// Primary command output
24    Artifact(Arc<dyn Artifact>),
25}
26
27impl fmt::Display for Entry {
28    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29        match self {
30            Entry::Message(text) => f.write_str(text),
31            Entry::Detail(text) => f.write_str(text),
32            Entry::Artifact(artifact) => fmt::Display::fmt(artifact, f),
33        }
34    }
35}
36
37#[cfg(test)]
38mod tests {
39    // An assertion in a test panics by design. A `# Panics` section on every test
40    // would repeat that and give the reader no information.
41    #![allow(clippy::missing_panics_doc)]
42
43    use std::sync::Arc;
44
45    use serde::Serialize;
46
47    use super::*;
48
49    #[derive(Clone, Debug, Serialize)]
50    struct TestArtifact {
51        value: String,
52    }
53
54    impl fmt::Display for TestArtifact {
55        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56            write!(f, "{}", self.value)
57        }
58    }
59
60    fn test_artifact() -> TestArtifact {
61        TestArtifact {
62            value: "result".to_string(),
63        }
64    }
65
66    #[test]
67    fn clone_detail_produces_equal_value() {
68        let entry = Entry::Detail("info".to_string());
69
70        let cloned = entry.clone();
71
72        let Entry::Detail(s) = &cloned else {
73            panic!("expected Entry::Detail");
74        };
75        assert_eq!(s, "info");
76    }
77
78    #[test]
79    fn clone_message_produces_equal_value() {
80        let entry = Entry::Message("hello".to_string());
81
82        let cloned = entry.clone();
83
84        let Entry::Message(s) = &cloned else {
85            panic!("expected Entry::Message");
86        };
87        assert_eq!(s, "hello");
88    }
89
90    #[test]
91    fn display_artifact_renders_via_display_trait() {
92        let entry = Entry::Artifact(Arc::new(test_artifact()));
93
94        let text = entry.to_string();
95
96        assert_eq!(text, "result");
97    }
98
99    #[test]
100    fn display_detail_renders_text() {
101        let entry = Entry::Detail("info".to_string());
102
103        let text = entry.to_string();
104
105        assert_eq!(text, "info");
106    }
107
108    #[test]
109    fn display_message_renders_text() {
110        let entry = Entry::Message("hello".to_string());
111
112        let text = entry.to_string();
113
114        assert_eq!(text, "hello");
115    }
116
117    #[test]
118    fn trait_send() {
119        fn assert_send<T: Send>() {}
120        assert_send::<Entry>();
121    }
122
123    #[test]
124    fn trait_sync() {
125        fn assert_sync<T: Sync>() {}
126        assert_sync::<Entry>();
127    }
128
129    #[test]
130    fn trait_unpin() {
131        fn assert_unpin<T: Unpin>() {}
132        assert_unpin::<Entry>();
133    }
134}