ssip_client/
fifo.rs

1// ssip-client -- Speech Dispatcher client in Rust
2// Copyright (c) 2021-2022 Laurent Pelecq
3//
4// Licensed under the Apache License, Version 2.0
5// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
6// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. All files in the project carrying such notice may not be copied,
8// modified, or distributed except according to those terms.
9
10use 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    /// Return the standard socket according to the [freedesktop.org](https://www.freedesktop.org/) specification.
34    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}