Fix response type and remove errors that can be mapped to io::Error.

main
Laurent Pelecq 2 years ago
parent 9e4b09d96d
commit 4528d9c407

@ -11,7 +11,8 @@ use std::io::{self, Read, Write};
use crate::constants::*;
use crate::protocol::{
flush_lines, parse_event_id, parse_single_value, parse_typed_lines, write_lines,
flush_lines, parse_event_id, parse_single_integer, parse_single_value, parse_typed_lines,
write_lines,
};
use crate::types::*;
@ -132,7 +133,7 @@ pub enum Response {
HistoryLastMsg(String), // 242
HistoryCurPosRet(String), // 243
TableListSent(Vec<String>), // 244
HistoryClientIdSent(String), // 245
HistoryClientIdSent(ClientId), // 245
MessageTextSent, // 246
HelpSent(Vec<String>), // 248
VoicesListSent(Vec<SynthesisVoice>), // 249
@ -653,7 +654,7 @@ impl<S: Read + Write + Source> Client<S> {
OK_LAST_MSG => Ok(Response::HistoryLastMsg(parse_single_value(&lines)?)),
OK_CUR_POS_RET => Ok(Response::HistoryCurPosRet(parse_single_value(&lines)?)),
OK_TABLE_LIST_SENT => Ok(Response::TableListSent(lines)),
OK_CLIENT_ID_SENT => Ok(Response::HistoryClientIdSent(parse_single_value(&lines)?)),
OK_CLIENT_ID_SENT => Ok(Response::HistoryClientIdSent(parse_single_integer(&lines)?)),
OK_MSG_TEXT_SENT => Ok(Response::MessageTextSent),
OK_HELP_SENT => Ok(Response::HelpSent(lines)),
OK_VOICES_LIST_SENT => Ok(Response::VoicesListSent(
@ -711,20 +712,26 @@ impl<S: Read + Write + Source> Client<S> {
/// Receive signed 8-bit integer
pub fn receive_i8(&mut self) -> ClientResult<u8> {
self.receive_string(OK_GET)
.and_then(|s| s.parse().map_err(|_| ClientError::InvalidType))
self.receive_string(OK_GET).and_then(|s| {
s.parse()
.map_err(|_| ClientError::invalid_data("invalid signed integer"))
})
}
/// Receive unsigned 8-bit integer
pub fn receive_u8(&mut self) -> ClientResult<u8> {
self.receive_string(OK_GET)
.and_then(|s| s.parse().map_err(|_| ClientError::InvalidType))
self.receive_string(OK_GET).and_then(|s| {
s.parse()
.map_err(|_| ClientError::invalid_data("invalid unsigned integer"))
})
}
/// Receive message id
pub fn receive_message_id(&mut self) -> ClientResult<MessageId> {
self.receive_string(OK_MESSAGE_QUEUED)
.and_then(|s| s.parse().map_err(|_| ClientError::InvalidType))
self.receive_string(OK_MESSAGE_QUEUED).and_then(|s| {
s.parse()
.map_err(|_| ClientError::invalid_data("invalid message id"))
})
}
/// Receive a list of synthesis voices
@ -738,14 +745,14 @@ impl<S: Read + Write + Source> Client<S> {
let mut lines = Vec::new();
crate::protocol::receive_answer(&mut self.input, Some(&mut lines)).and_then(|status| {
if lines.len() < 2 {
Err(ClientError::TruncatedMessage)
Err(ClientError::unexpected_eof("event truncated"))
} else {
let message = &lines[0];
let client = &lines[1];
match status.code {
700 => {
if lines.len() != 3 {
Err(ClientError::TruncatedMessage)
Err(ClientError::unexpected_eof("index markevent truncated"))
} else {
let mark = lines[3].to_owned();
Ok(Event::index_mark(mark, message, client))
@ -756,7 +763,7 @@ impl<S: Read + Write + Source> Client<S> {
703 => Ok(Event::cancel(message, client)),
704 => Ok(Event::pause(message, client)),
705 => Ok(Event::resume(message, client)),
_ => Err(ClientError::InvalidType),
_ => Err(ClientError::invalid_data("wrong status code for event")),
}
}
})

@ -40,13 +40,26 @@ pub(crate) fn parse_event_id(lines: &[String]) -> ClientResult<EventId> {
}
}
/// Parse single integer value
pub(crate) fn parse_single_integer<T>(lines: &[String]) -> ClientResult<T>
where
T: FromStr,
{
parse_single_value(lines)?.parse::<T>().map_err(|_| {
ClientError::Io(io::Error::new(
io::ErrorKind::InvalidData,
"invalid integer value",
))
})
}
pub(crate) fn parse_typed_lines<T>(lines: &[String]) -> ClientResult<Vec<T>>
where
T: FromStr<Err = io::Error>,
T: FromStr<Err = ClientError>,
{
lines
.iter()
.map(|line| T::from_str(line.as_str()).map_err(|err| ClientError::from(err)))
.map(|line| T::from_str(line.as_str()))
.collect::<ClientResult<Vec<T>>>()
}

@ -369,15 +369,18 @@ impl SynthesisVoice {
}
impl FromStr for SynthesisVoice {
type Err = io::Error;
type Err = ClientError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut iter = s.split('\t');
Ok(SynthesisVoice {
name: String::from(iter.next().unwrap()),
language: SynthesisVoice::parse_none(iter.next()),
dialect: SynthesisVoice::parse_none(iter.next()),
})
match iter.next() {
Some(name) => Ok(SynthesisVoice {
name: name.to_string(),
language: SynthesisVoice::parse_none(iter.next()),
dialect: SynthesisVoice::parse_none(iter.next()),
}),
None => Err(ClientError::unexpected_eof("missing synthesis voice name")),
}
}
}
@ -402,8 +405,6 @@ impl fmt::Display for StatusLine {
/// Client error, either I/O error or SSIP error.
#[derive(ThisError, Debug)]
pub enum ClientError {
#[error("Invalid type")]
InvalidType,
#[error("I/O: {0}")]
Io(io::Error),
#[error("Not ready")]
@ -414,12 +415,27 @@ pub enum ClientError {
TooFewLines,
#[error("Too many lines")]
TooManyLines,
#[error("Truncated message")]
TruncatedMessage,
#[error("Unexpected status: {0}")]
UnexpectedStatus(ReturnCode),
}
impl ClientError {
/// Create I/O error
pub(crate) fn io_error(kind: io::ErrorKind, msg: &str) -> Self {
Self::Io(io::Error::new(kind, msg))
}
/// Invalid data I/O error
pub(crate) fn invalid_data(msg: &str) -> Self {
ClientError::io_error(io::ErrorKind::InvalidData, msg)
}
/// Unexpected EOF I/O error
pub(crate) fn unexpected_eof(msg: &str) -> Self {
ClientError::io_error(io::ErrorKind::UnexpectedEof, msg)
}
}
impl From<io::Error> for ClientError {
fn from(err: io::Error) -> Self {
if err.kind() == io::ErrorKind::WouldBlock {
@ -538,26 +554,15 @@ impl HistoryClientStatus {
connected,
}
}
fn invalid_data(msg: &str) -> io::Error {
io::Error::new(io::ErrorKind::InvalidData, msg)
}
fn unexpected_eof(msg: &str) -> io::Error {
io::Error::new(io::ErrorKind::UnexpectedEof, msg)
}
}
impl FromStr for HistoryClientStatus {
type Err = io::Error;
type Err = ClientError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut iter = s.splitn(3, ' ');
match iter.next() {
Some("") => Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"expecting client id",
)),
Some("") => Err(ClientError::unexpected_eof("expecting client id")),
Some(client_id) => match client_id.parse::<u32>() {
Ok(id) => match iter.next() {
Some(name) => match iter.next() {
@ -567,16 +572,14 @@ impl FromStr for HistoryClientStatus {
Some(status) if status == "1" => {
Ok(HistoryClientStatus::new(id, name, true))
}
Some(_) => Err(HistoryClientStatus::invalid_data("invalid client status")),
None => Err(HistoryClientStatus::unexpected_eof(
"expecting client status",
)),
Some(_) => Err(ClientError::invalid_data("invalid client status")),
None => Err(ClientError::unexpected_eof("expecting client status")),
},
None => Err(HistoryClientStatus::unexpected_eof("expecting client name")),
None => Err(ClientError::unexpected_eof("expecting client name")),
},
Err(_) => Err(HistoryClientStatus::invalid_data("invalid client id")),
Err(_) => Err(ClientError::invalid_data("invalid client id")),
},
None => Err(HistoryClientStatus::unexpected_eof("expecting client id")),
None => Err(ClientError::unexpected_eof("expecting client id")),
}
}
}
@ -587,7 +590,7 @@ mod tests {
use std::io;
use std::str::FromStr;
use super::{HistoryClientStatus, HistoryPosition, MessageScope, SynthesisVoice};
use super::{ClientError, HistoryClientStatus, HistoryPosition, MessageScope, SynthesisVoice};
#[test]
fn parse_synthesis_voice() {
@ -635,23 +638,15 @@ mod tests {
] {
match HistoryClientStatus::from_str(line) {
Ok(_) => panic!("parsing should have failed"),
Err(err) => assert_eq!(
io::ErrorKind::InvalidData,
err.kind(),
"expecting error 'invalid data' parsing \"{}\"",
line
),
Err(ClientError::Io(err)) if err.kind() == io::ErrorKind::InvalidData => (),
Err(_) => panic!("expecting error 'invalid data' parsing \"{}\"", line),
}
}
for line in &["8 joe:speechd_client:main", "8", ""] {
match HistoryClientStatus::from_str(line) {
Ok(_) => panic!("parsing should have failed"),
Err(err) => assert_eq!(
io::ErrorKind::UnexpectedEof,
err.kind(),
"expecting error 'unexpected EOF' parsing \"{}\"",
line
),
Err(ClientError::Io(err)) if err.kind() == io::ErrorKind::UnexpectedEof => (),
Err(_) => panic!("expecting error 'unexpected EOF' parsing \"{}\"", line),
}
}
}

Loading…
Cancel
Save