1use std::pin::Pin;
2
3use anyhow::Result;
4use futures::{Stream, StreamExt};
5use palette::rgb::Rgb;
6use tokio::sync::{broadcast, mpsc, oneshot};
7use tokio_stream::wrappers::BroadcastStream;
8
9use crate::attr::Range;
10use crate::input::trigger::Trigger;
11
12use super::{InputValue, UpdateRequest};
13
14#[derive(Debug)]
15pub struct Sink<V>
16where V: InputValue
17{
18 pub(super) update_tx: mpsc::Sender<UpdateRequest<V>>,
19 pub(super) value_rx: broadcast::Receiver<V>,
20}
21
22impl<V> Sink<V>
23where V: InputValue + Sync
24{
25 pub async fn send(&self, next: V) -> Result<()> {
26 let (responder_tx, responder_rx) = oneshot::channel();
27
28 self.update_tx
29 .send(UpdateRequest {
30 value: next,
31 responder: responder_tx,
32 })
33 .await?;
34
35 return responder_rx.await?;
36 }
37
38 pub fn subscribe(&self) -> impl Stream<Item = V> + Send {
39 return BroadcastStream::new(self.value_rx.resubscribe()).filter_map(|r| async { r.ok() });
40 }
42}
43
44pub enum InputSink {
45 Trigger(Sink<Trigger>),
46 Boolean(Sink<bool>),
47 Integer(Sink<i64>),
48 Decimal(Sink<f32>),
49 Color(Sink<Rgb>),
50 IntegerRange(Sink<Range<i64>>),
51 DecimalRange(Sink<Range<f32>>),
52 ColorRange(Sink<Range<Rgb>>),
53}
54
55impl std::fmt::Debug for InputSink {
56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 return std::fmt::Display::fmt(self, f);
58 }
59}
60
61impl std::fmt::Display for InputSink {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 return f.write_str(match self {
64 Self::Trigger(_) => "trigger",
65 Self::Boolean(_) => "boolean",
66 Self::Integer(_) => "integer",
67 Self::Decimal(_) => "decimal",
68 Self::Color(_) => "color",
69 Self::IntegerRange(_) => "range<integer>",
70 Self::DecimalRange(_) => "range<decimal>",
71 Self::ColorRange(_) => "range<color>",
72 });
73 }
74}
75
76impl<V> From<Sink<V>> for InputSink
77where V: InputValue
78{
79 fn from(sink: Sink<V>) -> Self {
80 return V::sink(sink);
81 }
82}
83
84impl InputSink {
85 pub fn subscribe(&self) -> impl Stream<Item = AnyInputValue> + Send + Unpin {
86 let result: Pin<Box<dyn Stream<Item = _> + Send>> = match self {
87 InputSink::Trigger(sink) => Box::pin(sink.subscribe().map(AnyInputValue::from)),
88 InputSink::Boolean(sink) => Box::pin(sink.subscribe().map(AnyInputValue::from)),
89 InputSink::Integer(sink) => Box::pin(sink.subscribe().map(AnyInputValue::from)),
90 InputSink::Decimal(sink) => Box::pin(sink.subscribe().map(AnyInputValue::from)),
91 InputSink::Color(sink) => Box::pin(sink.subscribe().map(AnyInputValue::from)),
92 InputSink::IntegerRange(sink) => Box::pin(sink.subscribe().map(AnyInputValue::from)),
93 InputSink::DecimalRange(sink) => Box::pin(sink.subscribe().map(AnyInputValue::from)),
94 InputSink::ColorRange(sink) => Box::pin(sink.subscribe().map(AnyInputValue::from)),
95 };
96
97 return result;
98 }
99}
100
101#[derive(Debug, Copy, Clone)]
102pub enum AnyInputValue {
103 Trigger,
104 Boolean(bool),
105 Integer(i64),
106 Decimal(f32),
107 Color(Rgb),
108 IntegerRange(Range<i64>),
109 DecimalRange(Range<f32>),
110 ColorRange(Range<Rgb>),
111}
112
113impl From<Trigger> for AnyInputValue {
114 fn from(_: Trigger) -> Self {
115 return Self::Trigger;
116 }
117}
118
119impl From<bool> for AnyInputValue {
120 fn from(value: bool) -> Self {
121 return Self::Boolean(value);
122 }
123}
124
125impl From<i64> for AnyInputValue {
126 fn from(value: i64) -> Self {
127 return Self::Integer(value);
128 }
129}
130
131impl From<f32> for AnyInputValue {
132 fn from(value: f32) -> Self {
133 return Self::Decimal(value);
134 }
135}
136
137impl From<Rgb> for AnyInputValue {
138 fn from(value: Rgb) -> Self {
139 return Self::Color(value);
140 }
141}
142
143impl From<Range<i64>> for AnyInputValue {
144 fn from(value: Range<i64>) -> Self {
145 return Self::IntegerRange(value);
146 }
147}
148
149impl From<Range<f32>> for AnyInputValue {
150 fn from(value: Range<f32>) -> Self {
151 return Self::DecimalRange(value);
152 }
153}
154
155impl From<Range<Rgb>> for AnyInputValue {
156 fn from(value: Range<Rgb>) -> Self {
157 return Self::ColorRange(value);
158 }
159}