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

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

Loading…
Cancel
Save