Add all requests in AsyncClient.

* Simplify fifo tests with tempfile.
* Test getter in async fifo tests.
main
Laurent Pelecq 2 years ago
parent c0f0b69dd8
commit 883e491a7f

@ -23,3 +23,5 @@ async-mio = ["mio/net", "mio/os-poll"]
[dev-dependencies]
libc = "0"
mio = { version = "0.8", features = ["os-poll", "os-ext"] }
tempfile = "3"

@ -14,18 +14,53 @@ use std::io::{self, Read, Write};
use crate::{
client::{Client, ClientError, ClientName, ClientResult},
constants::*,
types::{EventId, SynthesisVoice},
types::*,
};
#[derive(Debug)]
#[derive(Debug, Clone)]
/// Request for SSIP server.
pub enum Request {
SetName(ClientName),
// Speech related requests
Speak,
SendLine(String),
SendLines(Vec<String>),
SendChar(char),
// Flow control
Stop(MessageScope),
Cancel(MessageScope),
Pause(MessageScope),
Resume(MessageScope),
// Setter and getter
SetPriority(Priority),
SetDebug(bool),
SetOutputModule(ClientScope, String),
GetOutputModule,
ListOutputModule,
SetLanguage(ClientScope, String),
GetLanguage,
SetSsmlMode(bool),
SetPunctuationMode(ClientScope, PunctuationMode),
SetSpelling(ClientScope, bool),
SetCapitalLettersRecognitionMode(ClientScope, CapitalLettersRecognitionMode),
SetVoiceType(ClientScope, String),
GetVoiceType,
ListVoiceTypes,
SetSynthesisVoice(ClientScope, String),
ListSynthesisVoices,
SetRate(ClientScope, i8),
GetRate,
SetPitch(ClientScope, i8),
GetPitch,
SetVolume(ClientScope, i8),
GetVolume,
SetPauseContext(ClientScope, u8),
SetHistory(ClientScope, bool),
Begin,
End,
Quit,
EnableNotification(NotificationType),
DisableNotification(NotificationType),
}
#[derive(Debug)]
@ -73,7 +108,7 @@ pub enum Response {
VoicesListSent(Vec<SynthesisVoice>), // 249
OutputModulesListSent(Vec<String>), // 250
GetString(String), // 251
GetInteger(u8), // 251
GetInteger(i8), // 251
InsideBlock, // 260
OutsideBlock, // 261
NotImplemented, // 299
@ -87,12 +122,18 @@ pub enum Response {
const INITIAL_REQUEST_QUEUE_CAPACITY: usize = 4;
enum GetType {
StringType,
IntegerType,
}
/// Asynchronous client based on `mio`.
///
///
pub struct AsyncClient<S: Read + Write + Source> {
client: Client<S>,
requests: VecDeque<Request>,
get_types: VecDeque<GetType>,
}
impl<S: Read + Write + Source> AsyncClient<S> {
@ -101,6 +142,7 @@ impl<S: Read + Write + Source> AsyncClient<S> {
Self {
client,
requests: VecDeque::with_capacity(INITIAL_REQUEST_QUEUE_CAPACITY),
get_types: VecDeque::with_capacity(INITIAL_REQUEST_QUEUE_CAPACITY),
}
}
@ -133,6 +175,16 @@ impl<S: Read + Write + Source> AsyncClient<S> {
!self.requests.is_empty()
}
/// Next get is a string.
fn push_get_string(&mut self) {
self.get_types.push_back(GetType::StringType);
}
/// Next get is an integer.
fn push_get_int(&mut self) {
self.get_types.push_back(GetType::IntegerType);
}
/// Write one pending request if any.
///
/// Instance of `mio::Poll` generates a writable event only once until the socket returns `WouldBlock`.
@ -151,7 +203,67 @@ impl<S: Read + Write + Source> AsyncClient<S> {
.as_slice(),
),
Request::SendChar(ch) => self.client.send_char(ch),
Request::Stop(scope) => self.client.stop(scope),
Request::Cancel(scope) => self.client.cancel(scope),
Request::Pause(scope) => self.client.pause(scope),
Request::Resume(scope) => self.client.resume(scope),
Request::SetPriority(prio) => self.client.set_priority(prio),
Request::SetDebug(value) => self.client.set_debug(value),
Request::SetOutputModule(scope, value) => {
self.client.set_output_module(scope, &value)
}
Request::GetOutputModule => {
self.push_get_string();
self.client.get_output_module()
}
Request::ListOutputModule => self.client.list_output_modules(),
Request::SetLanguage(scope, lang) => self.client.set_language(scope, &lang),
Request::GetLanguage => {
self.push_get_string();
self.client.get_language()
}
Request::SetSsmlMode(value) => self.client.set_ssml_mode(value),
Request::SetPunctuationMode(scope, mode) => {
self.client.set_punctuation_mode(scope, mode)
}
Request::SetSpelling(scope, value) => self.client.set_spelling(scope, value),
Request::SetCapitalLettersRecognitionMode(scope, mode) => {
self.client.set_capital_letter_recogn(scope, mode)
}
Request::SetVoiceType(scope, value) => self.client.set_voice_type(scope, &value),
Request::GetVoiceType => {
self.push_get_string();
self.client.get_voice_type()
}
Request::ListVoiceTypes => self.client.list_voice_types(),
Request::SetSynthesisVoice(scope, value) => {
self.client.set_synthesis_voice(scope, &value)
}
Request::ListSynthesisVoices => self.client.list_synthesis_voices(),
Request::SetRate(scope, value) => self.client.set_rate(scope, value),
Request::GetRate => {
self.push_get_int();
self.client.get_rate()
}
Request::SetPitch(scope, value) => self.client.set_pitch(scope, value),
Request::GetPitch => {
self.push_get_int();
self.client.get_pitch()
}
Request::SetVolume(scope, value) => self.client.set_volume(scope, value),
Request::GetVolume => {
self.push_get_int();
self.client.get_volume()
}
Request::SetPauseContext(scope, value) => {
self.client.set_pause_context(scope, value)
}
Request::SetHistory(scope, value) => self.client.set_history(scope, value),
Request::Quit => self.client.quit(),
Request::Begin => self.client.block_begin(),
Request::End => self.client.block_end(),
Request::EnableNotification(ntype) => self.client.enable_notification(ntype),
Request::DisableNotification(ntype) => self.client.disable_notification(ntype),
}
.map(|_| ()),
None => Ok(()),
@ -225,10 +337,17 @@ impl<S: Read + Write + Source> AsyncClient<S> {
OK_OUTPUT_MODULES_LIST_SENT => Ok(Response::OutputModulesListSent(lines)),
OK_GET => {
let sval = Client::<S>::parse_single_value(&lines)?;
Ok(match sval.parse::<u8>() {
Ok(uval) => Response::GetInteger(uval),
Err(_) => Response::GetString(sval),
})
match self
.get_types
.pop_front()
.expect("internal error: get_types is empty")
{
GetType::StringType => Ok(Response::GetString(sval)),
GetType::IntegerType => sval
.parse::<i8>()
.map(|uval| Response::GetInteger(uval))
.map_err(|_| ClientError::InvalidType),
}
}
OK_INSIDE_BLOCK => Ok(Response::InsideBlock),
OK_OUTSIDE_BLOCK => Ok(Response::OutsideBlock),

@ -63,7 +63,7 @@ pub type ClientResult<T> = Result<T, ClientError>;
pub type ClientStatus = ClientResult<StatusLine>;
/// Client name
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct ClientName {
pub user: String,
pub application: String,
@ -417,6 +417,8 @@ impl<S: Read + Write + Source> Client<S> {
"SET {} VOLUME {}"
);
client_send!(get_volume, "Get the current volume.", "GET VOLUME");
client_send!(
set_pause_context,
"Set the number of (more or less) sentences that should be repeated after a previously paused text is resumed.",
@ -433,8 +435,6 @@ impl<S: Read + Write + Source> Client<S> {
"SET {} HISTORY {}"
);
client_send!(get_volume, "Get the current volume.", "GET VOLUME");
client_send!(block_begin, "Open a block", "BLOCK BEGIN");
client_send!(block_end, "End a block", "BLOCK END");

@ -40,7 +40,6 @@ pub use client::Client;
pub use client::{ClientError, ClientName, ClientResult, ClientStatus};
pub use constants::*;
pub use types::StatusLine;
pub use types::*;
#[cfg(any(feature = "async-mio", doc))]

@ -80,6 +80,7 @@ pub(crate) fn receive_answer(
return Err(invalid_input!("expecting space or dash, got {}.", ch));
}
},
None if line.is_empty() => return Err(invalid_input!("empty line")),
None => return Err(invalid_input!("line too short: {}", line)),
}
}

@ -22,7 +22,7 @@ pub type MessageId = String;
pub type ClientId = String;
/// Message identifiers
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum MessageScope {
/// Last message from current client
Last,
@ -33,7 +33,7 @@ pub enum MessageScope {
}
/// Client identifiers
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum ClientScope {
/// Current client
Current,
@ -44,7 +44,7 @@ pub enum ClientScope {
}
/// Priority
#[derive(StrumDisplay, Debug)]
#[derive(StrumDisplay, Debug, Clone)]
pub enum Priority {
#[strum(serialize = "progress")]
Progress,
@ -59,7 +59,7 @@ pub enum Priority {
}
/// Punctuation mode.
#[derive(StrumDisplay, Debug)]
#[derive(StrumDisplay, Debug, Clone)]
pub enum PunctuationMode {
#[strum(serialize = "none")]
None,
@ -72,7 +72,7 @@ pub enum PunctuationMode {
}
/// Capital letters recognition mode.
#[derive(StrumDisplay, Debug)]
#[derive(StrumDisplay, Debug, Clone)]
pub enum CapitalLettersRecognitionMode {
#[strum(serialize = "none")]
None,
@ -83,7 +83,7 @@ pub enum CapitalLettersRecognitionMode {
}
/// Symbolic key names
#[derive(StrumDisplay, Debug)]
#[derive(StrumDisplay, Debug, Clone)]
pub enum KeyName {
#[strum(serialize = "space")]
Space,
@ -230,7 +230,7 @@ pub enum KeyName {
}
/// Notification type
#[derive(StrumDisplay, Debug)]
#[derive(StrumDisplay, Debug, Clone)]
pub enum NotificationType {
#[strum(serialize = "begin")]
Begin,
@ -249,7 +249,7 @@ pub enum NotificationType {
}
/// Notification event type (returned by server)
#[derive(StrumDisplay, Debug)]
#[derive(StrumDisplay, Debug, Clone)]
pub enum EventType {
Begin,
End,
@ -260,7 +260,7 @@ pub enum EventType {
}
/// Event identifier
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct EventId {
// Message id
pub message: String,
@ -279,7 +279,7 @@ impl EventId {
}
/// Notification event
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct Event {
pub ntype: EventType,
pub id: EventId,

Loading…
Cancel
Save