Skip to main content

sofie_core/
show.rs

1use std::fmt::Display;
2
3use serde_json::{Map, Number};
4
5use crate::{Instant, Timestamp, Uuid, Value};
6
7#[derive(Clone, Copy)]
8pub enum Shown<'a> {
9    Null,
10    Bool(bool),
11    Integer(i64),
12    Float(f64),
13    Number(&'a Number),
14    Text(&'a str),
15    /// Hex digits and hyphens only, so a page writes it without escaping or percent-encoding.
16    Uuid(Uuid),
17    /// Typed, so a screen tells it from text written like one.
18    Instant(Instant),
19    Formatted(&'a dyn Display),
20    List(&'a dyn Items),
21    Record(&'a dyn Fields),
22}
23
24pub trait Show {
25    fn show(&self) -> Shown<'_>;
26}
27
28pub trait Items {
29    fn item(&self, index: usize) -> Option<Shown<'_>>;
30}
31
32/// Named and ordered as the type's JSON; `#[derive(ReadModel)]` writes them for a read model.
33pub trait Fields {
34    fn name(&self, index: usize) -> Option<&str>;
35
36    fn get(&self, name: &str) -> Option<Shown<'_>>;
37
38    /// Overridden where `name(index)` walks from the start, so a walk over all fields stays linear.
39    fn names(&self) -> Box<dyn Iterator<Item = &str> + '_> {
40        Box::new((0..).map_while(|index| self.name(index)))
41    }
42}
43
44impl<V: Value> Show for V
45where
46    V::Inner: Show,
47{
48    fn show(&self) -> Shown<'_> {
49        self.inner().show()
50    }
51}
52
53impl Show for serde_json::Value {
54    fn show(&self) -> Shown<'_> {
55        match self {
56            Self::Null => Shown::Null,
57            Self::Bool(flag) => Shown::Bool(*flag),
58            Self::Number(number) => Shown::Number(number),
59            Self::String(text) => Shown::Text(text),
60            Self::Array(items) => Shown::List(items),
61            Self::Object(fields) => Shown::Record(fields),
62        }
63    }
64}
65
66impl Fields for Map<String, serde_json::Value> {
67    fn name(&self, index: usize) -> Option<&str> {
68        self.keys().nth(index).map(String::as_str)
69    }
70
71    fn get(&self, name: &str) -> Option<Shown<'_>> {
72        self.get(name).map(Show::show)
73    }
74
75    fn names(&self) -> Box<dyn Iterator<Item = &str> + '_> {
76        Box::new(self.keys().map(String::as_str))
77    }
78}
79
80impl<T: Show> Items for Vec<T> {
81    fn item(&self, index: usize) -> Option<Shown<'_>> {
82        self.get(index).map(Show::show)
83    }
84}
85
86impl<T: Show> Show for Vec<T> {
87    fn show(&self) -> Shown<'_> {
88        Shown::List(self)
89    }
90}
91
92impl<T: Show> Show for Option<T> {
93    fn show(&self) -> Shown<'_> {
94        self.as_ref().map_or(Shown::Null, Show::show)
95    }
96}
97
98macro_rules! show {
99    ($($type:ty => |$value:ident| $shown:expr),* $(,)?) => {
100        $(impl Show for $type { fn show(&self) -> Shown<'_> { let $value = self; $shown } })*
101    };
102}
103
104show! {
105    bool => |flag| Shown::Bool(*flag), String => |text| Shown::Text(text),
106    i64 => |number| Shown::Integer(*number), i32 => |number| Shown::Integer(i64::from(*number)),
107    u64 => |number| i64::try_from(*number).map_or(Shown::Formatted(number), Shown::Integer),
108    f64 => |number| Shown::Float(*number), Uuid => |uuid| Shown::Uuid(*uuid),
109    Timestamp => |timestamp| Shown::Instant(Instant::from(*timestamp)), jiff::civil::Date => |day| Shown::Formatted(day),
110}