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] [dev-dependencies]
libc = "0" libc = "0"
mio = { version = "0.8", features = ["os-poll", "os-ext"] } mio = { version = "0.8", features = ["os-poll", "os-ext"] }
tempfile = "3"

@ -14,18 +14,53 @@ use std::io::{self, Read, Write};
use crate::{ use crate::{
client::{Client, ClientError, ClientName, ClientResult}, client::{Client, ClientError, ClientName, ClientResult},
constants::*, constants::*,
types::{EventId, SynthesisVoice}, types::*,
}; };
#[derive(Debug)] #[derive(Debug, Clone)]
/// Request for SSIP server. /// Request for SSIP server.
pub enum Request { pub enum Request {
SetName(ClientName), SetName(ClientName),
// Speech related requests
Speak, Speak,
SendLine(String), SendLine(String),
SendLines(Vec<String>), SendLines(Vec<String>),
SendChar(char), 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, Quit,
EnableNotification(NotificationType),
DisableNotification(NotificationType),
} }
#[derive(Debug)] #[derive(Debug)]
@ -73,7 +108,7 @@ pub enum Response {
VoicesListSent(Vec<SynthesisVoice>), // 249 VoicesListSent(Vec<SynthesisVoice>), // 249
OutputModulesListSent(Vec<String>), // 250 OutputModulesListSent(Vec<String>), // 250
GetString(String), // 251 GetString(String), // 251
GetInteger(u8), // 251 GetInteger(i8), // 251
InsideBlock, // 260 InsideBlock, // 260
OutsideBlock, // 261 OutsideBlock, // 261
NotImplemented, // 299 NotImplemented, // 299
@ -87,12 +122,18 @@ pub enum Response {
const INITIAL_REQUEST_QUEUE_CAPACITY: usize = 4; const INITIAL_REQUEST_QUEUE_CAPACITY: usize = 4;
enum GetType {
StringType,
IntegerType,
}
/// Asynchronous client based on `mio`. /// Asynchronous client based on `mio`.
/// ///
/// ///
pub struct AsyncClient<S: Read + Write + Source> { pub struct AsyncClient<S: Read + Write + Source> {
client: Client<S>, client: Client<S>,
requests: VecDeque<Request>, requests: VecDeque<Request>,
get_types: VecDeque<GetType>,
} }
impl<S: Read + Write + Source> AsyncClient<S> { impl<S: Read + Write + Source> AsyncClient<S> {
@ -101,6 +142,7 @@ impl<S: Read + Write + Source> AsyncClient<S> {
Self { Self {
client, client,
requests: VecDeque::with_capacity(INITIAL_REQUEST_QUEUE_CAPACITY), 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() !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. /// Write one pending request if any.
/// ///
/// Instance of `mio::Poll` generates a writable event only once until the socket returns `WouldBlock`. /// 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(), .as_slice(),
), ),
Request::SendChar(ch) => self.client.send_char(ch), 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::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(|_| ()), .map(|_| ()),
None => Ok(()), None => Ok(()),
@ -225,10 +337,17 @@ impl<S: Read + Write + Source> AsyncClient<S> {
OK_OUTPUT_MODULES_LIST_SENT => Ok(Response::OutputModulesListSent(lines)), OK_OUTPUT_MODULES_LIST_SENT => Ok(Response::OutputModulesListSent(lines)),
OK_GET => { OK_GET => {
let sval = Client::<S>::parse_single_value(&lines)?; let sval = Client::<S>::parse_single_value(&lines)?;
Ok(match sval.parse::<u8>() { match self
Ok(uval) => Response::GetInteger(uval), .get_types
Err(_) => Response::GetString(sval), .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_INSIDE_BLOCK => Ok(Response::InsideBlock),
OK_OUTSIDE_BLOCK => Ok(Response::OutsideBlock), OK_OUTSIDE_BLOCK => Ok(Response::OutsideBlock),

@ -63,7 +63,7 @@ pub type ClientResult<T> = Result<T, ClientError>;
pub type ClientStatus = ClientResult<StatusLine>; pub type ClientStatus = ClientResult<StatusLine>;
/// Client name /// Client name
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct ClientName { pub struct ClientName {
pub user: String, pub user: String,
pub application: String, pub application: String,
@ -417,6 +417,8 @@ impl<S: Read + Write + Source> Client<S> {
"SET {} VOLUME {}" "SET {} VOLUME {}"
); );
client_send!(get_volume, "Get the current volume.", "GET VOLUME");
client_send!( client_send!(
set_pause_context, set_pause_context,
"Set the number of (more or less) sentences that should be repeated after a previously paused text is resumed.", "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 {}" "SET {} HISTORY {}"
); );
client_send!(get_volume, "Get the current volume.", "GET VOLUME");
client_send!(block_begin, "Open a block", "BLOCK BEGIN"); client_send!(block_begin, "Open a block", "BLOCK BEGIN");
client_send!(block_end, "End a block", "BLOCK END"); 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 client::{ClientError, ClientName, ClientResult, ClientStatus};
pub use constants::*; pub use constants::*;
pub use types::StatusLine;
pub use types::*; pub use types::*;
#[cfg(any(feature = "async-mio", doc))] #[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)); 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)), None => return Err(invalid_input!("line too short: {}", line)),
} }
} }

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

Loading…
Cancel
Save