lan_mouse_cli/
lib.rs

1use futures::StreamExt;
2use tokio::{
3    io::{AsyncBufReadExt, BufReader},
4    task::LocalSet,
5};
6
7use std::io::{self, Write};
8
9use self::command::{Command, CommandType};
10
11use lan_mouse_ipc::{
12    AsyncFrontendEventReader, AsyncFrontendRequestWriter, ClientConfig, ClientHandle, ClientState,
13    FrontendEvent, FrontendRequest, IpcError, DEFAULT_PORT,
14};
15
16mod command;
17
18pub fn run() -> Result<(), IpcError> {
19    let runtime = tokio::runtime::Builder::new_current_thread()
20        .enable_io()
21        .enable_time()
22        .build()?;
23    runtime.block_on(LocalSet::new().run_until(async move {
24        let (rx, tx) = lan_mouse_ipc::connect_async().await?;
25        let mut cli = Cli::new(rx, tx);
26        cli.run().await
27    }))?;
28    Ok(())
29}
30
31struct Cli {
32    clients: Vec<(ClientHandle, ClientConfig, ClientState)>,
33    changed: Option<ClientHandle>,
34    rx: AsyncFrontendEventReader,
35    tx: AsyncFrontendRequestWriter,
36}
37
38impl Cli {
39    fn new(rx: AsyncFrontendEventReader, tx: AsyncFrontendRequestWriter) -> Cli {
40        Self {
41            clients: vec![],
42            changed: None,
43            rx,
44            tx,
45        }
46    }
47
48    async fn run(&mut self) -> Result<(), IpcError> {
49        let stdin = tokio::io::stdin();
50        let stdin = BufReader::new(stdin);
51        let mut stdin = stdin.lines();
52
53        /* initial state sync */
54        self.clients = loop {
55            match self.rx.next().await {
56                Some(Ok(e)) => {
57                    if let FrontendEvent::Enumerate(clients) = e {
58                        break clients;
59                    }
60                }
61                Some(Err(e)) => return Err(e),
62                None => return Ok(()),
63            }
64        };
65
66        loop {
67            prompt()?;
68            tokio::select! {
69                line = stdin.next_line() => {
70                    let Some(line) = line? else {
71                        break Ok(());
72                    };
73                    let cmd: Command = match line.parse() {
74                        Ok(cmd) => cmd,
75                        Err(e) => {
76                            eprintln!("{e}");
77                            continue;
78                        }
79                    };
80                    self.execute(cmd).await?;
81                }
82                event = self.rx.next() => {
83                    if let Some(event) = event {
84                        self.handle_event(event?);
85                    } else {
86                        break Ok(());
87                    }
88                }
89            }
90            if let Some(handle) = self.changed.take() {
91                self.update_client(handle).await?;
92            }
93        }
94    }
95
96    async fn update_client(&mut self, handle: ClientHandle) -> Result<(), IpcError> {
97        self.tx.request(FrontendRequest::GetState(handle)).await?;
98        while let Some(Ok(event)) = self.rx.next().await {
99            self.handle_event(event.clone());
100            if let FrontendEvent::State(_, _, _) | FrontendEvent::NoSuchClient(_) = event {
101                break;
102            }
103        }
104        Ok(())
105    }
106
107    async fn execute(&mut self, cmd: Command) -> Result<(), IpcError> {
108        match cmd {
109            Command::None => {}
110            Command::Connect(pos, host, port) => {
111                let request = FrontendRequest::Create;
112                self.tx.request(request).await?;
113                let handle = loop {
114                    if let Some(Ok(event)) = self.rx.next().await {
115                        match event {
116                            FrontendEvent::Created(h, c, s) => {
117                                self.clients.push((h, c, s));
118                                break h;
119                            }
120                            _ => {
121                                self.handle_event(event);
122                                continue;
123                            }
124                        }
125                    }
126                };
127                for request in [
128                    FrontendRequest::UpdateHostname(handle, Some(host.clone())),
129                    FrontendRequest::UpdatePort(handle, port.unwrap_or(DEFAULT_PORT)),
130                    FrontendRequest::UpdatePosition(handle, pos),
131                ] {
132                    self.tx.request(request).await?;
133                }
134                self.update_client(handle).await?;
135            }
136            Command::Disconnect(id) => {
137                self.tx.request(FrontendRequest::Delete(id)).await?;
138                loop {
139                    if let Some(Ok(event)) = self.rx.next().await {
140                        self.handle_event(event.clone());
141                        if let FrontendEvent::Deleted(_) = event {
142                            self.handle_event(event);
143                            break;
144                        }
145                    }
146                }
147            }
148            Command::Activate(id) => {
149                self.tx.request(FrontendRequest::Activate(id, true)).await?;
150                self.update_client(id).await?;
151            }
152            Command::Deactivate(id) => {
153                self.tx
154                    .request(FrontendRequest::Activate(id, false))
155                    .await?;
156                self.update_client(id).await?;
157            }
158            Command::List => {
159                self.tx.request(FrontendRequest::Enumerate()).await?;
160                while let Some(e) = self.rx.next().await {
161                    let event = e?;
162                    self.handle_event(event.clone());
163                    if let FrontendEvent::Enumerate(_) = event {
164                        break;
165                    }
166                }
167            }
168            Command::SetHost(handle, host) => {
169                let request = FrontendRequest::UpdateHostname(handle, Some(host.clone()));
170                self.tx.request(request).await?;
171                self.update_client(handle).await?;
172            }
173            Command::SetPort(handle, port) => {
174                let request = FrontendRequest::UpdatePort(handle, port.unwrap_or(DEFAULT_PORT));
175                self.tx.request(request).await?;
176                self.update_client(handle).await?;
177            }
178            Command::Help => {
179                for cmd_type in [
180                    CommandType::List,
181                    CommandType::Connect,
182                    CommandType::Disconnect,
183                    CommandType::Activate,
184                    CommandType::Deactivate,
185                    CommandType::SetHost,
186                    CommandType::SetPort,
187                ] {
188                    eprintln!("{}", cmd_type.usage());
189                }
190            }
191        }
192        Ok(())
193    }
194
195    fn find_mut(
196        &mut self,
197        handle: ClientHandle,
198    ) -> Option<&mut (ClientHandle, ClientConfig, ClientState)> {
199        self.clients.iter_mut().find(|(h, _, _)| *h == handle)
200    }
201
202    fn remove(
203        &mut self,
204        handle: ClientHandle,
205    ) -> Option<(ClientHandle, ClientConfig, ClientState)> {
206        let idx = self.clients.iter().position(|(h, _, _)| *h == handle);
207        idx.map(|i| self.clients.swap_remove(i))
208    }
209
210    fn handle_event(&mut self, event: FrontendEvent) {
211        match event {
212            FrontendEvent::Changed(h) => self.changed = Some(h),
213            FrontendEvent::Created(h, c, s) => {
214                eprint!("client added ({h}): ");
215                print_config(&c);
216                eprint!(" ");
217                print_state(&s);
218                eprintln!();
219                self.clients.push((h, c, s));
220            }
221            FrontendEvent::NoSuchClient(h) => {
222                eprintln!("no such client: {h}");
223            }
224            FrontendEvent::State(h, c, s) => {
225                if let Some((_, config, state)) = self.find_mut(h) {
226                    let old_host = config.hostname.clone().unwrap_or("\"\"".into());
227                    let new_host = c.hostname.clone().unwrap_or("\"\"".into());
228                    if old_host != new_host {
229                        eprintln!(
230                            "client {h}: hostname updated ({} -> {})",
231                            old_host, new_host
232                        );
233                    }
234                    if config.port != c.port {
235                        eprintln!("client {h} changed port: {} -> {}", config.port, c.port);
236                    }
237                    if config.fix_ips != c.fix_ips {
238                        eprintln!("client {h} ips updated: {:?}", c.fix_ips)
239                    }
240                    *config = c;
241                    if state.active ^ s.active {
242                        eprintln!(
243                            "client {h} {}",
244                            if s.active { "activated" } else { "deactivated" }
245                        );
246                    }
247                    *state = s;
248                }
249            }
250            FrontendEvent::Deleted(h) => {
251                if let Some((h, c, _)) = self.remove(h) {
252                    eprint!("client {h} removed (");
253                    print_config(&c);
254                    eprintln!(")");
255                }
256            }
257            FrontendEvent::PortChanged(p, e) => {
258                if let Some(e) = e {
259                    eprintln!("failed to change port: {e}");
260                } else {
261                    eprintln!("changed port to {p}");
262                }
263            }
264            FrontendEvent::Enumerate(clients) => {
265                self.clients = clients;
266                self.print_clients();
267            }
268            FrontendEvent::Error(e) => {
269                eprintln!("ERROR: {e}");
270            }
271            FrontendEvent::CaptureStatus(s) => {
272                eprintln!("capture status: {s:?}")
273            }
274            FrontendEvent::EmulationStatus(s) => {
275                eprintln!("emulation status: {s:?}")
276            }
277        }
278    }
279
280    fn print_clients(&mut self) {
281        for (h, c, s) in self.clients.iter() {
282            eprint!("client {h}: ");
283            print_config(c);
284            eprint!(" ");
285            print_state(s);
286            eprintln!();
287        }
288    }
289}
290
291fn prompt() -> io::Result<()> {
292    eprint!("lan-mouse > ");
293    std::io::stderr().flush()?;
294    Ok(())
295}
296
297fn print_config(c: &ClientConfig) {
298    eprint!(
299        "{}:{} ({}), ips: {:?}",
300        c.hostname.clone().unwrap_or("(no hostname)".into()),
301        c.port,
302        c.pos,
303        c.fix_ips
304    );
305}
306
307fn print_state(s: &ClientState) {
308    eprint!("active: {}, dns: {:?}", s.active, s.ips);
309}