// ssip-client -- Speech Dispatcher client in Rust // Copyright (c) 2021-2022 Laurent Pelecq // // Licensed under the Apache License, Version 2.0 // or the MIT // license , at your // option. All files in the project carrying such notice may not be copied, // modified, or distributed except according to those terms. use std::io; use std::path::{Path, PathBuf}; const SPEECHD_APPLICATION_NAME: &str = "speech-dispatcher"; const SPEECHD_SOCKET_NAME: &str = "speechd.sock"; struct FifoPath { path: Option, } impl FifoPath { fn new() -> FifoPath { FifoPath { path: None } } fn set

(&mut self, path: P) where P: AsRef, { self.path = Some(path.as_ref().to_path_buf()); } /// Return the standard socket according to the [freedesktop.org](https://www.freedesktop.org/) specification. fn default_path() -> io::Result { match dirs::runtime_dir() { Some(runtime_dir) => Ok(runtime_dir .join(SPEECHD_APPLICATION_NAME) .join(SPEECHD_SOCKET_NAME)), None => Err(io::Error::new( io::ErrorKind::NotFound, "unix socket not found", )), } } fn get(&self) -> io::Result { match &self.path { Some(path) => Ok(path.to_path_buf()), _ => FifoPath::default_path(), } } } #[cfg(not(feature = "async-mio"))] mod synchronous { use std::io::{self, BufReader, BufWriter}; pub use std::os::unix::net::UnixStream; use std::path::Path; use std::time::Duration; use crate::client::Client; use crate::net::StreamMode; use super::FifoPath; pub struct Builder { path: FifoPath, mode: StreamMode, } impl Builder { pub fn new() -> Self { Self { path: FifoPath::new(), mode: StreamMode::Blocking, } } pub fn path

(&mut self, socket_path: P) -> &mut Self where P: AsRef, { self.path.set(socket_path); self } pub fn timeout(&mut self, read_timeout: Duration) -> &mut Self { self.mode = StreamMode::TimeOut(read_timeout); self } pub fn nonblocking(&mut self) -> &mut Self { self.mode = StreamMode::NonBlocking; self } pub fn build(&self) -> io::Result> { let input = UnixStream::connect(self.path.get()?)?; match self.mode { StreamMode::Blocking => input.set_nonblocking(false)?, StreamMode::NonBlocking => input.set_nonblocking(true)?, StreamMode::TimeOut(timeout) => input.set_read_timeout(Some(timeout))?, } let output = input.try_clone()?; Ok(Client::new(BufReader::new(input), BufWriter::new(output))) } } } #[cfg(not(feature = "async-mio"))] pub use synchronous::{Builder, UnixStream}; #[cfg(feature = "async-mio")] mod asynchronous { pub use mio::net::UnixStream; use std::io::{self, BufReader, BufWriter}; use std::os::unix::net::UnixStream as StdUnixStream; use std::path::Path; use crate::client::Client; use super::FifoPath; pub struct Builder { path: FifoPath, } impl Builder { pub fn new() -> Self { Self { path: FifoPath::new(), } } fn non_blocking(socket: StdUnixStream) -> io::Result { socket.set_nonblocking(true)?; Ok(socket) } pub fn path

(&mut self, socket_path: P) -> &mut Self where P: AsRef, { self.path.set(socket_path); self } pub fn build(&self) -> io::Result> { let stream = StdUnixStream::connect(self.path.get()?)?; Ok(Client::new( BufReader::new(UnixStream::from_std(Self::non_blocking( stream.try_clone()?, )?)), BufWriter::new(UnixStream::from_std(Self::non_blocking(stream)?)), )) } } } #[cfg(feature = "tokio")] mod asynchronous_tokio { pub use tokio::net::UnixStream; use tokio::io::{self, BufReader, BufWriter}; use std::path::Path; use crate::tokio::AsyncClient; use super::FifoPath; pub struct Builder { path: FifoPath, } impl Builder { pub fn new() -> Self { Self { path: FifoPath::new(), } } pub fn path

(&mut self, socket_path: P) -> &mut Self where P: AsRef, { self.path.set(socket_path); self } fn non_blocking(socket: UnixStream) -> io::Result { socket.set_nonblocking(true)?; Ok(socket) } pub fn build(&self) -> io::Result> { let stream = UnixStream::connect(self.path.get()?)?; Ok(AsyncClient::new( BufReader::new(UnixStream::from_std(Self::non_blocking( stream.try_clone()?, )?)), BufWriter::new(UnixStream::from_std(Self::non_blocking(stream)?)), )) } } } #[cfg(feature = "async-mio")] pub use asynchronous::{Builder, UnixStream}; impl Default for Builder { fn default() -> Self { Self::new() } } #[cfg(test)] mod tests { #[test] fn test_fifo_path() -> std::io::Result<()> { if std::env::var("XDG_RUNTIME_DIR").is_ok() { let socket_path = super::FifoPath::new(); assert!(socket_path .get()? .to_str() .unwrap() .ends_with("/speech-dispatcher/speechd.sock")); } Ok(()) } }