Skip to main content

trellis_core/
output_payload.rs

1use core::any::Any;
2use core::fmt;
3
4pub(crate) trait StoredOutput: Any + Send + Sync {
5    fn clone_box(&self) -> Box<dyn StoredOutput>;
6    fn equals(&self, other: &dyn StoredOutput) -> bool;
7    fn as_any(&self) -> &dyn Any;
8    fn type_name(&self) -> &'static str;
9}
10
11impl Clone for Box<dyn StoredOutput> {
12    fn clone(&self) -> Self {
13        self.clone_box()
14    }
15}
16
17#[derive(Clone)]
18pub(crate) struct OutputValue<T> {
19    value: T,
20}
21
22impl<T> OutputValue<T> {
23    pub(crate) fn new(value: T) -> Self {
24        Self { value }
25    }
26
27    pub(crate) fn get(&self) -> &T {
28        &self.value
29    }
30}
31
32impl<T> StoredOutput for OutputValue<T>
33where
34    T: Clone + PartialEq + Send + Sync + 'static,
35{
36    fn clone_box(&self) -> Box<dyn StoredOutput> {
37        Box::new(self.clone())
38    }
39
40    fn equals(&self, other: &dyn StoredOutput) -> bool {
41        other
42            .as_any()
43            .downcast_ref::<OutputValue<T>>()
44            .is_some_and(|other| self.value == other.value)
45    }
46
47    fn as_any(&self) -> &dyn Any {
48        self
49    }
50
51    fn type_name(&self) -> &'static str {
52        core::any::type_name::<T>()
53    }
54}
55
56/// Type-erased materialized output payload carried by an output frame.
57#[derive(Clone)]
58pub struct OutputPayload {
59    value: Box<dyn StoredOutput>,
60}
61
62impl OutputPayload {
63    /// Creates an erased output payload from a typed value.
64    pub fn new<T>(value: T) -> Self
65    where
66        T: Clone + PartialEq + Send + Sync + 'static,
67    {
68        Self {
69            value: Box::new(OutputValue::new(value)),
70        }
71    }
72
73    pub(crate) fn from_stored(value: Box<dyn StoredOutput>) -> Self {
74        Self { value }
75    }
76
77    /// Returns this payload as the requested type, if it matches.
78    pub fn get<T>(&self) -> Option<&T>
79    where
80        T: Clone + PartialEq + Send + Sync + 'static,
81    {
82        self.value
83            .as_any()
84            .downcast_ref::<OutputValue<T>>()
85            .map(OutputValue::get)
86    }
87
88    /// Returns the erased Rust type name carried by this payload.
89    pub fn type_name(&self) -> &'static str {
90        self.value.type_name()
91    }
92}
93
94impl fmt::Debug for OutputPayload {
95    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96        f.debug_struct("OutputPayload")
97            .field("type_name", &self.type_name())
98            .finish_non_exhaustive()
99    }
100}
101
102impl PartialEq for OutputPayload {
103    fn eq(&self, other: &Self) -> bool {
104        self.value.equals(other.value.as_ref())
105    }
106}
107
108pub(crate) fn boxed_output<T>(value: T) -> Box<dyn StoredOutput>
109where
110    T: Clone + PartialEq + Send + Sync + 'static,
111{
112    Box::new(OutputValue::new(value))
113}