diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7e1139f..2c5f0e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,5 +1,7 @@ name: CI -on: +env: + CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse +on: push: branches: - main @@ -12,7 +14,6 @@ jobs: strategy: matrix: target: [x86_64-unknown-linux-gnu, x86_64-unknown-linux-musl, wasm32-unknown-unknown, wasm32-wasi, aarch64-unknown-linux-musl, aarch64-unknown-linux-gnu] - #target: [x86_64-unknown-linux-gnu, x86_64-unknown-linux-musl, wasm32-unknown-unknown, wasm32-wasi, aarch64-unknown-linux-musl, aarch64-unknown-linux-gnu, x86_64-unknown-freebsd, x86_64-unknown-openbsd, x86_64-pc-windows-msvc] steps: - name: Git checkout uses: actions/checkout@v3 @@ -38,3 +39,216 @@ jobs: - name: Run tests working-directory: ssip-common run: cargo test + find-msrv: + runs-on: ubuntu-latest + outputs: + version: ${{ steps.step2.outputs.version }} + steps: + - uses: actions/checkout@v4 + - id: step2 + run: echo "version=`cat Cargo.toml | sed -n 's/rust-version = "\(.*\)"/\1/p'`" >> "$GITHUB_OUTPUT" + benchmarks-compile: + runs-on: ubuntu-latest + needs: [clippy,no-unused-dependencies] + steps: + - name: Git checkout + uses: actions/checkout@v3 + - name: Cache cargo home + uses: actions/cache@v3 + env: + cache-name: cache-cargo-home + with: + path: | + ~/.cargo/bin + ~/.cargo/registry/index + ~/.cargo/registry/cache + ~/.cargo/git/db + key: ${{ runner.os }}-x86_64-unknown-linux-gnu-build-${{ env.cache-name }}-${{ hashFiles('Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-x86_64-unknown-linux-gnu-build-${{ env.cache-name }}- + - name: Compile benchmarks (1k) + run: cargo bench --no-run --bench event_parsing + - name: Compile benchmarks (100k) + run: cargo bench --no-run --bench event_parsing_100k + clippy: + runs-on: ubuntu-latest + needs: [rustfmt,no-unused-dependencies] + name: clippy (nightly) + steps: + - name: Git checkout + uses: actions/checkout@v3 + - name: Cache cargo home + uses: actions/cache@v3 + env: + cache-name: cache-cargo-home + with: + path: | + ~/.cargo/bin + ~/.cargo/registry/index + ~/.cargo/registry/cache + ~/.cargo/git/db + key: ${{ runner.os }}-x86_64-unknown-linux-gnu-build-${{ env.cache-name }}-${{ hashFiles('Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-x86_64-unknown-linux-gnu-build-${{ env.cache-name }}- + - name: Install ${{ matrix.toolchain }} + uses: dtolnay/rust-toolchain@master + with: + components: clippy + toolchain: nightly + - name: Clippy + run: cargo clippy --tests --workspace --no-deps -- -D warnings + tests: + runs-on: ubuntu-latest + needs: [clippy,no-unused-dependencies] + steps: + - name: Git checkout + uses: actions/checkout@v3 + - name: Cache cargo home + uses: actions/cache@v3 + env: + cache-name: cache-cargo-home + with: + path: | + ~/.cargo/bin + ~/.cargo/registry/index + ~/.cargo/registry/cache + ~/.cargo/git/db + key: ${{ runner.os }}-x86_64-unknown-linux-gnu-build-${{ env.cache-name }}-${{ hashFiles('Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-x86_64-unknown-linux-gnu-build-${{ env.cache-name }}- + - name: Install Rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + - name: Run Tests (tokio) + run: cargo test --workspace --no-default-features --features=tokio -- --nocapture + - name: Run Tests (async-std) + run: cargo test --workspace --no-default-features --features=async-std -- --nocapture + rustfmt: + runs-on: ubuntu-latest + steps: + - name: Git checkout + uses: actions/checkout@v3 + - name: Install Rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + components: rustfmt + - name: Run formatter + run: cargo fmt --all --check + rustdoc: + runs-on: ubuntu-latest + needs: [clippy,no-unused-dependencies] + steps: + - name: Git checkout + uses: actions/checkout@v3 + - name: Cache cargo home + uses: actions/cache@v3 + env: + cache-name: cache-cargo-home + with: + path: | + ~/.cargo/bin + ~/.cargo/registry/index + ~/.cargo/registry/cache + ~/.cargo/git/db + key: ${{ runner.os }}-x86_64-unknown-linux-gnu-build-${{ env.cache-name }}-${{ hashFiles('Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-x86_64-unknown-linux-gnu-build-${{ env.cache-name }}- + - name: Install Rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: nightly + - name: Generate Documentation + run: cargo doc --workspace --no-deps --document-private-items + no-unused-dependencies: + runs-on: ubuntu-latest + needs: [rustfmt] + steps: + - name: Git checkout + uses: actions/checkout@v3 + - name: Cache cargo home + uses: actions/cache@v3 + env: + cache-name: cache-cargo-home + with: + path: | + ~/.cargo/bin + ~/.cargo/registry/index + ~/.cargo/registry/cache + ~/.cargo/git/db + key: ${{ runner.os }}-x86_64-unknown-linux-gnu-build-${{ env.cache-name }}-${{ hashFiles('Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-x86_64-unknown-linux-gnu-build-${{ env.cache-name }}- + - name: Install Rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + - name: Install Cargo Machete + uses: taiki-e/install-action@cargo-machete + - name: Check For Unused Dependencies + run: cargo machete + semver-compliance: + runs-on: ubuntu-latest + needs: [clippy,no-unused-dependencies] + steps: + - name: Git checkout + uses: actions/checkout@v3 + - name: Cache cargo home + uses: actions/cache@v3 + env: + cache-name: cache-cargo-home + with: + path: | + ~/.cargo/bin + ~/.cargo/registry/index + ~/.cargo/registry/cache + ~/.cargo/git/db + key: ${{ runner.os }}-x86_64-unknown-linux-gnu-build-${{ env.cache-name }}-${{ hashFiles('Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-x86_64-unknown-linux-gnu-build-${{ env.cache-name }}- + - name: Install Rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + - name: Install Semver Checks + # no default features so that it uses native Rust TLS instead of trying to link with system TLS. + uses: taiki-e/install-action@main + with: + tool: cargo-semver-checks + - name: Check Semver Compliance + run: LOCKSTEP_XML_PATH="../xml" cargo semver-checks check-release -p atspi --default-features + msrv-compliance: + runs-on: ubuntu-latest + needs: [clippy,no-unused-dependencies,find-msrv] + steps: + - name: install stable toolchain + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ needs.find-msrv.outputs.version }} + - name: Git checkout + uses: actions/checkout@v3 + - name: Check MSRV Compliance + run: cargo test --workspace --no-run --all-features + coverage: + runs-on: ubuntu-latest + needs: [clippy,no-unused-dependencies] + steps: + - name: Git checkout + uses: actions/checkout@v3 + - name: Install nightly + uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + components: llvm-tools-preview + - name: cargo install cargo-llvm-cov + uses: taiki-e/install-action@cargo-llvm-cov + - name: cargo generate-lockfile + if: hashFiles('Cargo.lock') == '' + run: cargo generate-lockfile + - name: cargo llvm-cov + run: cargo llvm-cov --workspace --locked --lcov --output-path lcov.info + - name: Upload to codecov.io + uses: codecov/codecov-action@v3 + with: + fail_ci_if_error: true diff --git a/Cargo.toml b/Cargo.toml index 7854841..f67d1de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,8 +15,6 @@ dirs = "4" log = { version = "0.4", features = ["max_level_debug", "release_max_level_info"] } mio = { version = "0.8", optional = true } thiserror = "1" -strum = "0.24" -strum_macros = "0.24" tokio = { version = "^1.21.2", features = ["io-util", "rt", "macros", "net"] } async-std = { version = "1.12.0", default_features = true } diff --git a/examples/async_tokio.rs b/examples/async_tokio.rs index 7d0ba36..c054f01 100644 --- a/examples/async_tokio.rs +++ b/examples/async_tokio.rs @@ -1,5 +1,7 @@ extern crate tokio; -use ssip_client::{fifo::asynchronous_tokio::Builder, ClientName, ClientResult, types::ClientScope}; +use ssip_client::{ + fifo::asynchronous_tokio::Builder, types::ClientScope, ClientName, ClientResult, +}; #[cfg(all(unix, feature = "tokio"))] #[tokio::main(flavor = "current_thread")] @@ -8,29 +10,44 @@ async fn main() -> ClientResult<()> { let mut client = Builder::new().build().await?; println!("Client created."); client - .set_client_name(ClientName::new("test", "hello")).await? - .check_client_name_set().await?; + .set_client_name(ClientName::new("test", "hello")) + .await? + .check_client_name_set() + .await?; println!("Client connected"); let msg_id = client - .speak().await? - .check_receiving_data().await? - .send_line("hello\r\n.").await? - .receive_message_id().await?; + .speak() + .await? + .check_receiving_data() + .await? + .send_line("hello\r\n.") + .await? + .receive_message_id() + .await?; println!("message: {}", msg_id); let volume = client.get_volume().await?.receive_u8().await?; println!("volume: {}", volume); - match client.set_volume(ClientScope::Current, 1).await?.receive().await { - Ok(id) => println!("Volume change ID: {:?}", id), - Err(e) => println!("Error: {:?}", e), + match client + .set_volume(ClientScope::Current, 1) + .await? + .receive() + .await + { + Ok(id) => println!("Volume change ID: {:?}", id), + Err(e) => println!("Error: {:?}", e), }; let volume = client.get_volume().await?.receive_u8().await?; println!("volume: {}", volume); let msg_id = client - .speak().await? - .check_receiving_data().await? - .send_line("hello\r\n.").await? - .receive_message_id().await?; - println!("id2: {}", msg_id); + .speak() + .await? + .check_receiving_data() + .await? + .send_line("hello\r\n.") + .await? + .receive_message_id() + .await?; + println!("id2: {}", msg_id); client.quit().await?; Ok(()) } diff --git a/src/async_std.rs b/src/async_std.rs index c03d0c9..eaec535 100644 --- a/src/async_std.rs +++ b/src/async_std.rs @@ -11,8 +11,8 @@ use std::io::{self, Read, Write}; use crate::constants::*; use crate::protocol::{ - flush_lines, parse_event_id, parse_single_integer, parse_single_value, parse_typed_lines, - flush_lines_async_std, write_lines_async_std, + flush_lines, flush_lines_async_std, parse_event_id, parse_single_integer, parse_single_value, + parse_typed_lines, write_lines_async_std, }; use crate::types::*; @@ -79,7 +79,8 @@ impl AsyncClient { .map(|s| s.as_str()) .collect::>() .as_slice(), - ).await?; + ) + .await?; flush_lines_async_std(&mut self.output, &END_OF_DATA).await?; Ok(self) } @@ -333,7 +334,8 @@ impl AsyncClient { scope: ClientScope, value: &str, ) -> ClientResult<&mut Self> { - self.send(Request::SetOutputModule(scope, value.to_string())).await + self.send(Request::SetOutputModule(scope, value.to_string())) + .await } /// Get the current output module @@ -347,8 +349,13 @@ impl AsyncClient { } /// Set language code - pub async fn set_language(&mut self, scope: ClientScope, value: &str) -> ClientResult<&mut Self> { - self.send(Request::SetLanguage(scope, value.to_string())).await + pub async fn set_language( + &mut self, + scope: ClientScope, + value: &str, + ) -> ClientResult<&mut Self> { + self.send(Request::SetLanguage(scope, value.to_string())) + .await } /// Get the current language @@ -371,7 +378,11 @@ impl AsyncClient { } /// Set spelling on or off - pub async fn set_spelling(&mut self, scope: ClientScope, value: bool) -> ClientResult<&mut Self> { + pub async fn set_spelling( + &mut self, + scope: ClientScope, + value: bool, + ) -> ClientResult<&mut Self> { self.send(Request::SetSpelling(scope, value)).await } @@ -381,12 +392,18 @@ impl AsyncClient { scope: ClientScope, mode: CapitalLettersRecognitionMode, ) -> ClientResult<&mut Self> { - self.send(Request::SetCapitalLettersRecognitionMode(scope, mode)).await + self.send(Request::SetCapitalLettersRecognitionMode(scope, mode)) + .await } /// Set the voice type (MALE1, FEMALE1, …) - pub async fn set_voice_type(&mut self, scope: ClientScope, value: &str) -> ClientResult<&mut Self> { - self.send(Request::SetVoiceType(scope, value.to_string())).await + pub async fn set_voice_type( + &mut self, + scope: ClientScope, + value: &str, + ) -> ClientResult<&mut Self> { + self.send(Request::SetVoiceType(scope, value.to_string())) + .await } /// Get the current pre-defined voice @@ -405,7 +422,8 @@ impl AsyncClient { scope: ClientScope, value: &str, ) -> ClientResult<&mut Self> { - self.send(Request::SetSynthesisVoice(scope, value.to_string())).await + self.send(Request::SetSynthesisVoice(scope, value.to_string())) + .await } /// Lists the available voices for the current synthesizer @@ -444,7 +462,11 @@ impl AsyncClient { } /// Set the number of (more or less) sentences that should be repeated after a previously paused text is resumed. - pub async fn set_pause_context(&mut self, scope: ClientScope, value: u32) -> ClientResult<&mut Self> { + pub async fn set_pause_context( + &mut self, + scope: ClientScope, + value: u32, + ) -> ClientResult<&mut Self> { self.send(Request::SetPauseContext(scope, value)).await } @@ -468,7 +490,11 @@ impl AsyncClient { } /// Enable or disable history of received messages. - pub async fn set_history(&mut self, scope: ClientScope, value: bool) -> ClientResult<&mut Self> { + pub async fn set_history( + &mut self, + scope: ClientScope, + value: bool, + ) -> ClientResult<&mut Self> { self.send(Request::SetHistory(scope, value)).await } @@ -494,7 +520,8 @@ impl AsyncClient { start: u32, number: u32, ) -> ClientResult<&mut Self> { - self.send(Request::HistoryGetClientMsgs(scope, start, number)).await + self.send(Request::HistoryGetClientMsgs(scope, start, number)) + .await } /// Get the id of the last message sent by the client. @@ -522,7 +549,10 @@ impl AsyncClient { } /// Move the cursor position backward or forward. - pub async fn history_move_cursor(&mut self, direction: CursorDirection) -> ClientResult<&mut Self> { + pub async fn history_move_cursor( + &mut self, + direction: CursorDirection, + ) -> ClientResult<&mut Self> { self.send(Request::HistoryCursorMove(direction)).await } @@ -541,13 +571,20 @@ impl AsyncClient { } /// Set the maximum length of short versions of history messages. - pub async fn history_set_short_message_length(&mut self, length: u32) -> ClientResult<&mut Self> { + pub async fn history_set_short_message_length( + &mut self, + length: u32, + ) -> ClientResult<&mut Self> { self.send(Request::HistorySetShortMsgLength(length)).await } /// Set the ordering of the message types, from the minimum to the maximum. - pub async fn history_set_ordering(&mut self, ordering: Vec) -> ClientResult<&mut Self> { - self.send(Request::HistorySetMsgTypeOrdering(ordering)).await + pub async fn history_set_ordering( + &mut self, + ordering: Vec, + ) -> ClientResult<&mut Self> { + self.send(Request::HistorySetMsgTypeOrdering(ordering)) + .await } /// Search in message history. @@ -556,7 +593,8 @@ impl AsyncClient { scope: ClientScope, condition: &str, ) -> ClientResult<&mut Self> { - self.send(Request::HistorySearch(scope, condition.to_string())).await + self.send(Request::HistorySearch(scope, condition.to_string())) + .await } /// Close the connection @@ -589,7 +627,8 @@ impl AsyncClient { /// Receive a single string pub async fn receive_string(&mut self, expected_code: ReturnCode) -> ClientResult { self.receive_lines(expected_code) - .await.and_then(|lines| parse_single_value(&lines)) + .await + .and_then(|lines| parse_single_value(&lines)) } /// Receive signed 8-bit integer @@ -636,42 +675,46 @@ impl AsyncClient { /// Receive a list of synthesis voices pub async fn receive_synthesis_voices(&mut self) -> ClientResult> { self.receive_lines(OK_VOICES_LIST_SENT) - .await.and_then(|lines| parse_typed_lines::(&lines)) + .await + .and_then(|lines| parse_typed_lines::(&lines)) } /// Receive a notification pub async fn receive_event(&mut self) -> ClientResult { let mut lines = Vec::new(); - self.receive_answer(Some(&mut lines)).await.and_then(|status| { - if lines.len() < 2 { - 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::unexpected_eof("index markevent truncated")) - } else { - let mark = lines[3].to_owned(); - Ok(Event::index_mark(mark, message, client)) + self.receive_answer(Some(&mut lines)) + .await + .and_then(|status| { + if lines.len() < 2 { + 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::unexpected_eof("index markevent truncated")) + } else { + let mark = lines[3].to_owned(); + Ok(Event::index_mark(mark, message, client)) + } } + 701 => Ok(Event::begin(message, client)), + 702 => Ok(Event::end(message, client)), + 703 => Ok(Event::cancel(message, client)), + 704 => Ok(Event::pause(message, client)), + 705 => Ok(Event::resume(message, client)), + _ => Err(ClientError::invalid_data("wrong status code for event")), } - 701 => Ok(Event::begin(message, client)), - 702 => Ok(Event::end(message, client)), - 703 => Ok(Event::cancel(message, client)), - 704 => Ok(Event::pause(message, client)), - 705 => Ok(Event::resume(message, client)), - _ => Err(ClientError::invalid_data("wrong status code for event")), } - } - }) + }) } /// Receive a list of client status from history. pub async fn receive_history_clients(&mut self) -> ClientResult> { self.receive_lines(OK_CLIENTS_LIST_SENT) - .await.and_then(|lines| parse_typed_lines::(&lines)) + .await + .and_then(|lines| parse_typed_lines::(&lines)) } /// Check the result of `set_client_name`. diff --git a/src/client.rs b/src/client.rs index 8e5a015..f24c8e3 100644 --- a/src/client.rs +++ b/src/client.rs @@ -12,7 +12,7 @@ use std::io::{self, Read, Write}; use crate::constants::*; use crate::protocol::{ flush_lines, parse_event_id, parse_single_integer, parse_single_value, parse_typed_lines, - write_lines + write_lines, }; use crate::types::*; @@ -579,7 +579,7 @@ impl Client { OK_OUTSIDE_BLOCK => Ok(Response::OutsideBlock), OK_NOT_IMPLEMENTED => Ok(Response::NotImplemented), EVENT_INDEX_MARK => match lines.len() { - 0 | 1 | 2 => Err(ClientError::TooFewLines), + 0..=2 => Err(ClientError::TooFewLines), 3 => Ok(Response::EventIndexMark( parse_event_id(&lines)?, lines[2].to_owned(), diff --git a/src/fifo.rs b/src/fifo.rs index a458280..b909c3a 100644 --- a/src/fifo.rs +++ b/src/fifo.rs @@ -157,13 +157,9 @@ mod asynchronous { } #[cfg(feature = "tokio")] pub mod asynchronous_tokio { - pub use tokio::net::{ - UnixStream, - unix::OwnedReadHalf, - unix::OwnedWriteHalf, - }; - use tokio::io::{self, BufReader as AsyncBufReader, BufWriter as AsyncBufWriter}; use std::path::Path; + use tokio::io::{self, BufReader as AsyncBufReader, BufWriter as AsyncBufWriter}; + pub use tokio::net::{unix::OwnedReadHalf, unix::OwnedWriteHalf, UnixStream}; use crate::tokio::AsyncClient; @@ -188,9 +184,12 @@ pub mod asynchronous_tokio { self } - pub async fn build(&self) -> io::Result, AsyncBufWriter>> { - let (read_stream, write_stream) = UnixStream::connect(self.path.get()?).await? - .into_split(); + pub async fn build( + &self, + ) -> io::Result, AsyncBufWriter>> + { + let (read_stream, write_stream) = + UnixStream::connect(self.path.get()?).await?.into_split(); Ok(AsyncClient::new( AsyncBufReader::new(read_stream), AsyncBufWriter::new(write_stream), diff --git a/src/lib.rs b/src/lib.rs index 08c869f..b0cceb6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,10 +42,10 @@ pub mod tcp; #[cfg(any(not(feature = "async-mio"), doc))] pub use client::Client; -#[cfg(any(feature = "tokio", doc))] -pub mod tokio; #[cfg(any(feature = "async-std", doc))] pub mod async_std; +#[cfg(any(feature = "tokio", doc))] +pub mod tokio; pub use constants::*; pub use poll::QueuedClient; diff --git a/src/protocol.rs b/src/protocol.rs index 93b3ae5..fc3c872 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -10,20 +10,13 @@ use log::debug; use std::io::{self, BufRead, Write}; -#[cfg(any(feature = "tokio", doc))] -use tokio::io::{ - AsyncWrite, AsyncWriteExt, - AsyncBufRead, AsyncBufReadExt, -}; #[cfg(any(feature = "async-std", doc))] use async_std::io::{ - Read as AsyncReadStd, - BufRead as AsyncBufReadStd, - Write as AsyncWriteStd, - ReadExt, - WriteExt, - prelude::BufReadExt, + prelude::BufReadExt, BufRead as AsyncBufReadStd, Read as AsyncReadStd, ReadExt, + Write as AsyncWriteStd, WriteExt, }; +#[cfg(any(feature = "tokio", doc))] +use tokio::io::{AsyncBufRead, AsyncBufReadExt, AsyncWrite, AsyncWriteExt}; use std::str::FromStr; @@ -91,7 +84,10 @@ pub(crate) fn write_lines(output: &mut W, lines: &[&str]) -> /// Write lines (asyncronously) separated by CRLF. #[cfg(any(feature = "tokio", doc))] -pub(crate) async fn write_lines_tokio(output: &mut W, lines: &[&str]) -> ClientResult<()> { +pub(crate) async fn write_lines_tokio( + output: &mut W, + lines: &[&str], +) -> ClientResult<()> { for line in lines.iter() { debug!("SSIP(out): {}", line); output.write_all(line.as_bytes()).await?; @@ -101,7 +97,10 @@ pub(crate) async fn write_lines_tokio(output: &m } /// Write lines (asyncronously) separated by CRLF. #[cfg(any(feature = "async-std", doc))] -pub(crate) async fn write_lines_async_std(output: &mut W, lines: &[&str]) -> ClientResult<()> { +pub(crate) async fn write_lines_async_std( + output: &mut W, + lines: &[&str], +) -> ClientResult<()> { for line in lines.iter() { debug!("SSIP(out): {}", line); output.write_all(line.as_bytes()).await?; @@ -118,14 +117,20 @@ pub(crate) fn flush_lines(output: &mut W, lines: &[&str]) -> } /// Write lines separated by CRLF and flush the output asyncronously. #[cfg(any(feature = "tokio", doc))] -pub(crate) async fn flush_lines_tokio(output: &mut W, lines: &[&str]) -> ClientResult<()> { +pub(crate) async fn flush_lines_tokio( + output: &mut W, + lines: &[&str], +) -> ClientResult<()> { write_lines_tokio(output, lines).await?; output.flush().await?; Ok(()) } /// Write lines separated by CRLF and flush the output asyncronously. #[cfg(any(feature = "async-std", doc))] -pub(crate) async fn flush_lines_async_std(output: &mut W, lines: &[&str]) -> ClientResult<()> { +pub(crate) async fn flush_lines_async_std( + output: &mut W, + lines: &[&str], +) -> ClientResult<()> { write_lines_async_std(output, lines).await?; output.flush().await?; Ok(()) diff --git a/src/tokio.rs b/src/tokio.rs index 62a3b39..55f9b69 100644 --- a/src/tokio.rs +++ b/src/tokio.rs @@ -7,11 +7,10 @@ // option. All files in the project carrying such notice may not be copied, // modified, or distributed except according to those terms. - use crate::constants::*; use crate::protocol::{ - parse_event_id, parse_single_integer, parse_single_value, parse_typed_lines, - flush_lines_tokio, write_lines_tokio, + flush_lines_tokio, parse_event_id, parse_single_integer, parse_single_value, parse_typed_lines, + write_lines_tokio, }; use crate::types::*; @@ -31,7 +30,7 @@ macro_rules! send_toggle { send_one_line!($output, $fmt, $arg, on_off($val)) }; } - + macro_rules! send_range { ($output:expr, $fmt:expr, $scope:expr, $val:expr) => { send_one_line!( @@ -77,12 +76,13 @@ impl AsyncClient { .map(|s| s.as_str()) .collect::>() .as_slice(), - ).await?; + ) + .await?; flush_lines_tokio(&mut self.output, &END_OF_DATA).await?; Ok(self) } pub async fn send_line(&mut self, line: &str) -> ClientResult<&mut Self> { - self.send(Request::SendLine(line.to_string())).await + self.send(Request::SendLine(line.to_string())).await } /// Receive answer from server async fn receive_answer(&mut self, lines: Option<&mut Vec>) -> ClientStatus { @@ -334,7 +334,8 @@ impl AsyncClient { scope: ClientScope, value: &str, ) -> ClientResult<&mut Self> { - self.send(Request::SetOutputModule(scope, value.to_string())).await + self.send(Request::SetOutputModule(scope, value.to_string())) + .await } /// Get the current output module @@ -348,8 +349,13 @@ impl AsyncClient { } /// Set language code - pub async fn set_language(&mut self, scope: ClientScope, value: &str) -> ClientResult<&mut Self> { - self.send(Request::SetLanguage(scope, value.to_string())).await + pub async fn set_language( + &mut self, + scope: ClientScope, + value: &str, + ) -> ClientResult<&mut Self> { + self.send(Request::SetLanguage(scope, value.to_string())) + .await } /// Get the current language @@ -372,7 +378,11 @@ impl AsyncClient { } /// Set spelling on or off - pub async fn set_spelling(&mut self, scope: ClientScope, value: bool) -> ClientResult<&mut Self> { + pub async fn set_spelling( + &mut self, + scope: ClientScope, + value: bool, + ) -> ClientResult<&mut Self> { self.send(Request::SetSpelling(scope, value)).await } @@ -382,12 +392,18 @@ impl AsyncClient { scope: ClientScope, mode: CapitalLettersRecognitionMode, ) -> ClientResult<&mut Self> { - self.send(Request::SetCapitalLettersRecognitionMode(scope, mode)).await + self.send(Request::SetCapitalLettersRecognitionMode(scope, mode)) + .await } /// Set the voice type (MALE1, FEMALE1, …) - pub async fn set_voice_type(&mut self, scope: ClientScope, value: &str) -> ClientResult<&mut Self> { - self.send(Request::SetVoiceType(scope, value.to_string())).await + pub async fn set_voice_type( + &mut self, + scope: ClientScope, + value: &str, + ) -> ClientResult<&mut Self> { + self.send(Request::SetVoiceType(scope, value.to_string())) + .await } /// Get the current pre-defined voice @@ -406,7 +422,8 @@ impl AsyncClient { scope: ClientScope, value: &str, ) -> ClientResult<&mut Self> { - self.send(Request::SetSynthesisVoice(scope, value.to_string())).await + self.send(Request::SetSynthesisVoice(scope, value.to_string())) + .await } /// Lists the available voices for the current synthesizer @@ -445,7 +462,11 @@ impl AsyncClient { } /// Set the number of (more or less) sentences that should be repeated after a previously paused text is resumed. - pub async fn set_pause_context(&mut self, scope: ClientScope, value: u32) -> ClientResult<&mut Self> { + pub async fn set_pause_context( + &mut self, + scope: ClientScope, + value: u32, + ) -> ClientResult<&mut Self> { self.send(Request::SetPauseContext(scope, value)).await } @@ -469,7 +490,11 @@ impl AsyncClient { } /// Enable or disable history of received messages. - pub async fn set_history(&mut self, scope: ClientScope, value: bool) -> ClientResult<&mut Self> { + pub async fn set_history( + &mut self, + scope: ClientScope, + value: bool, + ) -> ClientResult<&mut Self> { self.send(Request::SetHistory(scope, value)).await } @@ -495,7 +520,8 @@ impl AsyncClient { start: u32, number: u32, ) -> ClientResult<&mut Self> { - self.send(Request::HistoryGetClientMsgs(scope, start, number)).await + self.send(Request::HistoryGetClientMsgs(scope, start, number)) + .await } /// Get the id of the last message sent by the client. @@ -523,7 +549,10 @@ impl AsyncClient { } /// Move the cursor position backward or forward. - pub async fn history_move_cursor(&mut self, direction: CursorDirection) -> ClientResult<&mut Self> { + pub async fn history_move_cursor( + &mut self, + direction: CursorDirection, + ) -> ClientResult<&mut Self> { self.send(Request::HistoryCursorMove(direction)).await } @@ -542,13 +571,20 @@ impl AsyncClient { } /// Set the maximum length of short versions of history messages. - pub async fn history_set_short_message_length(&mut self, length: u32) -> ClientResult<&mut Self> { + pub async fn history_set_short_message_length( + &mut self, + length: u32, + ) -> ClientResult<&mut Self> { self.send(Request::HistorySetShortMsgLength(length)).await } /// Set the ordering of the message types, from the minimum to the maximum. - pub async fn history_set_ordering(&mut self, ordering: Vec) -> ClientResult<&mut Self> { - self.send(Request::HistorySetMsgTypeOrdering(ordering)).await + pub async fn history_set_ordering( + &mut self, + ordering: Vec, + ) -> ClientResult<&mut Self> { + self.send(Request::HistorySetMsgTypeOrdering(ordering)) + .await } /// Search in message history. @@ -557,7 +593,8 @@ impl AsyncClient { scope: ClientScope, condition: &str, ) -> ClientResult<&mut Self> { - self.send(Request::HistorySearch(scope, condition.to_string())).await + self.send(Request::HistorySearch(scope, condition.to_string())) + .await } /// Close the connection @@ -590,7 +627,8 @@ impl AsyncClient { /// Receive a single string pub async fn receive_string(&mut self, expected_code: ReturnCode) -> ClientResult { self.receive_lines(expected_code) - .await.and_then(|lines| parse_single_value(&lines)) + .await + .and_then(|lines| parse_single_value(&lines)) } /// Receive signed 8-bit integer @@ -637,42 +675,46 @@ impl AsyncClient { /// Receive a list of synthesis voices pub async fn receive_synthesis_voices(&mut self) -> ClientResult> { self.receive_lines(OK_VOICES_LIST_SENT) - .await.and_then(|lines| parse_typed_lines::(&lines)) + .await + .and_then(|lines| parse_typed_lines::(&lines)) } /// Receive a notification pub async fn receive_event(&mut self) -> ClientResult { let mut lines = Vec::new(); - self.receive_answer(Some(&mut lines)).await.and_then(|status| { - if lines.len() < 2 { - 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::unexpected_eof("index markevent truncated")) - } else { - let mark = lines[3].to_owned(); - Ok(Event::index_mark(mark, message, client)) + self.receive_answer(Some(&mut lines)) + .await + .and_then(|status| { + if lines.len() < 2 { + 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::unexpected_eof("index markevent truncated")) + } else { + let mark = lines[3].to_owned(); + Ok(Event::index_mark(mark, message, client)) + } } + 701 => Ok(Event::begin(message, client)), + 702 => Ok(Event::end(message, client)), + 703 => Ok(Event::cancel(message, client)), + 704 => Ok(Event::pause(message, client)), + 705 => Ok(Event::resume(message, client)), + _ => Err(ClientError::invalid_data("wrong status code for event")), } - 701 => Ok(Event::begin(message, client)), - 702 => Ok(Event::end(message, client)), - 703 => Ok(Event::cancel(message, client)), - 704 => Ok(Event::pause(message, client)), - 705 => Ok(Event::resume(message, client)), - _ => Err(ClientError::invalid_data("wrong status code for event")), } - } - }) + }) } /// Receive a list of client status from history. pub async fn receive_history_clients(&mut self) -> ClientResult> { self.receive_lines(OK_CLIENTS_LIST_SENT) - .await.and_then(|lines| parse_typed_lines::(&lines)) + .await + .and_then(|lines| parse_typed_lines::(&lines)) } /// Check the result of `set_client_name`. diff --git a/ssip-common/Cargo.toml b/ssip-common/Cargo.toml index 56af0a4..a35f104 100644 --- a/ssip-common/Cargo.toml +++ b/ssip-common/Cargo.toml @@ -11,6 +11,5 @@ homepage = "http://htmlpreview.github.io/?https://github.com/brailcom/speechd/bl # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -strum = "0.24.1" strum_macros = "0.24" thiserror = "1.0.40"