diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 12ae548..479c3e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -236,7 +236,7 @@ jobs: if: hashFiles('Cargo.lock') == '' run: cargo generate-lockfile - name: cargo llvm-cov - run: cargo llvm-cov --workspace --doctests --locked --lcov --output-path lcov.info + run: cargo llvm-cov --workspace --doctests --all-features --locked --lcov --output-path lcov.info - name: Upload to codecov.io uses: codecov/codecov-action@v4 with: diff --git a/Cargo.toml b/Cargo.toml index 35ac59f..86d8d0d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,11 +22,9 @@ serde = ["serde/derive", "bytes?/serde", "enumflags2?/serde"] [dependencies] bytes = { version = "1.9.0", default-features = false, optional = true } -nom = { version = "8.0.0", default-features = false } zbus = { version = "5.0", default-features = false, optional = true, features = ["async-io"] } serde = { version = "1.0", default-features = false, optional = true } enumflags2 = { version = "0.7.11", default-features = false, optional = true } -static_assertions = "1.1.0" serde_repr = { version = "0.1.20", optional = true } [dev-dependencies] @@ -35,6 +33,7 @@ assert_matches = { version = "1.5.0", default-features = false } spiel = { path = ".", default-features = false } hound = "3.5.1" itertools = "0.14.0" +proptest = { version = "1.6.0", default-features = false, features = ["std", "attr-macro"] } [[example]] name = "filter_audio_data" @@ -46,6 +45,11 @@ name = "list_voices" path = "./examples/list_voices.rs" required-features = ["client"] +[[example]] +name = "list_voices_with_client" +path = "./examples/list_voices_client.rs" +required-features = ["client"] + [[example]] name = "write_to_file" path = "./examples/write_something_to_files.rs" @@ -55,3 +59,13 @@ required-features = ["client"] name = "write_to_pipe" path = "./examples/write_something_to_pipe.rs" required-features = ["client"] + +[[example]] +name = "make_provider" +path = "./examples/provider.rs" +required-features = ["client"] + +[[example]] +name = "test_provider" +path = "./examples/test_provider.rs" +required-features = ["client"] diff --git a/README.md b/README.md index c6a731c..b350be8 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,11 @@ Read and write audio, as well as mixed audio/events streams using the Spiel spee Note that features with an unmarked checkbox are not yet implemented. -- [X] `default`: none. This includes all basic protocol functionality, both from bytes and into bytes: `no_std` and `no_alloc`. It depends only on the `nom` and `memchr` crates. +- [X] `default`: none. This includes all basic protocol functionality, both from bytes and into bytes: `no_std` and `no_alloc`. This feature set requires only `core`. - [X] `client`: `std`, and pulls in the [`zbus`](https://crates.io/crates/zbus) crate. This provides a `Client` proxy type that ask for the speech provider to synthesize some speech, as well as query which voices and options are available. - [X] `reader`: `alloc`. This gives you a sans-io `Reader` type where you can [`Reader::push`] bytes into the buffer, and then [`Reader::try_read`] to the conversion into a [`Message`]. - This is _almost_ zero-copy. But currently requires a clone of the string if an event sent from the synthesizer has a name. -- [X] `alloc`: pulls in the [`bytes`](https://crates.io/crates/bytes), if `serde` is enabled. It exposes new types like [`crate::Message`] and [`crate::Event`], which are owned versions of [`crate::MessageBorrow`] and [`crate::EventBorrow`]. +- [X] `alloc`: pulls in the [`bytes`](https://crates.io/crates/bytes), if `serde` is enabled. It exposes new types like [`crate::MessageOwned`] and [`crate::EventOwned`], which are owned versions of [`crate::Message`] and [`crate::Event`]. - [X] `poll`: add wrapper functions that return `Poll::Pending` when there is not enough data in the buffer. This is not for general use, but rather only if you are creating an async integration. - [X] `serde`: activate [`serde::Serialize`] and [`serde::Deserialize`] on all types. - [ ] `provider`: activates [`std`] and pulls in the [`zbus`](https://crates.io/crates/zbus) crate. This will provide the `SpeechProvider` struct, which can be used to provide speech over the Spiel protocol via `DBus`. diff --git a/examples/filter_audio_data.rs b/examples/filter_audio_data.rs index ec077f6..fd776b2 100644 --- a/examples/filter_audio_data.rs +++ b/examples/filter_audio_data.rs @@ -1,6 +1,6 @@ use hound::{SampleFormat, WavSpec, WavWriter}; use itertools::Itertools; -use spiel::{read_message_type, MessageType}; +use spiel::{read_message, Message}; fn main() { let mut data: &[u8] = include_bytes!("../test.wav"); @@ -13,16 +13,14 @@ fn main() { }; let mut writer = WavWriter::create("out.wav", spec).expect("Can make wave writer!"); for _ in 0..55 { - let (data_next, msg) = - read_message_type(data, header).expect("to be able to read data"); + let (offset, msg) = read_message(data, header).expect("to be able to read data"); header = true; - if let MessageType::Audio { samples_offset, samples_len } = msg { - let ch = &data[samples_offset..samples_len]; - for (l, h) in ch.iter().tuples() { + if let Message::Audio(samples) = msg { + for (l, h) in samples.iter().tuples() { let sample = i16::from_le_bytes([*l, *h]); writer.write_sample(sample).expect("Can write to file"); } } - data = data_next; + data = &data[offset..]; } } diff --git a/examples/list_voices_client.rs b/examples/list_voices_client.rs new file mode 100644 index 0000000..a956c4f --- /dev/null +++ b/examples/list_voices_client.rs @@ -0,0 +1,20 @@ +use std::error::Error; + +use spiel::client::Client; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = Client::new().await?; + let providers = client.list_providers().await?; + for provider in providers { + let pname = provider.name().await?; + println!("Provider: {pname}"); + for voice in provider.voices().await? { + println!("\t{}", voice.name); + for lang in voice.languages { + println!("\t\t{lang}"); + } + } + } + Ok(()) +} diff --git a/examples/provider.rs b/examples/provider.rs new file mode 100644 index 0000000..d860dff --- /dev/null +++ b/examples/provider.rs @@ -0,0 +1,92 @@ +use std::{ + io::{PipeWriter, Write}, + os::fd::OwnedFd, + time::Duration, +}; + +use spiel::{write_message, Event, EventType, Message, Voice, VoiceFeatureSet}; +use tokio::time::sleep; +use zbus::{connection::Builder, fdo::Error, interface, zvariant::Fd}; + +struct MySpeechProvider { + voices: Vec, +} + +#[interface(name = "org.freedesktop.Speech.Provider")] +impl MySpeechProvider { + #[zbus(property)] + async fn voices(&self) -> Vec { + self.voices.clone() + } + #[zbus(property)] + async fn name(&self) -> String { + "Silly Provider!".to_string() + } + #[allow(clippy::too_many_arguments)] + async fn synthesize( + &self, + pipe_fd: Fd<'_>, + _text: &str, + _voice_id: &str, + _pitch: f64, + _rate: f64, + _is_ssml: bool, + _language: &str, + ) { + println!("Received a syntheiszer event!"); + // find a voice that matches the language &str + // actually synthesize text, + // etc. + // + // We are just gonna write a simple `Message::Event` + let header = Message::Version("0.01"); + let msg = Message::Event(Event { + typ: EventType::Word, + start: 69, + end: 420, + name: Some("Hello :)"), + }); + // buffer has fixed size in this case + let mut buffer: [u8; 1024] = [0; 1024]; + let writer: OwnedFd = pipe_fd + .try_into() + .map_err(|_| Error::IOError("Cannot open file descriptor".to_string())) + .expect("Unable to open file descriptor!"); + let mut file = PipeWriter::from(writer); + // TODO: implement a more convenient way to not have to store a buffer, etc. + let offset = + write_message(&header, &mut buffer).expect("Unable to write to buffer!"); + let bytes_written_buf = write_message(&msg, &mut buffer[offset..]) + .expect("Unable to write to buffer!"); + let bytes_written_fd = file + .write(&buffer[..bytes_written_buf + offset]) + .map_err(|_| Error::IOError("Cannot write to file descriptor".to_string())) + .expect("Unable to write to file descriptor!"); + println!("Wrote {bytes_written_fd} bytes to Fd"); + assert_eq!(bytes_written_buf + offset, bytes_written_fd); + } +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let voice = Voice { + name: "My Voice".to_string(), + id: "my-voice".to_string(), + mime_format: "audio/x-spiel,format=S32LE,channels=1,rate=22050".to_string(), + features: VoiceFeatureSet::empty(), + // English, New Zealand + languages: vec!["en-NZ".to_string()], + }; + let voices = Vec::from([voice]); + let provider = MySpeechProvider { voices }; + let _connection = Builder::session()? + .name("org.domain.Speech.Provider")? + .serve_at("/org/domain/Speech/Provider", provider)? + .build() + .await?; + + // wait forever for 60 seconds to test another program can receive the event! + println!("Started provider! Go check if it works using the `test-provider` example!"); + sleep(Duration::from_secs(60)).await; + Ok(()) +} diff --git a/examples/test_provider.rs b/examples/test_provider.rs new file mode 100644 index 0000000..f7e004b --- /dev/null +++ b/examples/test_provider.rs @@ -0,0 +1,53 @@ +//! This tool can be run with the `examples/provider.rs` binary to check the the service shows up +//! on DBus. +//! And that methods can be sent and dealt with appropriately. + +use std::{error::Error, io, io::Read, os::fd::OwnedFd}; + +use spiel::{read_message, Client, Event, EventType, Message}; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = Client::new().await?; + let (mut reader, writer_pipe) = io::pipe()?; + let writer = OwnedFd::from(writer_pipe); + let providers = client.list_providers().await?; + let mut found = false; + for provider in providers { + if provider.inner().destination() != "org.domain.Speech.Provider" { + continue; + } + found = true; + print!("TRY SEND..."); + provider.synthesize( + writer.into(), // pipe writer + "my-voice", // voice ID + "Hello!", // text to synthesize + 0.5, // pitch + 0.5, // rate + false, // SSML on + "en-NZ", // English, New Zealand + ) + .await?; + println!("SENT!"); + let mut buf = Vec::new(); + let bytes_read = reader.read_to_end(&mut buf)?; + println!("BYTES READ: {bytes_read}"); + let (bytes_read2, header) = read_message(&buf[..], false)?; + let (bytes_read3, msg) = read_message(&buf[bytes_read2..], true)?; + assert_eq!(bytes_read, bytes_read2 + bytes_read3); + assert_eq!(header, Message::Version("0.01")); + assert_eq!( + msg, + Message::Event(Event { + typ: EventType::Word, + start: 69, + end: 420, + name: Some("Hello :)"), + }) + ); + break; + } + assert!(found, "Could not find org.domain.Speech.Provider!"); + Ok(()) +} diff --git a/proptest-regressions/protocol.txt b/proptest-regressions/protocol.txt new file mode 100644 index 0000000..b4a0fca --- /dev/null +++ b/proptest-regressions/protocol.txt @@ -0,0 +1,8 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc 532f41e61cebd4f77a0ecbc9fcb99f48c95f8229580fd3c5249c03d6e63d6dea # shrinks to NeverPanicArgs = NeverPanicArgs { field0: [1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 127, 1, 111, 171, 102, 70, 127, 109, 7, 79, 170, 130, 186, 168, 160, 184, 44, 54, 179, 253, 237, 231, 72, 248, 179, 18, 56, 112, 100, 247, 181, 175, 213, 201, 145, 159, 80, 221, 4, 203, 127, 240, 171, 15, 89, 151, 222, 155, 236, 79, 112, 159, 60, 205, 21, 154, 80, 39, 223, 241, 202, 35, 81, 57, 210, 32, 243, 62, 175, 167, 90, 20, 10, 99, 161, 229, 214, 204, 5, 66, 58, 217, 126, 203, 34, 82, 157, 64, 107, 54, 84, 161, 185, 189, 82, 82, 210, 100, 120, 112, 75, 184, 14, 110, 26, 7, 94, 78, 102, 26, 197, 21, 113, 190, 217, 174, 53, 171, 12, 197, 190, 135, 236, 177, 176, 110, 210, 50, 80, 66, 201, 196, 83, 129, 24, 133, 36, 6, 197, 166, 216, 182, 141, 223, 122, 168, 40, 16, 254, 245, 227, 64, 224, 180, 133, 41, 18, 227, 231, 198, 68, 96, 52, 64, 25, 205, 161, 203, 182, 128, 77, 81, 197, 72, 194, 147, 118, 98, 253, 24, 169, 120, 227, 89, 216, 157, 93, 98, 135, 30, 109, 153, 95, 209, 62, 248, 226, 189, 188, 206, 178, 94, 220, 17, 18, 229, 7, 1, 237, 238, 140, 197, 30, 55, 197, 248, 57, 140, 38, 86, 142, 235, 234, 5, 98, 180, 16, 3, 106, 168, 146, 38, 159, 10, 212, 236, 248, 231, 194, 78, 173, 85, 231, 119, 36, 205, 147, 160, 41, 70, 123, 240, 21, 95, 160, 19, 130, 32, 235, 28, 82, 28, 84, 184, 80, 231, 253, 8, 33, 167, 215, 53, 201, 252, 242, 156, 246, 74, 193, 7, 223, 157, 160, 12, 75, 88, 174, 213, 152, 222, 19, 250, 89, 120, 17, 251, 139, 82, 212, 20, 118, 134, 145, 189, 1, 12, 137, 123, 66, 203, 65, 123, 221, 130, 169, 185, 230, 158, 217, 237, 81, 213, 82, 13, 55, 56, 167, 254, 254, 168, 196, 188, 115, 175, 124, 43, 240, 205, 169, 217, 222, 171, 68, 35, 72, 31, 76, 140, 0, 108, 54, 216, 9, 103, 158, 189, 93, 157, 251, 150, 146, 225, 46, 223, 18, 141, 176, 180, 254, 108, 199, 140, 162, 102, 197, 166, 179, 174, 67, 27, 165, 237, 102, 72, 13, 161, 247, 190, 82, 122, 237, 88, 190, 43, 247, 161, 234, 160, 108, 197, 199, 40, 42, 98, 120, 71, 251, 123, 119, 60, 238, 178, 51, 19, 104, 203, 145, 30, 110, 220, 64, 79, 5, 219, 231, 110, 253, 112, 5, 18, 228, 146, 86, 81, 105, 226, 54, 6, 133, 99, 232, 62, 108, 207, 207, 107, 187, 87, 228, 206, 87, 254, 181, 1, 241, 65, 161, 158, 2, 152, 66, 234, 227, 14, 162, 74, 54, 63, 187, 179, 235, 190, 30, 131, 114, 246, 136, 13, 225, 109, 209, 48, 177, 105, 180, 105, 145, 62, 186, 198, 80, 245, 233, 202, 129, 37, 217, 137, 5, 0, 55, 42, 106, 127, 195, 174, 127, 45, 50, 156, 48, 30, 228, 152, 118, 86, 72, 161, 117, 1, 104, 221, 204, 66, 39, 9, 164, 230, 12, 171, 9, 168, 211, 244, 105, 195, 155, 88, 27, 145, 203, 179, 169, 106, 147, 32, 7, 58, 251, 111, 203, 54, 210, 204, 159, 98, 6, 30, 31, 154, 68, 199, 228, 105, 48, 215, 163, 206, 48, 132, 35, 254, 195, 4, 233, 15, 128, 237, 52, 127, 141, 65, 156, 219, 252, 148, 245, 13, 147, 184, 46, 152, 35, 28, 150, 58, 180, 140, 245, 127, 46, 48, 76, 37, 152, 61, 223, 226, 145, 190, 175, 101, 229, 88, 15, 191, 2, 77, 24, 28, 90, 17, 126, 217, 182, 152, 49, 182, 5, 33, 111, 144, 100, 27, 15, 168, 231, 240, 118, 53, 234, 49, 213, 149, 193, 240, 194, 109, 85, 62, 115, 193, 36, 36, 168, 208, 164, 213, 204, 120, 84, 142, 134, 110, 31, 246, 117, 134, 124, 28, 123, 201, 162, 177, 218, 140, 67, 223, 150, 239, 17, 220, 9, 18, 221, 160, 119, 64, 190, 125, 52, 27, 55, 177, 249, 221, 228, 169, 247, 31, 240, 219, 11, 98, 29, 0, 183, 229, 115, 105, 198, 85, 210, 222, 153, 251, 145, 112, 68, 89, 99, 102, 122, 243, 116, 175, 55, 155, 83, 205, 8, 79, 53, 127, 59, 47, 234, 107, 156, 65, 189, 88, 72, 58, 141, 103, 189, 114, 201, 135, 236, 169, 3, 196, 14, 30, 98, 1, 116, 117, 171, 116, 111, 115, 80, 196, 191, 224, 176, 249, 12, 66, 126, 50, 193, 240, 69, 13, 44, 31, 242, 75, 10, 80, 88, 26, 233, 23, 117, 145, 147, 248, 95, 112, 121, 61, 168, 154, 59, 171, 226, 246, 49, 80, 45, 101, 138, 90, 73, 206, 236, 132, 60, 12, 196, 104, 201, 244, 137, 122, 0, 91, 231, 236, 46, 2, 87, 45, 61, 40, 31, 137, 10, 244, 234, 33, 4, 21, 187, 165, 164, 170, 163, 198, 87, 164, 235, 164, 121, 194, 217, 133, 49, 80, 74, 35, 83, 175, 160, 200, 242, 79, 161, 89, 180, 89, 169, 125, 34, 233, 69, 161, 230, 122, 234, 166, 255, 24, 197, 108, 184, 178, 46, 151, 66, 188, 168, 168, 185, 134, 215, 138, 23, 91, 188, 42, 204, 87, 22, 246, 86, 65, 95, 224, 55, 184, 226, 239, 133, 132, 204, 197, 36, 5, 95, 158, 74, 19, 63, 27, 225, 215, 235, 51, 214, 112, 52, 166, 207, 36, 87, 197, 219, 34, 118, 191, 17, 82, 234, 187, 193, 61, 248, 100, 234, 86, 142, 94, 48, 80, 191, 254, 46, 135, 202, 15, 237, 28, 43, 43, 185, 207, 65, 19, 215, 60, 177, 77, 6, 56, 167, 105, 69, 241, 16, 47, 59, 205, 139, 94, 52, 156, 213, 17, 123, 9, 172, 43, 42, 118, 135, 209, 58, 201, 234, 24, 174, 235, 154, 1, 27, 20, 254, 164, 241, 1, 204, 56, 92, 163, 53, 125, 87, 72, 207, 129, 26, 157, 55, 246, 58, 44, 81, 218, 2, 102, 182, 3, 253, 118, 49, 63, 68, 36, 242, 171, 10, 82, 49, 42, 176, 90, 24, 84, 169, 81, 156, 154, 194, 209, 118, 171, 156, 200, 191, 199, 162, 152, 122, 0, 137, 238, 99, 29, 61, 200, 188, 157, 158, 73, 229, 217, 130, 23, 244, 122, 63, 55, 252, 76, 43, 96, 206, 236, 76, 148, 17, 229, 110, 196, 160, 221, 99, 43, 229, 19, 83, 0, 195, 178, 215, 210, 99, 159, 96, 182, 53, 22, 192, 77, 42, 182, 58, 142, 61, 186, 216, 127, 95, 6, 73, 68, 241, 80, 108, 5, 96, 99, 215, 40, 113, 148, 188, 222, 26, 167, 119, 112, 166, 89, 177, 176, 63, 99, 210, 237, 132, 185, 113, 214, 204, 216, 246, 205, 5, 96, 124, 138, 99, 250, 49, 124, 59, 238, 228, 107, 215, 248, 37, 132, 48, 136, 51, 37, 154, 225, 60, 8, 42, 171, 206, 43, 228, 250, 72, 151, 68, 251, 175, 97, 187, 161, 222, 167, 39, 200, 190, 214, 76, 255, 76, 175, 6, 92, 212, 200, 8, 236, 60, 53, 134, 105, 158, 4, 253, 110, 31, 113, 61, 120, 92, 243, 211, 243, 220, 222, 163, 89, 68, 76, 93, 252, 106, 154, 158, 217, 97, 83, 92, 249, 173, 151, 20, 27, 98, 200, 214, 68, 192, 204, 131, 188, 180, 141, 146, 164, 17, 64, 194, 24, 113, 157, 153, 39, 50, 249, 105, 22, 143, 31, 112, 36, 77, 254, 77, 24, 133, 147, 67, 26, 161, 106, 94, 122, 49, 51, 189, 196, 63, 203, 35, 82, 254, 247, 18, 135, 86, 69, 112, 249, 154, 190, 166, 87, 225, 148, 231, 121, 108, 246, 5, 113, 176, 189, 243, 51, 125, 191, 145, 213, 88, 110, 211, 222, 200, 157, 97, 169, 125, 252, 18, 72, 235, 115, 157, 89, 54, 143, 131, 15, 35, 90, 152, 36, 243, 208, 169, 224, 172, 29, 209, 111, 146, 42, 85, 214, 164, 217, 172, 44, 41, 148, 248, 218, 127, 32, 245, 244, 223, 144, 104, 155, 230, 69, 11, 176, 195, 10, 52, 80, 128, 213, 17, 166, 22, 253, 79, 59, 89, 148, 170, 231, 48, 229, 222, 71, 156, 122, 86, 50, 64, 16, 160, 190, 19, 39, 150, 32, 202, 99, 80, 199, 42, 87, 38, 103, 167, 19, 214, 128, 32, 214, 204, 61, 23, 16, 155, 20, 39, 132, 6, 161, 13, 81, 49, 149, 136, 42, 0, 186, 193, 216, 106, 26, 176, 103, 119, 5, 57, 94, 141, 235, 0, 161, 146, 1, 11, 107, 243, 24, 227, 63, 192, 237, 146, 50, 235, 205, 164, 139, 226, 158, 133, 61, 66, 75, 92, 149, 42, 166, 43, 69, 79, 159, 188, 138, 205, 234, 79, 58, 173, 32, 51, 248, 99, 194, 116, 45, 208, 155, 245, 243, 226, 202, 80, 174, 36, 31, 144, 199, 36, 129, 208, 37, 204, 17, 96, 246, 43, 80, 124, 49, 57, 27, 217, 25, 25, 188, 221, 94, 7, 239, 200, 154, 83, 166, 141, 27, 23, 207, 134, 247, 152, 38, 232, 193, 19, 109, 165, 30, 139, 31, 136, 125, 97, 64, 242, 55, 113, 239, 79, 59, 238, 246, 237, 98, 217, 180, 141, 27, 10, 8, 242, 187, 110, 196, 113, 203, 92, 173, 15, 181, 242, 140, 138, 101, 162, 64, 104, 22, 129, 7, 247, 141, 48, 253, 115, 89, 141, 135, 201, 192, 134, 202, 244, 100, 145, 62, 166, 68, 34, 11, 188, 240, 128, 119, 187, 89, 94, 107, 180, 247, 232, 150, 232, 134, 132, 9, 168, 205, 149, 58, 94, 22, 169, 147, 105, 130, 72, 255, 144, 98, 158, 163, 14, 105, 82, 0, 55, 76, 4, 201, 6, 179, 165, 106, 94, 154, 122, 216, 54, 194, 227, 238, 121, 102, 38, 241, 212, 62, 188, 54, 244, 33, 57, 116, 10, 234, 61, 97, 188, 209, 200, 118, 68, 124, 180, 145, 192, 7, 68, 23, 14, 202, 19, 87, 156, 46, 251, 215, 224, 54, 66, 225, 54, 101, 117, 95, 120, 182, 191, 37, 167, 74, 229, 35, 171, 109, 133, 213, 62, 10, 87, 2, 35, 231, 189, 220, 12, 28, 226, 177, 72, 185, 56, 109, 168, 225, 214, 157, 188, 213, 204, 131, 193, 248, 86, 32, 33, 17, 184, 21, 100, 207, 171, 151, 169, 70, 110, 22, 124, 101, 148, 32, 210, 172, 187, 36, 186, 155, 161, 206, 157, 244, 69, 92, 126, 250, 23, 89, 167, 147, 69, 84, 100, 46, 154, 77, 157, 130, 40, 7, 251, 0, 48, 58, 250, 185, 36, 19, 184, 112, 57, 242, 96, 80, 249, 118, 32, 200, 221, 169, 216, 73, 0, 60, 18, 59, 60, 180, 47, 174, 200, 162, 232, 253, 153, 194, 27, 3, 18, 205, 209, 207, 250, 42, 216, 132, 41, 79, 74, 126, 157, 71, 245, 102, 132, 192, 238, 98, 35, 165, 220, 29, 95, 13, 143, 214, 39, 122, 230, 93, 114, 13, 84, 121, 214, 100, 19, 127, 188, 154, 241, 111, 91, 16, 140, 138, 74, 187, 220, 161, 247, 172, 184, 92, 57, 129, 163, 240, 245, 137, 86, 192, 6, 194, 72, 26, 16, 133, 221, 243, 133, 221, 107, 159, 132, 191, 209, 205, 88, 203, 90, 40, 58, 118, 18, 166, 178, 232, 1, 111, 124, 39, 147, 196, 99, 225, 170, 108, 235, 33, 42, 199, 255, 76, 81, 96, 95, 145, 63, 226, 197, 72, 88, 136, 69, 80, 175, 218, 78, 55, 162, 245, 194, 23, 182, 36, 195, 20, 53, 250, 208, 221, 160, 255, 205, 222, 23, 154, 169, 110, 144, 22, 143, 19, 133, 79, 121, 159, 108, 15, 104, 0, 234, 97, 232, 88, 169, 89, 198, 11, 113, 157, 7, 16, 248, 8, 166, 151, 15, 187, 30, 4, 227, 60, 213, 242, 128, 160, 198, 122, 9, 246, 208, 26, 229, 113, 141, 189, 91, 52, 159, 180, 32, 247, 242, 184, 41, 186, 73, 176, 131, 53, 131, 202, 107, 31, 49, 227, 131, 91, 29, 202, 110, 151, 128, 176, 183, 179, 164, 233, 230, 38, 233, 15, 236, 144, 57, 162, 26, 70, 89, 73, 140, 167, 4, 180, 18, 36, 215, 105, 255, 90, 70, 250, 127, 248, 29, 86, 222, 33, 60, 97, 147, 193, 102, 247, 86, 227, 242, 252, 209, 19, 118, 58, 10, 63, 103, 246, 21, 144, 2, 93, 160, 163, 80, 61, 158, 38, 60, 235, 136, 49, 69, 197, 62, 248, 76, 115, 20, 206, 219, 9, 173, 142, 18, 32, 181, 156, 219, 1, 189, 26, 71, 212, 120, 99, 76, 30, 18, 20, 171, 14, 119, 48, 33, 150, 45, 145, 117, 133, 162, 178, 25, 111, 133, 49, 87, 135, 168, 153, 86, 80, 202, 210, 175, 38, 245, 87, 64, 142, 19, 200, 74, 126, 225, 187, 175, 224, 60, 108, 137, 136, 205, 126, 37, 165, 198, 32, 89, 100, 33, 172, 25, 147, 185, 146, 32, 40, 29, 249, 213, 96, 172, 25, 82, 145, 239, 78, 129, 5, 28, 50, 29, 245, 84, 175, 95, 89, 49, 209, 187, 169, 52, 241, 12, 14, 128, 13, 126, 21, 245, 136, 127, 71, 149, 98, 151, 210, 176, 196, 134, 226, 82, 48, 7, 87, 169, 166, 51, 244, 178, 206, 91, 125, 213, 139, 68, 12, 103, 78, 45, 6, 254, 192, 78, 190, 197, 17, 102, 62, 167, 254, 155, 191, 163, 149, 114, 24, 4, 210, 65, 57, 168, 168, 94, 114, 252, 247, 229, 31, 142, 214, 64, 7, 59, 251, 253, 226, 204, 155, 210, 22, 179, 181, 8, 238, 34, 55, 162, 198, 249, 79, 165, 252, 233, 150, 170, 229, 202, 189, 164, 220, 77, 228, 168, 100, 254, 60, 98, 16, 18, 25, 140, 125, 90, 115, 158, 37, 222, 169, 14, 142, 238, 179, 212, 252, 3, 163, 132, 106, 215, 144, 89, 115, 9, 200, 24, 8, 0, 211, 131, 25, 71, 8, 199, 94, 194, 162, 156, 84, 179, 104, 118, 124, 25, 150, 5, 89, 39, 195, 107, 109, 71, 178, 90, 4, 36, 210, 240, 133, 159, 136, 164, 91, 161, 104, 201, 110, 196, 76, 18, 239, 197, 75, 128, 153, 123, 51, 110, 196, 31, 248, 242, 45, 96, 118, 12, 232, 45, 125, 26, 32, 63, 71, 156, 168, 189, 108, 154, 81, 143, 177, 158, 106, 140, 6, 147, 114, 32, 157, 246, 152, 52, 23, 116, 159, 242, 192, 148, 191, 247, 60, 157, 75, 93, 174, 9, 229, 247, 91, 196, 85, 72, 128, 64, 50, 117, 211, 116, 0, 105, 102, 149, 167, 47, 31, 121, 228, 171, 35, 209, 51, 165, 44, 69, 129, 222, 102, 66, 200, 83, 202, 151, 55, 141, 223, 208, 99, 115, 160, 219, 142, 70, 239, 49, 29, 95, 216, 118, 70, 25, 64, 87, 45, 115, 168, 92, 117, 95, 151, 161, 73, 132, 182, 121, 181, 33, 22, 165, 64, 9, 249, 174, 108, 75, 92, 26, 244, 18, 229, 188, 118, 88, 140, 14, 216, 10, 192, 90, 185, 210, 227, 76, 156, 87, 235, 165, 118, 0, 49, 164, 38, 61, 220, 209, 151, 63, 165, 25, 243, 11, 90, 195, 2, 108, 125, 164, 108, 39, 29, 36, 103, 91, 157, 231, 56, 146, 77, 209, 185, 22, 57, 178, 139, 51, 69, 43, 115, 62, 133, 185, 46, 57, 115, 181, 161, 77, 212, 20, 21, 129, 251, 162, 69, 118, 105, 22, 247, 207, 122, 60, 222, 194, 230, 110, 220, 5, 60, 212, 82, 118, 192, 7, 104, 224, 165, 148, 170, 148, 32, 117, 10, 245, 32, 20, 226, 81, 134, 84, 22, 194, 187, 47, 8, 199, 30, 39, 139, 65, 125, 105, 147, 84, 57, 46, 141, 50, 240, 175, 57, 216, 50, 125, 147, 54, 85, 66, 94, 167, 79, 13, 89, 223, 148, 103, 50, 136, 206, 144, 192, 6, 97, 71, 73, 224, 35, 183, 203, 217, 168, 7, 131, 223, 212, 192, 97, 216, 61, 103, 185, 236, 206, 50, 222, 214, 189, 8, 39, 135, 110, 16, 122, 19, 116, 170, 137, 157, 255, 245, 37, 173, 210, 56, 33, 29, 124, 37, 141, 2, 65, 60, 241, 82, 62, 121, 0, 147, 136, 49, 79, 170, 214, 45, 79, 189, 30, 33, 15, 84, 79, 227, 96, 73, 70, 120, 239, 164, 109, 63, 43, 164, 25, 83, 1, 0, 209, 143, 7, 173, 121, 113, 192, 69, 77, 27, 126, 18, 122, 241, 127, 96, 210, 108, 226, 30, 203, 106, 177, 173, 52, 69, 119, 129, 239, 68, 223, 64, 76, 124, 178, 8, 66, 81, 154, 199, 48, 69, 252, 106, 232, 180, 239, 59, 77, 234, 1, 96, 128, 110, 164, 140, 80, 140, 20, 151, 98, 61, 222, 74, 77, 27, 155, 109, 255, 81, 59, 247, 227, 66, 205, 5, 151, 14, 174, 64, 69, 69, 162, 207, 192, 21, 22, 202, 164, 154, 95, 158, 6, 235, 134, 68, 174, 237, 177, 134, 230, 80, 185, 2, 144, 131, 84, 44, 185, 241, 247, 226, 124, 177, 93, 162, 73, 142, 31, 235, 217, 198, 109, 194, 221, 205, 179, 103, 74, 160, 6, 137, 93, 248, 164, 130, 104, 83, 147, 254, 133, 152, 204, 78, 230, 183, 100, 44, 186, 172, 16, 115, 35, 207, 196, 4, 195, 32, 228, 133, 107, 58, 126, 54, 237, 90, 72, 24, 228, 241, 38, 231, 245, 180, 183, 14, 93, 9, 75, 14, 228, 114, 86, 195, 215, 0, 32, 242, 53, 221, 105, 38, 253, 254, 101, 169, 67, 159, 44, 149, 107, 156, 106, 219, 7, 58, 70, 28, 22, 11, 219, 37, 216, 121, 114, 231, 22, 138, 218, 100, 221, 188, 94, 235, 211, 213, 202, 172, 62, 44, 27, 80, 224, 25, 226, 140, 179, 24, 15, 235, 116, 155, 25, 53, 182, 190, 32, 248, 36, 11, 238, 45, 12, 37, 133, 91, 60, 17, 36, 220, 234, 199, 130, 201, 10, 4, 194, 115, 43, 237, 194, 11, 17, 135, 240, 122, 69, 90, 196, 112, 172, 9, 210, 34, 239, 81, 208, 159, 7, 77, 145, 200, 165, 78, 59, 52, 38, 204, 226, 35, 16, 219, 31, 56, 136, 64, 182, 216, 227, 145, 155, 8, 112, 6, 76, 140, 27, 25, 100, 129, 148, 66, 82, 125, 63, 243, 96, 215, 114, 37, 238, 52, 128, 23, 24, 49, 221, 37, 133, 208, 248, 26, 115, 24, 122, 32, 219, 15, 214, 94, 173, 21, 25, 187, 40, 152, 85, 11, 56, 123, 234, 56, 101, 33, 92, 222, 25, 152, 124, 84, 103, 197, 134, 95, 214, 48, 182, 188, 211, 14, 131, 222, 106, 4, 84, 224, 68, 27, 30, 91, 121, 114, 207, 241, 90, 98, 232, 177, 222, 244, 184, 102, 205, 27, 89, 25, 89, 51, 72, 194, 157, 38, 21, 158, 35, 155, 241, 238, 112, 93, 54, 184, 43, 1, 111, 166, 129, 247, 109, 138, 78, 5, 112, 185, 135, 211, 168, 137, 10, 73, 22, 254, 157, 16, 22, 133, 151, 30, 186, 18, 231, 221, 3, 72, 21, 214, 172, 50, 131, 171, 221, 216, 123, 33, 58, 196, 247, 117, 107, 211, 198, 51, 242, 205, 151, 231, 163, 156, 121, 244, 109, 62, 192, 74, 63, 179, 85, 85, 117, 238, 77, 94, 227, 29, 138, 1, 178, 144, 209, 137, 109, 194, 21, 217, 222, 36, 62, 85, 69, 129, 171, 65, 157, 127, 246, 238, 172, 138, 132, 194, 93, 218, 53, 26, 51, 243, 169, 58, 21, 160, 120, 96, 229, 129, 88, 255, 99, 120, 107, 103, 174, 184, 53, 183, 97, 87, 57, 105, 212, 185, 70, 83, 232, 5, 2, 176, 72, 174, 110, 123, 33, 201, 76, 2, 100, 127, 156, 169, 76, 253, 227, 165, 70, 239, 251, 129, 75, 140, 161, 72, 183, 152, 146, 6, 137, 14, 144, 49, 130, 226, 82, 96, 109, 72, 143, 208, 21, 57, 19, 4, 88, 29, 124, 211, 249, 222, 76, 41, 150, 116, 98, 65, 60, 129, 59, 192, 40, 208, 97, 159, 153, 183, 205, 178, 144, 131, 135, 120, 211, 17, 142, 39, 198, 25, 173, 75, 144, 178, 195, 127, 242, 24, 141, 229, 65, 149, 152, 92, 152, 10, 54, 11, 124, 136, 181, 129, 122, 194, 144, 132, 10, 41, 109, 92, 51, 45, 182, 210, 168, 35, 213, 59, 90, 30, 55, 78, 42, 238, 106, 80, 46, 191, 92, 220, 38, 35, 57, 189, 63, 228, 24, 165, 130, 165, 238, 219, 250, 132, 225, 49, 234, 191, 119, 221, 136, 128, 171, 221, 150, 150, 176, 202, 164, 221, 211, 125, 40, 125, 43, 173, 254, 132, 138, 10, 123, 66, 128, 188, 89, 58, 35, 118, 137, 229, 228, 97, 200, 247, 133, 142, 107, 162, 31, 204, 245, 44, 75, 44, 242, 214, 193, 34, 69, 212, 130, 125, 149, 75, 29, 232, 134, 96, 176, 57, 83, 90, 66, 160, 189, 157, 205, 119, 26, 179, 207, 117, 55, 138, 69, 236, 89, 49, 151, 186, 69, 7, 63, 229, 173, 68, 166, 159, 211, 236, 198, 38, 201, 193, 54, 38, 231, 14, 19, 24, 137, 191, 219, 31, 171, 35, 2, 161, 136, 167, 63, 60, 230, 185, 48, 155, 169, 9, 223, 208, 49, 118, 86, 253, 86, 38, 145, 128, 85, 213, 220, 203, 89, 77, 125, 110, 219, 147, 41, 238, 216, 34, 113, 98, 143, 18, 145, 36, 47, 103, 191, 211, 200, 160, 142, 231, 144, 227, 24, 175, 131, 33, 173, 83, 205, 247, 209, 11, 174, 60, 155, 216, 189, 193, 28, 163, 63, 34, 93, 69, 235, 162, 219, 190, 128, 115, 212, 166, 141, 155, 253, 169, 170, 159, 200, 150, 107, 156, 139, 251, 48, 125, 13, 129, 174, 223, 83, 123, 215, 149, 176, 82, 215, 158, 225, 247, 121, 126, 206, 110, 11, 217, 122, 20, 226, 75, 158, 87, 191, 221, 97, 206, 188, 97, 35, 38, 98, 22, 45, 43, 121, 1, 164, 234, 69, 89, 190, 226, 56, 8, 156, 102, 212, 64, 161, 33, 152, 141, 89, 67, 103, 239, 96, 231, 218, 108, 112, 30, 208, 125, 86, 87, 103, 96, 254, 206, 185, 221, 238, 29, 196, 244, 44, 27, 247, 8, 117, 205, 70, 216, 155, 136, 232, 12, 237, 164, 62, 68, 181, 170, 23, 64, 21, 199, 4, 171, 47, 29, 3, 139, 98, 226, 6, 145, 130, 105, 123, 216, 7, 239, 132, 163, 90, 109, 4, 201, 146, 233, 232, 40, 25, 58, 215, 162, 219, 27, 134, 95, 179, 37, 74, 111, 60, 55, 145, 74, 226, 78, 149, 173, 2, 34, 249, 231, 105, 183, 88, 156, 149, 213, 110, 230, 223, 59, 60, 246, 78, 70, 39, 100, 251, 130, 183, 208, 25, 44, 86, 190, 233, 163, 120, 21, 132, 203, 26, 170, 224, 15, 116, 81, 107, 65, 168, 187, 64, 245, 162, 201, 46, 152, 213, 90, 67, 202, 45, 122, 129, 213, 28, 214, 75, 123, 38, 1, 48, 59, 28, 151, 76, 28, 0, 230, 12, 208, 21, 245, 252, 18, 22, 10, 40, 4, 48, 144, 188, 219, 97, 252, 163, 197, 192, 114, 52, 190, 0, 66, 12, 101, 60, 139, 132, 54, 145, 78, 105, 4, 126, 236, 78, 66, 105, 11, 89, 87, 109, 143, 118, 178, 111], field1: true } +cc 2c6c3194736ceefb8b04ac8f5958a0f7ceb397ab7cc688925f82498c5a067dbf # shrinks to NeverPanicArgs = NeverPanicArgs { field0: [2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], field1: true } diff --git a/src/client.rs b/src/client.rs index 546cc7a..10458db 100644 --- a/src/client.rs +++ b/src/client.rs @@ -11,7 +11,8 @@ //! //! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html //! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, -use enumflags2::{bitflags, BitFlag, BitFlags}; + +use enumflags2::{bitflags, BitFlags}; use zbus::proxy; /// An individual voice feature. @@ -31,13 +32,13 @@ use zbus::proxy; )] #[repr(u64)] pub enum VoiceFeature { - /// Send [`spiel::Event`] when starting/ending the speech within a word. + /// Send [`crate::Event`] when starting/ending the speech within a word. EventsWord, - /// Send [`spiel::Event`] when starting/ending the speech within a sentence. + /// Send [`crate::Event`] when starting/ending the speech within a sentence. EventsSentence, - /// Send [`spiel::Event`] when starting/ending the speech within a given range. + /// Send [`crate::Event`] when starting/ending the speech within a given range. EventsRange, - /// Send [`spiel::Event`] when starting/ending the speech between an SSML `` tag. + /// Send [`crate::Event`] when starting/ending the speech between an SSML `` tag. EventsSSMLMark, /// Will interpret `` SSMLSayAsDate, @@ -69,36 +70,41 @@ pub enum VoiceFeature { SSMLToken, } -static_assertions::assert_impl_all!(VoiceFeature: BitFlag, Type); - -use zbus::zvariant::{Structure, Type, Value}; +use zbus::zvariant::{Type, Value}; #[derive(Copy, Clone, Debug, PartialEq, Eq, Type, serde::Serialize, serde::Deserialize)] #[repr(transparent)] #[serde(transparent)] pub struct VoiceFeatureSet(BitFlags); +impl VoiceFeatureSet { + #[must_use] + pub fn empty() -> Self { + VoiceFeatureSet(BitFlags::::EMPTY) + } +} + impl TryFrom> for VoiceFeatureSet { type Error = zbus::zvariant::Error; fn try_from(zv: Value<'_>) -> Result { Ok(VoiceFeatureSet(BitFlags::from_bits_truncate(TryInto::::try_into(zv)?))) } } -impl<'a> From for Structure<'a> { - fn from(vfs: VoiceFeatureSet) -> Structure<'a> { - Structure::from((vfs.0.bits(),)) - } -} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Type, PartialEq, Eq)] +#[serde(transparent)] +#[repr(transparent)] +pub struct VoiceList(Vec); /// All the information about a voice, including its audio output format, capabilities, and a /// string-based unique ID in order to reference it. -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Value, Type, PartialEq, Eq)] +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Type, PartialEq, Eq)] #[zvariant(signature = "ssstas")] pub struct Voice { /// A human-readable name. - name: String, + pub name: String, /// A unique identifier for calling [`ProviderProxy::synthesize`]. - id: String, + pub id: String, /// A MIME followed by audio format information in GStreamer-Caps style, e.g.: /// /// - `audio/x-raw,format=S32LE,channels=2,rate=22050` @@ -111,11 +117,25 @@ pub struct Voice { /// - Sample rate: 22050 /// /// It is up to the caller to determine what to do with this string. - mime_format: String, - /// Bitflag of [`VoiceFeatures`]. - features: VoiceFeatureSet, + pub mime_format: String, + /// Bitflag of [`VoiceFeature`]. + pub features: VoiceFeatureSet, /// A list of BCP 47 tags. - languages: Vec, + pub languages: Vec, +} + +impl TryFrom> for Voice { + type Error = zbus::zvariant::Error; + fn try_from(zv: Value<'_>) -> Result { + let (name, id, mime_format, features, languages) = zv.try_into()?; + Ok(Voice { name, id, mime_format, features, languages }) + } +} +impl From for Value<'_> { + fn from(voice: Voice) -> Self { + let Voice { name, id, mime_format, features, languages } = voice; + (name, id, mime_format, features.0.bits(), languages).into() + } } #[proxy(interface = "org.freedesktop.Speech.Provider")] @@ -153,7 +173,56 @@ fn serialize_deserialize_dbus() { features: VoiceFeatureSet(VoiceFeature::EventsWord | VoiceFeature::SSMLSub), }; let ctxt = Context::new_dbus(LE, 0); - let encoded = to_bytes(ctxt, &voice).unwrap(); - let (voice2, _decoded) = encoded.deserialize::().unwrap(); + let encoded = to_bytes(ctxt, &voice).expect("Unable to serialize over DBus"); + let (voice2, _decoded) = encoded + .deserialize::() + .expect("Unable to deserialzie from DBus data"); assert_eq!(voice, voice2); } + +use zbus::{fdo::DBusProxy, Connection}; + +pub struct Client<'a> { + con: Connection, + fdo: DBusProxy<'a>, +} + +impl Client<'_> { + /// Create a new Spiel client. + /// + /// # Errors + /// + /// Anything that causes the `DBus` connection to fail will be returned as an error. + /// You need an active session in order to complete this. + pub async fn new() -> Result { + let con = Connection::session().await?; + let fdo = DBusProxy::new(&con).await?; + Ok(Client { con, fdo }) + } + /// Get a list of speech providers. + /// + /// # Errors + /// + /// Any error that causes `DBus` to either be: + /// + /// 1. Unable to query the session for activatable names, or + /// 2. Stops the creation of proxies pointing to a name ending in `Speech.Provider`. + pub async fn list_providers(&self) -> Result>, zbus::Error> { + let names = + self.fdo.list_names() + .await? + .into_iter() + .filter(|name| name.ends_with(".Speech.Provider")); + let mut providers = Vec::new(); + for name in names { + let proxy = ProviderProxy::new( + &self.con, + name.clone(), + format!("/{}", name.as_str().replace('.', "/")), + ) + .await?; + providers.push(proxy); + } + Ok(providers) + } +} diff --git a/src/lib.rs b/src/lib.rs index 39e807f..568654b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,68 +4,32 @@ clippy::all, clippy::std_instead_of_core, clippy::std_instead_of_alloc, - clippy::alloc_instead_of_core + clippy::alloc_instead_of_core, + clippy::print_stdout, + clippy::print_stderr, + clippy::unwrap_used )] -#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(not(any(feature = "std", test)), no_std)] + +#[cfg(not(any(target_pointer_width = "64", target_pointer_width = "32")))] +compile_error!("You need at least 32-bit pointers to use this crate."); mod protocol; +#[cfg(feature = "poll")] +pub use protocol::poll_read_message; #[cfg(feature = "reader")] pub use protocol::Reader; -#[cfg(feature = "poll")] -pub use protocol::{poll_read_message_borrow, poll_read_message_type}; pub use protocol::{ - read_message_borrow, read_message_type, write_message_type, write_message_type_unchecked, - EventBorrow, MessageBorrow, MessageType, + read_message, read_message_type, write_message, ChunkType, Event, EventType, Message, + MessageType, }; #[cfg(feature = "alloc")] -pub use protocol::{Event, Message}; +pub use protocol::{EventOwned, MessageOwned}; #[cfg(feature = "alloc")] extern crate alloc; #[cfg(feature = "client")] pub mod client; - -#[repr(u64)] -#[derive(Clone, Copy, Debug)] -/// A bitfield of potential voice features that can be advertised to consumers. -pub enum VoiceFeature { - /// Provider dispatches event when about to speak word. - EventsWord, - /// Provider dispatches event when about to speak sentence. - EventsSentence, - /// Provider dispatches event when about to speak unspecified range. - EventsRange, - /// Provider dispatches event when SSML mark is reached. - EventsSsmlMark, - /// - SsmlSayAsDate, - /// - SsmlSayAsTime, - /// - SsmlSayAsTelephone, - /// - SsmlSayAsCharacters, - /// - SsmlSayAsCharactersGlyphs, - /// - SsmlSayAsCardinal, - /// - SsmlSayAsOrdinal, - /// - SsmlSayAsCurrency, - /// - SsmlBreak, - /// - SsmlSub, - /// - SsmlPhoneme, - /// - SsmlEmphasis, - /// - SsmlProsody, - /// - SsmlSentenceParagraph, - /// - SsmlToken, -} +#[cfg(feature = "client")] +pub use client::{Client, Voice, VoiceFeatureSet}; diff --git a/src/protocol.rs b/src/protocol.rs index 0b86830..ea113a3 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -1,31 +1,70 @@ //! Spiel protocol defenitions. #[cfg(feature = "alloc")] -use alloc::string::String; +use alloc::{string::String, string::ToString}; #[cfg(feature = "poll")] use core::task::Poll; +use core::{fmt, str::Utf8Error}; + +#[derive(Debug, Eq, PartialEq)] +pub enum Error { + /// Reader does not have enough bytes to complete its read. + /// Inner value specifies how many further bytes are needed. + NotEnoughBytes(usize), + /// Read an event type not specified in [`EventType`]. + InvalidEventType(u8), + /// Read a chunk type not specified in [`ChunkType`]. + InvalidChunkType(u8), + /// Writer does not have enough space to write into the buffer. + /// Inner value specifies how many more bytes are necessary. + NotEnoughSpace(usize), + /// Unable to decode the str as utf8. + /// Since the text should be ASCII conformant, this should never happen. + Utf8(Utf8Error), +} +impl fmt::Display for Error { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::NotEnoughBytes(more) => { + fmt.write_str("Not enough bytes; need ")?; + more.fmt(fmt)?; + fmt.write_str(" more") + } + Error::InvalidEventType(ty) => { + fmt.write_str("Invalid event type: ")?; + ty.fmt(fmt)?; + fmt.write_str(". Valid values are 1, 2, 3, 4") + } + Error::InvalidChunkType(ty) => { + fmt.write_str("Invalid chunk type: ")?; + ty.fmt(fmt)?; + fmt.write_str(". Valid values are 1, 2") + } + Error::NotEnoughSpace(more) => { + fmt.write_str("Not enough space in write buffer; need ")?; + more.fmt(fmt)?; + fmt.write_str(" more") + } + Error::Utf8(utfe) => { + fmt.write_str("UTF-8 Error: ")?; + utfe.fmt(fmt) + } + } + } +} +impl core::error::Error for Error {} #[cfg(feature = "alloc")] use bytes::Bytes; #[cfg(feature = "reader")] use bytes::BytesMut; -#[cfg(feature = "reader")] -use nom::Needed; -use nom::{ - bytes::streaming::take, - combinator::{map, map_res}, - error::{Error, ErrorKind}, - multi::length_data, - number::{streaming::u32, Endianness}, - IResult, Input, Parser, -}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; #[cfg(feature = "alloc")] #[derive(Debug, PartialEq, Clone)] #[cfg_attr(all(feature = "serde", feature = "alloc"), derive(Serialize, Deserialize))] -pub struct Event { +pub struct EventOwned { pub typ: EventType, pub start: u32, pub end: u32, @@ -34,7 +73,7 @@ pub struct Event { #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct EventBorrow<'a> { +pub struct Event<'a> { pub typ: EventType, pub start: u32, pub end: u32, @@ -50,7 +89,7 @@ pub enum ChunkType { } #[repr(u8)] -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Copy)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum EventType { Word = 1, @@ -60,7 +99,7 @@ pub enum EventType { } impl EventType { - fn to_ne_bytes(&self) -> [u8; 1] { + fn to_ne_bytes(self) -> [u8; 1] { match self { EventType::Word => [1], EventType::Sentence => [2], @@ -70,254 +109,44 @@ impl EventType { } } -#[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum MessageType { - Version { - version: [char; 4], - }, - /// With this variant, you should then be able to: - Audio { - /// The index to the start of the data slice where the audio begins. - samples_offset: usize, - /// This length of the slice you should take in order to grab the audio frame. - samples_len: usize, - }, - Event { - name_offset: usize, - typ: EventType, - start: u32, - end: u32, - name_len: usize, - }, -} - -impl MessageType { - /// Determine how many bytes are necessary in order to write this (and its accompanying data - /// slice) to a new buffer. - fn bin_length(&self) -> usize { - match self { - // +1 for null byte - // +1 for EventType::Event specifier - // +4 for name_offset as u32 - MessageType::Event { name_len, .. } => name_len + 6, - // +1 for EventType::Audio specifier - // +4 for samples_offset as u32 - // +4 for start as u32 - // +4 for end as u32 - MessageType::Audio { samples_len, .. } => samples_len + 15, - // 4 bytes for the version inforamtion; NO TERMINATING NULL BYTE! - MessageType::Version { .. } => 4, - } - } - /// Get the offset of the accompanying data in a buffer. - fn offset(&self) -> usize { - match self { - MessageType::Version { .. } => 0, - MessageType::Audio { samples_offset, .. } => *samples_offset, - MessageType::Event { name_offset, .. } => *name_offset, - } +fn read_version(buf: &[u8]) -> Result<(usize, Message<'_>), Error> { + if buf.len() < 4 { + return Err(Error::NotEnoughBytes(4 - buf.len())); } + Ok((4, Message::Version(str::from_utf8(&buf[..4]).map_err(Error::Utf8)?))) } - -fn read_version(buf: &[u8]) -> IResult<&[u8], MessageType> { - map( - map(take(4usize), |bytes: &[u8]| { - // SAFETY: This is allowed because we already know we've taken 4, and exactly 4 - // bytes at this point! - [ - char::from(bytes[0]), - char::from(bytes[1]), - char::from(bytes[2]), - char::from(bytes[3]), - ] - }), - |s| MessageType::Version { version: s }, - ) - .parse(buf) -} - -fn read_version_borrow(buf: &[u8]) -> IResult<&[u8], MessageBorrow<'_>> { - map( - map_res(take(4usize), |bytes: &[u8]| str::from_utf8(&bytes[..4])), - MessageBorrow::Version, - ) - .parse(buf) -} -/// [`read_message_type`] provides a method to _attempt_ to read Spiel messages from a buffer. -/// See fields on [`MessageType`] to interpret the values that are returned in the happy-path case. -/// NOTE: you need to keep the buffer passed to this function alive in order to extract the audio -/// buffer. -/// -/// ```no_run -/// use spiel::{ -/// read_message_type, -/// MessageType, -/// }; -/// use itertools::Itertools; -/// // Just imagine that you have mutable data from somewhere; this obviously won't run! -/// // See the [`filter_audio_data`] example to see how this is used in practice. -/// let mut data: &[u8] = &[0u8]; -/// let mut header = false; -/// while let Ok((data_next, msg)) = read_message_type(data, header) { -/// header = true; -/// if let MessageType::Audio { -/// samples_offset, -/// samples_len, -/// } = msg -/// { -/// // NOTE: here is why the data must stay alive; `read_message_type` only gives you -/// // enough data to _reference_ the correct slices. It does not get these slices for you. -/// // If you want this functionality: receiving [`Message`] or [`MessageBorrow`]s -/// // directly, you want [`spiel::Reader`]. -/// let ch = &data[samples_offset..samples_len]; -/// for (l, h) in ch.iter().tuples() { -/// let sample = i16::from_le_bytes([*l, *h]); -/// // write this sample to a file/pipe/etc. -/// } -/// } -/// data = data_next; -/// } -/// ``` -/// -/// It fails under any condition which `nom` will fail to parse, but that usually comes down to 2 -/// major cases: -/// -/// # Errors -/// -/// 1. Not enough data to parse a whole message (Error, recoverable, try again later) -/// 2. Invalid data (Failure, this means completely unrecoverable) -pub fn read_message_type(buf: &[u8], header_already_read: bool) -> IResult<&[u8], MessageType> { - if !header_already_read { - return read_version(buf); - } - let (data, ct) = read_chunk_type(buf)?; - match ct { - ChunkType::Audio => read_message_audio(data), - ChunkType::Event => read_message_event(data), +fn read_version_type(buf: &[u8]) -> Result<(usize, MessageType), Error> { + if buf.len() < 4 { + return Err(Error::NotEnoughBytes(4 - buf.len())); } + let buf_4: &[u8; 4] = &buf[..4].try_into().expect("Exactly 4 bytes"); + Ok((4, MessageType::Version { version: buf_4.map(char::from) })) } -pub enum WriteError { - NotEnoughSpaceInBuffer, - DataNotLongEnough, -} - -/// [`write_message_type`] will attempt to write a message's type and its data to a byte strim. -/// Returns the number of bytes written to the buffer upon success. +/// [`read_message`] takes a buffer and triees to read a [`Message`] from it. +/// This borrows data from the buffer, then returns a tuple containing: /// -/// # Errors -/// -/// - `data` buffer doesn't contain the data it's said to in `MessageType` -/// - `buf` buffer isn't long enough to take all the data. -/// -pub fn write_message_type( - mt: MessageType, - data: &[u8], - buf: &mut [u8], -) -> Result { - if mt.bin_length() > buf.len() { - return Err(WriteError::NotEnoughSpaceInBuffer); - } - if mt.offset() + mt.bin_length() > data.len() { - return Err(WriteError::DataNotLongEnough); - } - Ok(write_message_type_unchecked(mt, data, buf)) -} - -/// [`write_message_type_unchecked`] will write a message's type and its data to a byte stream. -/// -/// # Returns -/// -/// The number of bytes written. -/// -/// # Panics -/// -/// Panics if the space needed for writing the message is not large enough. -/// Or if the data slice does not contain enough data at the requested offset. -/// Use [`MessageType::bin_length`] to determine how long the buffer must be, -/// and use [`MessageType::offset`] to determine where in the data slice the accompanying data -/// should lie. -/// -/// # SAFETY -/// -/// Callers are required to either: -/// -/// 1. Never give a partial `data` buffer (i.e., you may not continually pass -/// an offet further into the buffer upon successive calls). You *must* pass the entire data buffer -/// every time. -/// 2. Modify the offsets in the [`MessageType::Audio`] and [`MessageType::Event`] variants to -/// match the offsets read from the `data` buffer (the value returned from the function). -/// -/// Use [`write_message_type`] if you want these failure cases handled for you. -pub fn write_message_type_unchecked(mt: MessageType, data: &[u8], buf: &mut [u8]) -> usize { - match mt { - MessageType::Version { version } => { - let buf_first_4 = &mut buf[..4]; - // We could also have gotten it from the underlying data slice. Either way. - let first_4_data = &[ - version[0] as u8, - version[1] as u8, - version[2] as u8, - version[3] as u8, - ]; - buf_first_4.copy_from_slice(first_4_data); - 4 - } - MessageType::Audio { samples_offset, samples_len } => { - buf[0] = 1; - let samp_writer = &mut buf[1..5]; - #[allow(clippy::cast_possible_truncation)] - samp_writer.copy_from_slice(&(samples_len as u32).to_ne_bytes()); - let data_reader = - &data[(5 + samples_offset)..(samples_offset + samples_len + 5)]; - let data_writer = &mut buf[5..(samples_len + 5)]; - data_writer.copy_from_slice(data_reader); - 5 + samples_len - } - MessageType::Event { typ, start, end, name_offset, name_len } => { - buf[0] = 2; - let typ_write = &mut buf[1..2]; - typ_write.copy_from_slice(&typ.to_ne_bytes()); - let start_write = &mut buf[2..6]; - start_write.copy_from_slice(&start.to_ne_bytes()); - let end_write = &mut buf[6..10]; - end_write.copy_from_slice(&end.to_ne_bytes()); - let name_len_write = &mut buf[10..14]; - #[allow(clippy::cast_possible_truncation)] - name_len_write.copy_from_slice(&(name_offset as u32).to_ne_bytes()); - let name_write = &mut buf[14..(14 + name_len)]; - let name_read = &data[name_offset..(name_len + name_offset)]; - name_write.copy_from_slice(name_read); - 14 + name_len - } - } -} - -/// `read_message_borrow` provides a method to _attempt_ to read Spiel messages from a buffer. -/// See fields on [`MessageBorrow`] to interpret the values that are returned in the happy-path case. -/// It fails under any condition which `nom` will fail to parse, but that usually comes down to 2 -/// major cases: +/// 1. The number of bytes read. +/// 2. The borrowed message. /// /// # Errors /// -/// 1. Not enough data to parse a whole message (Error, recoverable, try again later) -/// 2. Invalid data (Failure, this means completely unrecoverable) -pub fn read_message_borrow( - buf: &[u8], - header_already_read: bool, -) -> IResult<&[u8], MessageBorrow<'_>> { +/// - Not enough bytes in the buffer, +/// - Invalid variant of either [`ChunkType`] or [`EventType`], +/// - Converting a string into UTF-8 failed. +pub fn read_message(buf: &[u8], header_already_read: bool) -> Result<(usize, Message<'_>), Error> { if !header_already_read { - return read_version_borrow(buf); - } - let (data, ct) = read_chunk_type(buf)?; - match ct { - ChunkType::Audio => read_message_borrow_audio(data), - ChunkType::Event => read_message_borrow_event(data), + return read_version(buf); } + let (ct_offset, ct) = read_chunk_type(buf)?; + let (offset, mt) = match ct { + ChunkType::Audio => read_message_audio(&buf[ct_offset..]), + ChunkType::Event => read_message_event(&buf[ct_offset..]), + }?; + Ok((ct_offset + offset, mt)) } -/// [`poll_read_message_type`] provides a method to _attempt_ to read Spiel messages from a buffer, but +/// [`poll_read_message`] provides a method to _attempt_ to read Spiel messages from a buffer, but /// mapped to a [`Poll`] type instead of a plain result. /// See fields on [`MessageType`] to interpret the values that are returned in the happy-path case. /// Unlike [`read_message_type`], this fails under only one condition, and in the case not enough data has been provided, it will return [`Poll::Pending`]. @@ -328,136 +157,176 @@ pub fn read_message_borrow( /// #[cfg(feature = "poll")] #[allow(clippy::type_complexity)] -pub fn poll_read_message_type( - buf: &[u8], - header_already_read: bool, -) -> Poll>> { - match read_message_type(buf, header_already_read) { - Ok(happy_data) => Poll::Ready(Ok(happy_data)), - Err(nom::Err::Incomplete(_)) => Poll::Pending, - Err(nom::Err::Error(err) | nom::Err::Failure(err)) => Poll::Ready(Err(err)), - } -} - -/// `poll_read_message_borrow` provides a method to _attempt_ to read Spiel messages from a buffer, but -/// mapped to a [`Poll`] type instead of a plain result. -/// See fields on [`MessageType`] to interpret the values that are returned in the happy-path case. -/// Unline [`read_message_borrow`], this fails under only one condition, and in the case not enough data has been provided, it will return [`Poll::Pending`]. -/// -/// # Errors -/// -/// Invalid data was provided in the buffer. -/// -#[cfg(feature = "poll")] -#[allow(clippy::type_complexity)] -pub fn poll_read_message_borrow( +pub fn poll_read_message( buf: &[u8], header_already_read: bool, -) -> Poll), Error<&[u8]>>> { - match read_message_borrow(buf, header_already_read) { +) -> Poll), Error>> { + match read_message(buf, header_already_read) { Ok(happy_data) => Poll::Ready(Ok(happy_data)), - Err(nom::Err::Incomplete(_)) => Poll::Pending, - Err(nom::Err::Error(err) | nom::Err::Failure(err)) => Poll::Ready(Err(err)), + Err(Error::NotEnoughBytes(_)) => Poll::Pending, + Err(e) => Poll::Ready(Err(e)), } } #[cfg(feature = "alloc")] #[derive(Debug, PartialEq, Clone)] #[cfg_attr(all(feature = "serde", feature = "alloc"), derive(Serialize, Deserialize))] -pub enum Message { +pub enum MessageOwned { Version(String), Audio(Bytes), - Event(Event), + Event(EventOwned), } #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum MessageBorrow<'a> { +pub enum Message<'a> { #[cfg_attr(feature = "serde", serde(borrow))] Version(&'a str), #[cfg_attr(feature = "serde", serde(borrow))] Audio(&'a [u8]), #[cfg_attr(feature = "serde", serde(borrow))] - Event(EventBorrow<'a>), + Event(Event<'a>), } -fn read_chunk_size(buf: &[u8]) -> IResult<&[u8], u32> { - // TODO: should this always be native? I'm pretty sure it dpeneds on the stream parameters? - u32(Endianness::Native)(buf) +/// [`read_message_type`] allows you to get references to the input slice instead of placing it +/// into a [`Message`]. +/// +/// This is useful in the case that you are giving out references to the data on your stack. +/// Generally, you should use [`read_message`]. +/// +/// # Errors +/// +/// - Not enough bytes in the buffer, or +/// - Invalid event variants. +pub fn read_message_type( + buf: &[u8], + header_already_read: bool, +) -> Result<(usize, MessageType), Error> { + if !header_already_read { + return read_version_type(buf); + } + let (ct_offset, ct) = read_chunk_type(buf)?; + let (offset, msgt) = match ct { + ChunkType::Audio => { + let (cs_size, chunk_size) = read_u32(&buf[1..])?; + let msg_b = MessageType::Audio { + samples_offset: ct_offset + cs_size, + samples_len: chunk_size as usize, + }; + Ok((cs_size + chunk_size as usize, msg_b)) + } + ChunkType::Event => { + let (typ_len, typ) = read_event_type(&buf[ct_offset..])?; + let (start_len, start) = read_u32(&buf[ct_offset + 1..])?; + let (end_len, end) = read_u32(&buf[ct_offset + 5..])?; + let (name_len_len, name_len) = read_u32(&buf[ct_offset + 9..])?; + + let msg_len = + typ_len + start_len + end_len + name_len_len + name_len as usize; + Ok(( + msg_len, + MessageType::Event { + typ, + start, + end, + name_offset: ct_offset + 14, + name_len: name_len as usize, + }, + )) + } + }?; + Ok((offset + ct_offset, msgt)) } -fn read_chunk_type(buf: &[u8]) -> IResult<&[u8], ChunkType> { - map_res(take(1usize), |bytes: &[u8]| match bytes[0] { - 1 => Ok(ChunkType::Audio), - 2 => Ok(ChunkType::Event), - _ => Err(nom::error::Error::new(bytes[0], ErrorKind::Tag)), - }) - .parse(buf) + +#[cfg(feature = "alloc")] +impl Event<'_> { + #[must_use] + pub fn into_owned(self) -> EventOwned { + EventOwned { + typ: self.typ, + start: self.start, + end: self.end, + name: self.name.map(ToString::to_string), + } + } +} + +#[cfg(feature = "alloc")] +impl Message<'_> { + #[must_use] + pub fn into_owned(self) -> MessageOwned { + match self { + Message::Version(s) => MessageOwned::Version(s.to_string()), + Message::Audio(frame) => MessageOwned::Audio(Bytes::copy_from_slice(frame)), + Message::Event(ev) => MessageOwned::Event(ev.into_owned()), + } + } } -fn read_event_type(buf: &[u8]) -> IResult<&[u8], EventType> { - map_res(take(1usize), |bytes: &[u8]| match bytes[0] { - 1 => Ok(EventType::Word), - 2 => Ok(EventType::Sentence), - 3 => Ok(EventType::Range), - 4 => Ok(EventType::Mark), - _ => Err(Error::new(bytes[0], ErrorKind::Tag)), - }) - .parse(buf) +fn read_u32(buf: &[u8]) -> Result<(usize, u32), Error> { + if buf.len() < 4 { + return Err(Error::NotEnoughBytes(4 - buf.len())); + } + let bytes: [u8; 4] = buf[..4].try_into().expect("at least 4 bytes"); + Ok((4, u32::from_ne_bytes(bytes))) +} +fn read_chunk_type(buf: &[u8]) -> Result<(usize, ChunkType), Error> { + if buf.is_empty() { + return Err(Error::NotEnoughBytes(1)); + } + let ct = match buf[0] { + 1 => ChunkType::Audio, + 2 => ChunkType::Event, + i => return Err(Error::InvalidChunkType(i)), + }; + Ok((1, ct)) } -fn read_message_audio(buf: &[u8]) -> IResult<&[u8], MessageType> { - map(length_data(read_chunk_size), |data| MessageType::Audio { - samples_offset: 5, - samples_len: data.input_len(), - }) - .parse(buf) + +fn read_event_type(buf: &[u8]) -> Result<(usize, EventType), Error> { + if buf.is_empty() { + return Err(Error::NotEnoughBytes(1)); + } + let et = match buf[0] { + 1 => EventType::Word, + 2 => EventType::Sentence, + 3 => EventType::Range, + 4 => EventType::Mark, + i => return Err(Error::InvalidEventType(i)), + }; + Ok((1, et)) } -fn read_message_borrow_audio(buf: &[u8]) -> IResult<&[u8], MessageBorrow<'_>> { - map(length_data(read_chunk_size), MessageBorrow::Audio).parse(buf) +fn read_message_audio(buf: &[u8]) -> Result<(usize, Message<'_>), Error> { + let (cs_size, chunk_size) = read_u32(buf)?; + let Some(audio_buf) = &buf.get(cs_size..(cs_size + chunk_size as usize)) else { + return Err(Error::NotEnoughBytes((cs_size + chunk_size as usize) - buf.len())); + }; + let msg_b = Message::Audio(audio_buf); + Ok((cs_size + chunk_size as usize, msg_b)) } -fn read_message_event(buf: &[u8]) -> IResult<&[u8], MessageType> { - map( - ( - // Takes exactly 1 byte!! - read_event_type, - // then 3 x 4 bytes - u32(Endianness::Native), - u32(Endianness::Native), - length_data(u32(Endianness::Native)), - // ^ then take the number of bytes contianed in the data. - ), - |(typ, start, end, name_data)| MessageType::Event { - name_offset: 14, +fn read_message_event(buf: &[u8]) -> Result<(usize, Message<'_>), Error> { + let (typ_len, typ) = read_event_type(buf)?; + let (start_len, start) = read_u32(&buf[1..])?; + let (end_len, end) = read_u32(&buf[5..])?; + let (name_len_len, name_len) = read_u32(&buf[9..])?; + + let msg_len = typ_len + start_len + end_len + name_len_len + name_len as usize; + let Some(name_buf) = &buf.get(13..(13 + name_len as usize)) else { + return Err(Error::NotEnoughBytes((13 + name_len as usize) - buf.len())); + }; + Ok(( + msg_len, + Message::Event(Event { typ, start, end, - name_len: name_data.input_len(), - }, - ) - .parse(buf) -} -fn read_message_borrow_event(buf: &[u8]) -> IResult<&[u8], MessageBorrow<'_>> { - map( - ( - // Takes exactly 1 byte!! - read_event_type, - // then 3 x 4 bytes - u32(Endianness::Native), - u32(Endianness::Native), - map_res(length_data(u32(Endianness::Native)), |bytes| { - str::from_utf8(bytes) - }), - ), - |(typ, start, end, name)| { - MessageBorrow::Event(EventBorrow { - typ, - start, - end, - name: if name.is_empty() { None } else { Some(name) }, - }) - }, - ) - .parse(buf) + name: if name_len == 0 { + None + } else { + Some(str::from_utf8(name_buf).map_err(Error::Utf8)?) + }, + }), + )) } #[cfg(feature = "reader")] @@ -467,14 +336,6 @@ pub struct Reader { header_done: bool, buffer: BytesMut, } -#[cfg(feature = "reader")] -#[derive(Debug, PartialEq, Eq)] -/// TODO: Add `serde` support for this type! -pub enum ParseError { - Needed(Needed), - Error(Error), - Failure(Error), -} #[cfg(feature = "reader")] impl Reader { @@ -487,37 +348,21 @@ impl Reader { /// # Errors /// /// See [`read_message_type`] for failure cases. - pub fn try_read(&mut self) -> Result { + pub fn try_read(&mut self) -> Result { let mut data = self.buffer.split().freeze(); let (new_buf, message_type) = read_message_type(&data, self.header_done) - .map(|(nb, mt)| (BytesMut::from(nb), mt)) - .map_err(|err| match err { - nom::Err::Incomplete(need) => ParseError::Needed(need), - nom::Err::Error(Error { input, code }) => { - ParseError::Error(Error { - code, - input: data.slice(input.as_ptr().addr() - - data.as_ptr().addr()..), - }) - } - nom::Err::Failure(Error { input, code }) => { - ParseError::Failure(Error { - code, - input: data.slice(input.as_ptr().addr() - - data.as_ptr().addr()..), - }) - } - })?; + .map(|(offset, mt)| (BytesMut::from(&data[offset..]), mt))?; + let msg = match message_type { MessageType::Version { version } => { self.header_done = true; - Message::Version(version.into_iter().collect()) + MessageOwned::Version(version.into_iter().collect()) } - MessageType::Audio { samples_offset, samples_len } => Message::Audio( + MessageType::Audio { samples_offset, samples_len } => MessageOwned::Audio( data.split_off(samples_offset - 1).split_to(samples_len), ), MessageType::Event { typ, start, end, name_offset, name_len } => { - Message::Event(Event { + MessageOwned::Event(EventOwned { typ, start, end, @@ -550,10 +395,10 @@ fn test_wave_reader() { let mut reader = Reader::default(); let data: &[u8] = include_bytes!("../test.wav"); reader.push(data); - assert_eq!(reader.try_read(), Ok(Message::Version("0.01".to_string()))); + assert_eq!(reader.try_read(), Ok(MessageOwned::Version("0.01".to_string()))); assert_eq!( reader.try_read(), - Ok(Message::Event(Event { + Ok(MessageOwned::Event(EventOwned { typ: EventType::Sentence, start: 0, end: 0, @@ -562,43 +407,74 @@ fn test_wave_reader() { ); assert_eq!( reader.try_read(), - Ok(Message::Event(Event { typ: EventType::Word, start: 0, end: 4, name: None })) + Ok(MessageOwned::Event(EventOwned { + typ: EventType::Word, + start: 0, + end: 4, + name: None + })) ); - for _ in 0..4 { - assert_matches!(reader.try_read(), Ok(Message::Audio(_))); + for i in 0..4 { + assert_matches!(reader.try_read(), Ok(MessageOwned::Audio(_)), "{i}"); } - let word_is = Message::Event(Event { typ: EventType::Word, start: 5, end: 7, name: None }); - let word_a = Message::Event(Event { typ: EventType::Word, start: 8, end: 9, name: None }); - let word_test = - Message::Event(Event { typ: EventType::Word, start: 10, end: 14, name: None }); - let word_using = - Message::Event(Event { typ: EventType::Word, start: 15, end: 20, name: None }); - let word_spiel = - Message::Event(Event { typ: EventType::Word, start: 21, end: 26, name: None }); - let word_whaha = - Message::Event(Event { typ: EventType::Word, start: 28, end: 35, name: None }); + let word_is = MessageOwned::Event(EventOwned { + typ: EventType::Word, + start: 5, + end: 7, + name: None, + }); + let word_a = MessageOwned::Event(EventOwned { + typ: EventType::Word, + start: 8, + end: 9, + name: None, + }); + let word_test = MessageOwned::Event(EventOwned { + typ: EventType::Word, + start: 10, + end: 14, + name: None, + }); + let word_using = MessageOwned::Event(EventOwned { + typ: EventType::Word, + start: 15, + end: 20, + name: None, + }); + let word_spiel = MessageOwned::Event(EventOwned { + typ: EventType::Word, + start: 21, + end: 26, + name: None, + }); + let word_whaha = MessageOwned::Event(EventOwned { + typ: EventType::Word, + start: 28, + end: 35, + name: None, + }); assert_eq!(reader.try_read(), Ok(word_is)); for _ in 0..3 { - assert_matches!(reader.try_read(), Ok(Message::Audio(_))); + assert_matches!(reader.try_read(), Ok(MessageOwned::Audio(_))); } assert_eq!(reader.try_read(), Ok(word_a)); - assert_matches!(reader.try_read(), Ok(Message::Audio(_))); - assert_matches!(reader.try_read(), Ok(Message::Audio(_))); + assert_matches!(reader.try_read(), Ok(MessageOwned::Audio(_))); + assert_matches!(reader.try_read(), Ok(MessageOwned::Audio(_))); assert_eq!(reader.try_read(), Ok(word_test)); for _ in 0..6 { - assert_matches!(reader.try_read(), Ok(Message::Audio(_))); + assert_matches!(reader.try_read(), Ok(MessageOwned::Audio(_))); } assert_eq!(reader.try_read(), Ok(word_using)); for _ in 0..6 { - assert_matches!(reader.try_read(), Ok(Message::Audio(_))); + assert_matches!(reader.try_read(), Ok(MessageOwned::Audio(_))); } assert_eq!(reader.try_read(), Ok(word_spiel)); for _ in 0..14 { - assert_matches!(reader.try_read(), Ok(Message::Audio(_))); + assert_matches!(reader.try_read(), Ok(MessageOwned::Audio(_))); } assert_eq!( reader.try_read(), - Ok(Message::Event(Event { + Ok(MessageOwned::Event(EventOwned { typ: EventType::Sentence, start: 28, end: 28, @@ -607,64 +483,154 @@ fn test_wave_reader() { ); assert_eq!(reader.try_read(), Ok(word_whaha)); for _ in 0..10 { - assert_matches!(reader.try_read(), Ok(Message::Audio(_))); + assert_matches!(reader.try_read(), Ok(MessageOwned::Audio(_))); } assert_eq!(&reader.buffer.freeze().slice(..)[..], &[]); } #[test] fn test_read_write_version() { - let mt = MessageType::Version { version: ['w', 'o', 'w', 'z'] }; - let data = &[0]; + let mt = Message::Version("wowz"); let buf = &mut [0; 1024]; - let _offset = write_message_type_unchecked(mt.clone(), &data[..], &mut buf[..]); - let (_read_offset, mt2) = read_message_type(&buf[..], false).expect("Valid MessageType!"); + let _offset = write_message(&mt.clone(), &mut buf[..]); + let (_read_offset, mt2) = read_message(&buf[..], false).expect("Valid MessageType!"); assert_eq!(mt, mt2); } #[test] fn test_read_write_event() { let mt_name = "WTF is this!?"; - let name_offset = 14; - let mt = MessageType::Event { + let mt = Message::Event(Event { typ: EventType::Word, start: 872, end: 99999, - name_offset, - // +1: terminating null byte - name_len: mt_name.len() + 1, - }; + name: Some(mt_name), + }); let data = &mut [0u8; 1024]; // No need to write this to the `data` buffer because we start with all 0s. let data_writer = &mut data[14..(14 + mt_name.len())]; data_writer.copy_from_slice(mt_name.as_bytes()); let buf = &mut [0u8; 1024]; - let _offset = write_message_type_unchecked(mt.clone(), &data[..], &mut buf[..]); - let (_read_offset, mt2) = read_message_type(&buf[..], true).expect("Valid MessageType!"); - let MessageType::Event { name_offset, name_len, .. } = mt2.clone() else { - assert_eq!(mt, mt2); - panic!(); - }; - // NOTE: the -1 is because the binary format requires the terminating null byte - assert_eq!(&data[name_offset..(name_offset + name_len - 1)], mt_name.as_bytes()); + let _offset = write_message(&mt.clone(), &mut buf[..]); + let (_read_offset, mt2) = read_message(&buf[..], true).expect("Valid MessageType!"); assert_eq!(mt, mt2); } #[test] fn test_read_write_audio() { - let samples = [123, 93, 87, 16, 15, 15, 15, 0, 0, 0, 0]; - let samples_offset = 5; - let mt = MessageType::Audio { samples_offset, samples_len: samples.len() }; + let samples: [u8; 11] = [123, 93, 87, 16, 15, 15, 15, 0, 0, 0, 0]; + let mt = Message::Audio(&samples[..]); let data = &mut [0u8; 1024]; let data_writer = &mut data[5..(5 + samples.len())]; data_writer.copy_from_slice(&samples[..]); let buf = &mut [0u8; 1024]; - let _offset = write_message_type_unchecked(mt.clone(), &data[..], &mut buf[..]); - let (_read_offset, mt2) = read_message_type(&buf[..], true).expect("Valid MessageType!"); - let MessageType::Audio { samples_offset, samples_len } = mt2.clone() else { + let _offset = write_message(&mt.clone(), &mut buf[..]); + let (_read_offset, mt2) = read_message(&buf[..], true).expect("Valid MessageType"); + let Message::Audio(samples2) = mt2 else { assert_eq!(mt, mt2); panic!(); }; - assert_eq!(&data[samples_offset..(samples_offset + samples_len)], &samples); + assert_eq!(&samples, &samples2); assert_eq!(mt, mt2); } + +/// Write a message to the buffer. +/// +/// # Errors +/// +/// Fails if the buiffer is too small. +pub fn write_message(mt: &Message, buf: &mut [u8]) -> Result { + match mt { + Message::Version(version) => { + if buf.len() < 4 { + return Err(Error::NotEnoughSpace(4 - buf.len())); + } + let buf_first_4 = &mut buf[..4]; + + buf_first_4.copy_from_slice(version.as_bytes()); + Ok(4) + } + Message::Audio(samples) => { + if buf.len() < 5 + samples.len() { + return Err(Error::NotEnoughSpace((5 + samples.len()) - buf.len())); + } + buf[0] = 1; + let samples_len = samples.len(); + let samp_writer = &mut buf[1..5]; + #[allow(clippy::cast_possible_truncation)] + samp_writer.copy_from_slice(&(samples_len as u32).to_ne_bytes()); + let data_writer = &mut buf[5..(samples_len + 5)]; + data_writer.copy_from_slice(samples); + Ok(5 + samples_len) + } + Message::Event(Event { typ, start, end, name: maybe_name }) => { + if buf.len() < 14 + maybe_name.unwrap_or_default().len() { + return Err(Error::NotEnoughSpace( + (14 + maybe_name.unwrap_or_default().len()) - buf.len(), + )); + } + buf[0] = 2; + let typ_write = &mut buf[1..2]; + typ_write.copy_from_slice(&typ.to_ne_bytes()); + let start_write = &mut buf[2..6]; + start_write.copy_from_slice(&start.to_ne_bytes()); + let end_write = &mut buf[6..10]; + end_write.copy_from_slice(&end.to_ne_bytes()); + let name_len_write = &mut buf[10..14]; + let name_offset = 14; + let name_len = maybe_name.unwrap_or_default().len(); + #[allow(clippy::cast_possible_truncation)] + name_len_write.copy_from_slice(&(name_len as u32).to_ne_bytes()); + if let Some(name) = maybe_name { + let name_write = &mut buf[name_offset..(name_offset + name_len)]; + name_write.copy_from_slice(name.as_bytes()); + } + Ok(name_offset + name_len) + } + } +} + +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +/// A type for interpreting buffer data, instead of taking references to the underlying data. +/// This is for when the data is on your stack, and you want to borrow the results yourself. +/// +/// Mostly, you should use [`Message`] instead. +pub enum MessageType { + Version { + version: [char; 4], + }, + /// With this variant, you should then be able to: + Audio { + /// The index to the start of the data slice where the audio begins. + samples_offset: usize, + /// This length of the slice you should take in order to grab the audio frame. + samples_len: usize, + }, + Event { + name_offset: usize, + typ: EventType, + start: u32, + end: u32, + name_len: usize, + }, +} + +#[cfg(test)] +#[proptest::property_test] +fn never_panic(data: Vec, header: bool) { + let mut new_data = &data[..]; + loop { + if new_data.is_empty() { + break; + } + let Ok((offset, _msg)) = read_message(new_data, header) else { + break; + }; + if let Some(next_data) = &new_data.get(offset..) { + new_data = next_data; + } else { + break; + } + } +}