ssip_client/poll.rs
1// ssip-client -- Speech Dispatcher client in Rust
2// Copyright (c) 2022 Laurent Pelecq
3//
4// Licensed under the Apache License, Version 2.0
5// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
6// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. All files in the project carrying such notice may not be copied,
8// modified, or distributed except according to those terms.
9
10use std::collections::VecDeque;
11use std::io::{Read, Write};
12
13use crate::{
14 client::{Client, Request, Response, Source},
15 types::*,
16};
17
18// Hack to generate the doc. There must be a better way.
19#[cfg(all(not(feature = "async-mio"), doc))]
20mod mio {
21 /// Polls for readiness events on all registered values.
22 ///
23 /// See [`mio::Poll`](https://docs.rs/mio/latest/mio/struct.Poll.html#)
24 #[derive(Debug)]
25 pub struct Poll {}
26
27 /// Source identifier.
28 ///
29 /// See [`mio::Token`](https://docs.rs/mio/latest/mio/struct.Token.html#).
30 #[derive(Debug)]
31 pub struct Token(pub usize);
32}
33
34const INITIAL_REQUEST_QUEUE_CAPACITY: usize = 4;
35
36/// Client with a queue of requests.
37///
38/// The client can be used with crates like [popol](https://crates.io/crates/popol) or
39/// with [mio](https://crates.io/crates/mio) if feature `async-mio` is enabled.
40///
41/// When the output is ready, a next event can be sent.
42#[derive(Debug)]
43pub struct QueuedClient<S: Read + Write + Source> {
44 client: Client<S>,
45 requests: VecDeque<Request>,
46}
47
48impl<S: Read + Write + Source> QueuedClient<S> {
49 /// New asynchronous client build on top of a synchronous client.
50 pub fn new(client: Client<S>) -> Self {
51 Self {
52 client,
53 requests: VecDeque::with_capacity(INITIAL_REQUEST_QUEUE_CAPACITY),
54 }
55 }
56
57 #[cfg(all(not(feature = "async-mio"), unix))]
58 /// Input source.
59 pub fn input_source(&self) -> &S {
60 self.client.input_source()
61 }
62
63 #[cfg(all(not(feature = "async-mio"), unix))]
64 /// Output source.
65 pub fn output_source(&self) -> &S {
66 self.client.output_source()
67 }
68
69 #[cfg(any(feature = "async-mio", doc))]
70 /// Register client
71 pub fn register(
72 &mut self,
73 poll: &mio::Poll,
74 input_token: mio::Token,
75 output_token: mio::Token,
76 ) -> std::io::Result<()> {
77 self.client.register(poll, input_token, output_token)
78 }
79
80 /// Push a new request in the queue.
81 pub fn push(&mut self, request: Request) {
82 self.requests.push_back(request);
83 }
84
85 /// Pop the last request in the queue.
86 pub fn pop(&mut self) -> Option<Request> {
87 self.requests.pop_back()
88 }
89
90 /// Return true if there is a pending request.
91 pub fn has_next(&self) -> bool {
92 !self.requests.is_empty()
93 }
94
95 /// Return true if there is a pending request to send data.
96 ///
97 /// After a `Speak` has been sent, when the client receives `OK_RECEIVING_DATA`, the
98 /// next command must be `Request::SendLine` or `Request::SendLines`.
99 pub fn has_data_next(&self) -> bool {
100 match self.next() {
101 Some(request) => request.is_data(),
102 None => false,
103 }
104 }
105
106 /// Next request in the queue.
107 pub fn next(&self) -> Option<&Request> {
108 self.requests.front()
109 }
110
111 /// Last request in the queue.
112 pub fn last(&self) -> Option<&Request> {
113 self.requests.back()
114 }
115
116 /// Write one pending request if any.
117 ///
118 /// Instance of `mio::Poll` generates a writable event only once until the socket returns `WouldBlock`.
119 /// This error is mapped to `ClientError::NotReady`.
120 pub fn send_next(&mut self) -> ClientResult<bool> {
121 if let Some(request) = self.requests.pop_front() {
122 self.client.send(request)?;
123 Ok(true)
124 } else {
125 Ok(false)
126 }
127 }
128
129 /// Receive one response.
130 ///
131 /// Must be called each time a readable event is returned by `mio::Poll`.
132 pub fn receive_next(&mut self) -> ClientResult<Response> {
133 self.client.receive()
134 }
135}