photonic_interface_cli/
lib.rs1use std::sync::Arc;
2
3use anyhow::Result;
4use palette::rgb::Rgb;
5use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader, BufWriter};
6
7use photonic::attr::Range;
8use photonic::input::{InputSink, Trigger};
9use photonic::interface::Introspection;
10
11pub mod stdio;
12pub mod telnet;
13
14async fn run(i: impl AsyncRead + Unpin, o: impl AsyncWrite + Unpin, introspection: Arc<Introspection>) -> Result<()> {
15 let i = BufReader::new(i);
16 let mut o = BufWriter::new(o);
17
18 let mut lines = i.lines();
19 loop {
20 o.write_all("✨ » ".as_bytes()).await?;
21 o.flush().await?;
22
23 let line = match lines.next_line().await? {
24 Some(line) => line,
25 None => {
26 break;
27 }
28 };
29
30 let line = match shlex::split(&line) {
31 Some(line) => line,
32 None => {
33 o.write_all("Invalid input\n".as_bytes()).await?;
34 continue;
35 }
36 };
37
38 match line.first().map(String::as_str) {
39 Some("exit") => {
40 break;
41 }
42
43 Some("node") => {
44 if let Some(node) = line.get(1) {
45 if let Some(node) = introspection.nodes.get(node) {
46 o.write_all(format!("Node '{}':\n", node.name()).as_bytes()).await?;
47 o.write_all(format!(" Kind: {}\n", node.kind()).as_bytes()).await?;
48 o.write_all(format!(" Nodes: {}\n", node.kind()).as_bytes()).await?;
49 for (name, info) in node.nodes().iter() {
50 o.write_all(format!(" {} = [{}]\n", name, info.kind()).as_bytes()).await?;
51 }
52 o.write_all(format!(" Attributes: {}\n", node.kind()).as_bytes()).await?;
53 for (name, info) in node.attrs().iter() {
54 o.write_all(
55 format!(" {} : {} = [{}]\n", name, info.value_type(), info.kind()).as_bytes(),
56 )
57 .await?;
58 }
61 } else {
62 o.write_all(format!("No such node: '{node}'\n").as_bytes()).await?;
63 }
64 } else {
65 for (name, info) in introspection.nodes.iter() {
66 o.write_all(format!("{} = [{}]\n", name, info.kind()).as_bytes()).await?;
67 }
68 }
69 }
70
71 Some("input") => {
72 if let Some(input) = line.get(1) {
73 if let Some(input) = introspection.inputs.get(input) {
74 if let Some(value) = line.get(2) {
75 let res: Result<()> = (async {
76 match &input.sink() {
77 InputSink::Trigger(sink) => sink.send(Trigger::next()).await,
78 InputSink::Boolean(sink) => sink.send(value.parse()?).await,
79 InputSink::Integer(sink) => sink.send(value.parse()?).await,
80 InputSink::Decimal(sink) => sink.send(value.parse()?).await,
81 InputSink::Color(sink) => {
82 sink.send(value.parse::<Rgb<_, u8>>()?.into_format()).await
83 }
84 InputSink::IntegerRange(sink) => sink.send(value.parse()?).await,
85 InputSink::DecimalRange(sink) => sink.send(value.parse()?).await,
86 InputSink::ColorRange(sink) => {
87 sink.send(value.parse::<Range<Rgb<_, u8>>>()?.map(Rgb::into_format)).await
88 }
89 }
90 })
91 .await;
92
93 match res {
94 Ok(()) => {}
95 Err(err) => {
96 o.write_all(
97 format!("Invalid value: '{}' for {}: {}", value, input.sink(), err).as_bytes(),
98 )
99 .await?;
100 continue;
101 }
102 }
103 } else {
104 o.write_all(format!("Input '{}':\n", input.name()).as_bytes()).await?;
105 o.write_all(format!(" Value: {}\n", input.value_type()).as_bytes()).await?;
106 }
107 } else {
108 o.write_all(format!("No such input: '{input}'\n").as_bytes()).await?;
109 }
110 } else {
111 for (name, info) in introspection.inputs.iter() {
112 o.write_all(format!("{} : {}\n", name, info.value_type()).as_bytes()).await?;
113 }
114 }
115 }
116
117 Some(unknown) => {
118 o.write_all(format!("Unknown command: '{unknown}'\n").as_bytes()).await?;
119 continue;
120 }
121 None => {
122 continue;
123 }
124 }
125 }
126
127 return Ok(());
128}