1#![feature(impl_trait_in_assoc_type)]
2
3use std::pin::Pin;
4use std::sync::Arc;
5
6use anyhow::Result;
7use futures::StreamExt;
8use tonic::codegen::tokio_stream::Stream;
9use tonic::transport::Server;
10use tonic::{async_trait, Request, Response, Status};
11
12use photonic::attr::Range;
13use photonic::input::{InputSink, InputValueType, Trigger};
14use photonic::interface::{Interface, Introspection};
15use photonic_interface_grpc_proto::input_value::{ColorRange, DecimalRange, IntegerRange, Rgb};
16use photonic_interface_grpc_proto::interface_server::InterfaceServer;
17use photonic_interface_grpc_proto::{
18 input_value, interface_server, AttrInfoRequest, AttrInfoResponse, InputInfoRequest, InputInfoResponse,
19 InputSendRequest, InputSubscribeRequest, InputValue, InputsResponse, NodeInfoRequest, NodeInfoResponse,
20 NodesResponse,
21};
22
23pub struct GRPC {}
24
25impl GRPC {
26 pub fn new() -> Result<Self> {
27 return Ok(Self {});
28 }
29}
30
31impl Interface for GRPC {
32 async fn listen(self, introspection: Arc<Introspection>) -> Result<()> {
33 let addr = "[::1]:51889".parse().expect("Invalid interface");
34
35 Server::builder()
36 .add_service(InterfaceServer::new(InterfaceImpl {
37 introspection,
38 }))
39 .serve(addr)
40 .await?;
41
42 return Ok(());
43 }
44}
45
46struct InterfaceImpl {
47 introspection: Arc<Introspection>,
48}
49
50#[async_trait]
51impl interface_server::Interface for InterfaceImpl {
52 type InputSubscribeStream = Pin<Box<dyn Stream<Item = Result<InputValue, Status>> + Send + 'static>>;
53
54 async fn nodes(&self, _request: Request<()>) -> Result<Response<NodesResponse>, Status> {
55 let nodes = self.introspection.nodes.keys().cloned().collect();
56
57 return Ok(Response::new(NodesResponse {
58 nodes,
59 }));
60 }
61
62 async fn inputs(&self, _request: Request<()>) -> Result<Response<InputsResponse>, Status> {
63 let inputs = self.introspection.inputs.keys().cloned().collect();
64
65 return Ok(Response::new(InputsResponse {
66 inputs,
67 }));
68 }
69 async fn root(&self, _request: Request<()>) -> Result<Response<NodeInfoResponse>, Status> {
70 let root = &*self.introspection.root;
71
72 return Ok(Response::new(NodeInfoResponse {
73 kind: root.kind().to_string(),
74 name: root.name().to_string(),
75 nodes: root.nodes().iter().map(|(name, info)| (name.clone(), info.name().to_owned())).collect(),
76 attrs: root.attrs().keys().cloned().collect(),
77 }));
78 }
79
80 async fn node(&self, request: Request<NodeInfoRequest>) -> Result<Response<NodeInfoResponse>, Status> {
81 let request = request.get_ref();
82
83 let node = &**self
84 .introspection
85 .nodes
86 .get(&request.name)
87 .ok_or_else(|| Status::not_found(format!("No such node: {}", request.name)))?;
88
89 return Ok(Response::new(NodeInfoResponse {
90 kind: node.kind().to_string(),
91 name: node.name().to_string(),
92 nodes: node.nodes().iter().map(|(name, info)| (name.clone(), info.name().to_owned())).collect(),
93 attrs: node.attrs().keys().cloned().collect(),
94 }));
95 }
96
97 async fn attr(&self, request: Request<AttrInfoRequest>) -> Result<Response<AttrInfoResponse>, Status> {
98 let request = request.get_ref();
99
100 let attr_ref = request.name.as_ref().ok_or_else(|| Status::invalid_argument("Value missing: attr"))?;
101
102 let node = &**self
103 .introspection
104 .nodes
105 .get(&attr_ref.node)
106 .ok_or_else(|| Status::not_found(format!("No such node: {}", attr_ref.node)))?;
107
108 let attr = node.find_attr(attr_ref.path.iter()).ok_or_else(|| {
109 Status::not_found(format!("No such attribute: {}/{}", attr_ref.node, attr_ref.path.join("/")))
110 })?;
111
112 return Ok(Response::new(AttrInfoResponse {
113 attr: Some(attr_ref.clone()),
114 kind: attr.kind().to_string(),
115 value_type: attr.value_type().to_string(),
116 attrs: attr.attrs().keys().cloned().collect(),
117 inputs: attr.inputs().iter().map(|(name, info)| (name.clone(), info.name().to_owned())).collect(),
118 }));
119 }
120
121 async fn input(&self, request: Request<InputInfoRequest>) -> Result<Response<InputInfoResponse>, Status> {
122 let request = request.get_ref();
123
124 let input = &**self
125 .introspection
126 .inputs
127 .get(&request.name)
128 .ok_or_else(|| Status::not_found(format!("No such input: {}", request.name)))?;
129
130 return Ok(Response::new(InputInfoResponse {
131 name: input.name().to_string(),
132 value_type: match input.value_type() {
133 InputValueType::Trigger => photonic_interface_grpc_proto::InputValueType::Trigger,
134 InputValueType::Boolean => photonic_interface_grpc_proto::InputValueType::Bool,
135 InputValueType::Integer => photonic_interface_grpc_proto::InputValueType::Integer,
136 InputValueType::Decimal => photonic_interface_grpc_proto::InputValueType::Decimal,
137 InputValueType::Color => photonic_interface_grpc_proto::InputValueType::Color,
138 InputValueType::IntegerRange => photonic_interface_grpc_proto::InputValueType::IntegerRange,
139 InputValueType::DecimalRange => photonic_interface_grpc_proto::InputValueType::DecimalRange,
140 InputValueType::ColorRange => photonic_interface_grpc_proto::InputValueType::ColorRange,
141 }
142 .into(),
143 }));
144 }
145
146 async fn input_send(&self, request: Request<InputSendRequest>) -> Result<Response<()>, Status> {
147 let request = request.get_ref();
148
149 let input = &**self
150 .introspection
151 .inputs
152 .get(&request.name)
153 .ok_or_else(|| Status::not_found(format!("No such input: {}", request.name)))?;
154
155 macro_rules! match_value {
156 ($pattern:ident) => {
157 match request
158 .value
159 .as_ref()
160 .ok_or(Status::invalid_argument("Value missing"))?
161 .value
162 .as_ref()
163 .ok_or(Status::invalid_argument("Value missing"))?
164 {
165 input_value::Value::$pattern(ref value) => value,
166 _ => return Err(Status::invalid_argument("Value of wrong type")),
167 }
168 };
169 }
170
171 match &input.sink() {
172 InputSink::Trigger(sink) => {
173 match_value!(Trigger);
174 sink.send(Trigger::next()).await
175 }
176
177 InputSink::Boolean(sink) => {
178 let value = match_value!(Bool);
179 sink.send(*value).await
180 }
181
182 InputSink::Integer(sink) => {
183 let value = match_value!(Integer);
184 sink.send(*value).await
185 }
186
187 InputSink::Decimal(sink) => {
188 let value = match_value!(Decimal);
189 sink.send(*value).await
190 }
191
192 InputSink::Color(sink) => {
193 let value = match_value!(Color);
194 let value = palette::Srgb::new(value.r, value.g, value.b);
195 sink.send(value).await
196 }
197
198 InputSink::IntegerRange(sink) => {
199 let value = match_value!(IntegerRange);
200 let value = Range::new(value.a, value.b);
201 sink.send(value).await
202 }
203
204 InputSink::DecimalRange(sink) => {
205 let value = match_value!(DecimalRange);
206 let value = Range::new(value.a, value.b);
207 sink.send(value).await
208 }
209
210 InputSink::ColorRange(sink) => {
211 let value = match_value!(ColorRange);
212 let value_a = value.a.as_ref().ok_or(Status::invalid_argument("Value missing"))?;
213 let value_b = value.b.as_ref().ok_or(Status::invalid_argument("Value missing"))?;
214 let value = Range::new(
215 palette::Srgb::new(value_a.r, value_a.g, value_a.b),
216 palette::Srgb::new(value_b.r, value_b.g, value_b.b),
217 );
218 sink.send(value).await
219 }
220 }
221 .map_err(|err| Status::invalid_argument(format!("Invalid value: {err}")))?;
222
223 return Ok(Response::new(()));
224 }
225
226 async fn input_subscribe(
227 &self,
228 request: Request<InputSubscribeRequest>,
229 ) -> Result<Response<Self::InputSubscribeStream>, Status> {
230 let request = request.get_ref();
231
232 let input = &**self
233 .introspection
234 .inputs
235 .get(&request.name)
236 .ok_or_else(|| Status::not_found(format!("No such input: {}", request.name)))?;
237
238 let stream: Pin<Box<dyn Stream<Item = Result<_, _>> + Send + 'static>> = match &input.sink() {
239 InputSink::Trigger(sink) => Box::pin(sink.subscribe().map(|_| {
240 Ok(InputValue {
241 value: Some(input_value::Value::Trigger(())),
242 })
243 })),
244
245 InputSink::Boolean(sink) => Box::pin(sink.subscribe().map(|value| {
246 Ok(InputValue {
247 value: Some(input_value::Value::Bool(value)),
248 })
249 })),
250
251 InputSink::Integer(sink) => Box::pin(sink.subscribe().map(|value| {
252 Ok(InputValue {
253 value: Some(input_value::Value::Integer(value)),
254 })
255 })),
256
257 InputSink::Decimal(sink) => Box::pin(sink.subscribe().map(|value| {
258 Ok(InputValue {
259 value: Some(input_value::Value::Decimal(value)),
260 })
261 })),
262
263 InputSink::Color(sink) => Box::pin(sink.subscribe().map(|value| {
264 Ok(InputValue {
265 value: Some(input_value::Value::Color(Rgb {
266 r: value.red,
267 g: value.green,
268 b: value.blue,
269 })),
270 })
271 })),
272
273 InputSink::IntegerRange(sink) => Box::pin(sink.subscribe().map(|value| {
274 Ok(InputValue {
275 value: Some(input_value::Value::IntegerRange(IntegerRange {
276 a: value.0,
277 b: value.1,
278 })),
279 })
280 })),
281
282 InputSink::DecimalRange(sink) => Box::pin(sink.subscribe().map(|value| {
283 Ok(InputValue {
284 value: Some(input_value::Value::DecimalRange(DecimalRange {
285 a: value.0,
286 b: value.1,
287 })),
288 })
289 })),
290
291 InputSink::ColorRange(sink) => Box::pin(sink.subscribe().map(|value| {
292 Ok(InputValue {
293 value: Some(input_value::Value::ColorRange(ColorRange {
294 a: Some(Rgb {
295 r: value.0.red,
296 g: value.0.green,
297 b: value.0.blue,
298 }),
299 b: Some(Rgb {
300 r: value.1.red,
301 g: value.1.green,
302 b: value.1.blue,
303 }),
304 })),
305 })
306 })),
307 };
308
309 return Ok(Response::new(stream));
310 }
311}