1use std::io;
11use std::path::{Path, PathBuf};
12
13const SPEECHD_APPLICATION_NAME: &str = "speech-dispatcher";
14const SPEECHD_SOCKET_NAME: &str = "speechd.sock";
15
16#[derive(Debug)]
17struct FifoPath {
18 path: Option<PathBuf>,
19}
20
21impl FifoPath {
22 fn new() -> FifoPath {
23 FifoPath { path: None }
24 }
25
26 fn set<P>(&mut self, path: P)
27 where
28 P: AsRef<Path>,
29 {
30 self.path = Some(path.as_ref().to_path_buf());
31 }
32
33 fn default_path() -> io::Result<PathBuf> {
35 match dirs::runtime_dir() {
36 Some(runtime_dir) => Ok(runtime_dir
37 .join(SPEECHD_APPLICATION_NAME)
38 .join(SPEECHD_SOCKET_NAME)),
39 None => Err(io::Error::new(
40 io::ErrorKind::NotFound,
41 "unix socket not found",
42 )),
43 }
44 }
45
46 fn get(&self) -> io::Result<PathBuf> {
47 match &self.path {
48 Some(path) => Ok(path.to_path_buf()),
49 _ => FifoPath::default_path(),
50 }
51 }
52}
53
54#[cfg(not(feature = "async-mio"))]
55mod synchronous {
56 use std::io::{self, BufReader, BufWriter};
57 pub use std::os::unix::net::UnixStream;
58 use std::path::Path;
59 use std::time::Duration;
60
61 use crate::client::Client;
62 use crate::net::StreamMode;
63
64 use super::FifoPath;
65
66 #[derive(Debug)]
67 pub struct Builder {
68 path: FifoPath,
69 mode: StreamMode,
70 }
71
72 impl Builder {
73 pub fn new() -> Self {
74 Self {
75 path: FifoPath::new(),
76 mode: StreamMode::Blocking,
77 }
78 }
79
80 pub fn path<P>(&mut self, socket_path: P) -> &mut Self
81 where
82 P: AsRef<Path>,
83 {
84 self.path.set(socket_path);
85 self
86 }
87
88 pub fn timeout(&mut self, read_timeout: Duration) -> &mut Self {
89 self.mode = StreamMode::TimeOut(read_timeout);
90 self
91 }
92
93 pub fn nonblocking(&mut self) -> &mut Self {
94 self.mode = StreamMode::NonBlocking;
95 self
96 }
97
98 pub fn build(&self) -> io::Result<Client<UnixStream>> {
99 let input = UnixStream::connect(self.path.get()?)?;
100 match self.mode {
101 StreamMode::Blocking => input.set_nonblocking(false)?,
102 StreamMode::NonBlocking => input.set_nonblocking(true)?,
103 StreamMode::TimeOut(timeout) => input.set_read_timeout(Some(timeout))?,
104 }
105 let output = input.try_clone()?;
106 Ok(Client::new(BufReader::new(input), BufWriter::new(output)))
107 }
108 }
109}
110
111#[cfg(not(feature = "async-mio"))]
112pub use synchronous::{Builder, UnixStream};
113
114#[cfg(feature = "async-mio")]
115mod asynchronous {
116 pub use mio::net::UnixStream;
117 use std::io::{self, BufReader, BufWriter};
118 use std::os::unix::net::UnixStream as StdUnixStream;
119 use std::path::Path;
120
121 use crate::client::Client;
122
123 use super::FifoPath;
124
125 pub struct Builder {
126 path: FifoPath,
127 }
128
129 impl Builder {
130 pub fn new() -> Self {
131 Self {
132 path: FifoPath::new(),
133 }
134 }
135
136 fn non_blocking(socket: StdUnixStream) -> io::Result<StdUnixStream> {
137 socket.set_nonblocking(true)?;
138 Ok(socket)
139 }
140
141 pub fn path<P>(&mut self, socket_path: P) -> &mut Self
142 where
143 P: AsRef<Path>,
144 {
145 self.path.set(socket_path);
146 self
147 }
148
149 pub fn build(&self) -> io::Result<Client<UnixStream>> {
150 let stream = StdUnixStream::connect(self.path.get()?)?;
151 Ok(Client::new(
152 BufReader::new(UnixStream::from_std(Self::non_blocking(
153 stream.try_clone()?,
154 )?)),
155 BufWriter::new(UnixStream::from_std(Self::non_blocking(stream)?)),
156 ))
157 }
158 }
159}
160
161#[cfg(feature = "async-mio")]
162pub use asynchronous::{Builder, UnixStream};
163
164impl Default for Builder {
165 fn default() -> Self {
166 Self::new()
167 }
168}
169
170#[cfg(test)]
171mod tests {
172
173 #[test]
174 fn test_fifo_path() -> std::io::Result<()> {
175 if std::env::var("XDG_RUNTIME_DIR").is_ok() {
176 let socket_path = super::FifoPath::new();
177 assert!(socket_path
178 .get()?
179 .to_str()
180 .unwrap()
181 .ends_with("/speech-dispatcher/speechd.sock"));
182 }
183 Ok(())
184 }
185}