1use core::error;
2use core::fmt;
3use core::mem;
4use core::result;
5use core::str;
6
7use std::borrow;
8use std::collections;
9use std::io;
10use std::path;
11
12#[cfg(any(feature = "audio", doc))]
13use crate::audio;
14#[cfg(any(feature = "logger", doc))]
15use crate::logging;
16use crate::modapi;
17
18pub mod enums {
20 pub use super::modapi::{
21 MessageType as Message,
22 DataMode,
23 Priority,
24 Notification as Notifications,
25 NotificationType as Notification,
26 };
27}
28
29
30#[derive(thiserror::Error, Debug)]
32#[non_exhaustive]
33pub enum Error {
34 #[error("General I/O error: {0}")]
36 IO(#[from] io::Error),
37
38 #[cfg(any(feature = "logger", doc))]
40 #[error("Logger configuration failed: {0}")]
41 LoggerError(#[from] logging::flexi::Error),
42
43 #[error("Module configuration loading failed: {0}")]
45 ModuleConfigFailed(#[source] Box<dyn error::Error>),
46
47 #[error("Module initialization failed: {0}")]
49 ModuleInitFailed(#[source] Box<dyn error::Error>),
50
51 #[error("Module LIST VOICES failed: {0}")]
53 ModuleListVoicesFailed(#[source] Box<dyn error::Error>),
54
55 #[error("Module SET failed: {0}")]
57 ModuleSetParamFailed(#[source] Box<dyn error::Error>),
58
59 #[error("Module SPEAK failed: {0}")]
61 ModuleSpeakFailed(#[source] Box<dyn error::Error>),
62
63 #[cfg(any(not(feature = "logger"), doc))]
65 #[error("Module LOGLEVEL value update failed: {0}")]
66 ModuleLoglevelSetFailed(#[source] Box<dyn error::Error>),
67
68 #[cfg(any(not(feature = "logger"), doc))]
70 #[error("Module DEBUG update failed: {0}")]
71 ModuleDebugFailed(#[source] Box<dyn error::Error>),
72
73 #[error("Server did not send INIT as first message")]
75 ProtoNoInit,
76
77 #[error("Server sent malformed SET parameter {typ} value for “{0}”: {msg}",
82 typ = if .1.is_some() { "numeric" } else { "string" },
83 msg = if let Some(ref inner) = *.1 {
84 borrow::Cow::from(inner.to_string())
85 } else {
86 borrow::Cow::from("Invalid value")
87 })]
88 ProtoParamMalformed(String, #[source] Option<bounded_integer::ParseError>),
89
90 #[error("Received unknown LOGLEVEL parameter update: {0}")]
94 UnknownLoglevelParam(String),
95
96 #[error("Received unknown SET parameter update: {0}")]
98 UnknownParam(String),
99}
100
101impl From<io::ErrorKind> for Error {
102 fn from(kind: io::ErrorKind) -> Self {
103 Self::IO(kind.into())
104 }
105}
106
107
108#[derive(Debug, Clone, PartialEq, Eq)]
139pub struct StringError(String);
140
141impl fmt::Display for StringError {
142 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143 fmt::Display::fmt(&self.0, f)
144 }
145}
146
147impl error::Error for StringError {}
148
149
150
151pub type Result<T> = result::Result<T, Error>;
152
153
154#[repr(transparent)]
155#[derive(Debug, Default, PartialEq, Eq)]
156pub struct MessageSettings(modapi::MsgSettings);
157
158
159#[repr(transparent)]
160#[derive(Debug, Default, PartialEq, Eq)]
161pub struct Voice(modapi::Voice);
162
163#[derive(Debug, PartialEq, Eq)]
164pub struct Voices(mbox::MArray<Option<mbox::MBox<Voice>>>);
165
166impl Voices {
167 #[must_use]
168 pub fn take_slice(items: &mut [Voice]) -> Self {
169 let mut ptrs = mbox::MBox::<[Option<mbox::MBox<Voice>>]>::new_uninit_slice(items.len() + 1);
171
172 for (idx, item) in items.iter_mut().enumerate() {
175 ptrs[idx] = mem::MaybeUninit::new(Some(mbox::MBox::new(mem::take(item))));
176 }
177 ptrs[items.len()] = mem::MaybeUninit::new(None);
178
179 Voices(
181 unsafe { mbox::MArray::from_raw(ptrs.assume_init().into_raw_parts().0) },
182 )
183 }
184
185 #[must_use]
186 pub fn as_ptr(&self) -> *const *const modapi::Voice {
187 self.0.as_ptr().cast::<*const modapi::Voice>()
188 }
189
190 #[must_use]
191 pub fn into_raw(self) -> *const *const modapi::Voice {
192 let Voices(inner) = self;
193 mbox::MBox::into_raw(inner.into_mbox()) as *const *const modapi::Voice
194 }
195}
196
197
198pub mod params {
199 bounded_integer::bounded_integer! {
200 pub struct Rate(-100, 100);
206 }
207 bounded_integer::bounded_integer! {
208 pub struct Pitch(-100, 100);
214 }
215 bounded_integer::bounded_integer! {
216 pub struct PitchRange(-100, 100);
222 }
223 bounded_integer::bounded_integer! {
224 pub struct Volume(-100, 100);
230 }
231
232 pub use super::modapi::{
233 CapitalLetters,
234 Punctuation,
235 Spelling,
236 VoiceType,
237 };
238}
239
240#[derive(Debug, Clone, PartialEq, Eq)]
247pub enum Param {
248 Rate(params::Rate),
250
251 Pitch(params::Pitch),
253
254 PitchRange(params::PitchRange),
256
257 Volume(params::Volume),
259
260 PunctuationMode(params::Punctuation),
262
263 SpellingMode(params::Spelling),
265
266 CapLetRecogn(params::CapitalLetters),
268
269 VoiceType(params::VoiceType),
271
272 VoiceName(String),
279
280 Language(String),
286
287 Other(String, String),
289}
290
291impl Param {
292 pub fn from_kv(name: &str, value: &str) -> Result<Self> {
293 fn int_from<T: str::FromStr<Err=bounded_integer::ParseError>>(name: &str, value: &str) -> Result<T> {
294 T::from_str(value).map_err(|err| Error::ProtoParamMalformed(name.to_owned(), Some(err)))
295 }
296
297 fn itm_from<'a, T: TryFrom<&'a str>>(name: &str, value: &'a str) -> Result<T> {
298 T::try_from(value).or(Err(Error::ProtoParamMalformed(name.to_owned(), None)))
299 }
300
301 Ok(match name {
302 "rate" => Self::Rate(int_from::<params::Rate>(name, value)?),
303 "pitch" => Self::Pitch(int_from::<params::Pitch>(name, value)?),
304 "pitch_range" => Self::PitchRange(int_from::<params::PitchRange>(name, value)?),
305 "volume" => Self::Volume(int_from::<params::Volume>(name, value)?),
306
307 "punctuation_mode" => Self::PunctuationMode(itm_from::<params::Punctuation>(name, value)?),
308 "spelling_mode" => Self::SpellingMode(itm_from::<params::Spelling>(name, value)?),
309 "cap_let_recogn" => Self::CapLetRecogn(itm_from::<params::CapitalLetters>(name, value)?),
310 "voice" => Self::VoiceType(itm_from::<params::VoiceType>(name, value)?),
311
312 "synthesis_voice" => Self::VoiceName(value.to_owned()),
313 "language" => Self::Language(value.to_owned()),
314
315 _ => Self::Other(name.to_owned(), value.to_owned()),
316 })
317 }
318}
319
320
321#[non_exhaustive]
323#[derive(Debug, Default, Clone, PartialEq, Eq)]
324pub struct Params {
325 pub rate: params::Rate,
327
328 pub pitch: params::Pitch,
330
331 pub pitch_range: params::PitchRange,
333
334 pub volume: params::Volume,
336
337 pub punctuation_mode: params::Punctuation,
339
340 pub spelling_mode: params::Spelling,
342
343 pub cap_let_recogn: params::CapitalLetters,
345
346 pub voice_type: params::VoiceType,
348
349 pub voice_name: String,
351
352 pub language: String,
354
355 pub custom: collections::HashMap<String, String>,
357
358}
359
360
361pub trait Module {
362 type Error: error::Error + 'static;
363
364 fn config(&mut self, config_path: Option<&path::Path>) -> result::Result<(), Self::Error>;
366
367 fn init(&mut self) -> result::Result<String, Self::Error>;
369
370 fn list_voices(&mut self) -> result::Result<Voices, Self::Error>;
372
373 fn set_param(&mut self, param: Param) -> result::Result<bool, Self::Error>;
388
389 #[cfg(any(feature = "async", doc))]
390 fn speak(&mut self, utterance: &str, msg_type: enums::Message) -> result::Result<(), Self::Error>;
401
402 #[cfg(any(not(feature = "async"), doc))]
403 fn speak_sync(&mut self, utterance: &str, msg_type: enums::Message) -> result::Result<(), Self::Error>;
418
419 fn pause(&mut self) -> result::Result<(), Self::Error>;
423
424 fn stop(&mut self) -> result::Result<(), Self::Error>;
428
429 #[cfg(any(feature = "audio", doc))]
435 fn audio_ctx(&mut self) -> &mut audio::Context;
436
437 #[cfg(any(not(feature = "logger"), doc))]
446 fn loglevel_set(&mut self, level: bounded_integer::BoundedU8<0, 5>) -> result::Result<(), Self::Error>;
447
448 #[cfg(any(not(feature = "logger"), doc))]
456 fn debug(&mut self, path: Option<&path::Path>) -> result::Result<(), Self::Error>;
457}
458
459
460#[derive(Debug)]
474pub(super) struct ErrorBoxWrapper(Box<dyn error::Error>);
475
476impl ErrorBoxWrapper {
477 fn new<E: error::Error + 'static>(err: E) -> Self {
478 Self(Box::new(err))
479 }
480
481 pub(super) fn into_inner(self) -> Box<dyn error::Error> {
482 self.0
483 }
484}
485
486impl fmt::Display for ErrorBoxWrapper {
487 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
488 fmt::Display::fmt(&self.0, f)
489 }
490}
491
492impl error::Error for ErrorBoxWrapper {
493 fn source(&self) -> Option<&(dyn error::Error + 'static)> { self.0.source() }
494}
495
496
497pub(crate) struct ModuleWrapper<M: Module>(pub M);
499
500impl<M: Module> Module for ModuleWrapper<M> {
501 type Error = ErrorBoxWrapper;
502
503 fn config(&mut self, config_path: Option<&path::Path>) -> result::Result<(), Self::Error> {
504 self.0.config(config_path).map_err(Self::Error::new)
505 }
506
507 fn init(&mut self) -> result::Result<String, Self::Error> {
508 self.0.init().map_err(Self::Error::new)
509 }
510
511 fn list_voices(&mut self) -> result::Result<Voices, Self::Error> {
512 self.0.list_voices().map_err(Self::Error::new)
513 }
514
515 fn set_param(&mut self, param: Param) -> result::Result<bool, Self::Error> {
516 self.0.set_param(param).map_err(Self::Error::new)
517 }
518
519 #[cfg(any(feature = "async", doc))]
520 fn speak(&mut self, utterance: &str, msg_type: enums::Message) -> result::Result<(), Self::Error> {
521 self.0.speak(utterance, msg_type).map_err(Self::Error::new)
522 }
523
524 #[cfg(any(not(feature = "async"), doc))]
525 fn speak_sync(&mut self, utterance: &str, msg_type: enums::Message) -> result::Result<(), Self::Error> {
526 self.0.speak_sync(utterance, msg_type).map_err(Self::Error::new)
527 }
528 fn pause(&mut self) -> result::Result<(), Self::Error> {
529 self.0.pause().map_err(Self::Error::new)
530 }
531
532 fn stop(&mut self) -> result::Result<(), Self::Error> {
533 self.0.stop().map_err(Self::Error::new)
534 }
535
536 #[cfg(any(feature = "audio", doc))]
537 fn audio_ctx(&mut self) -> &mut audio::Context {
538 self.0.audio_ctx()
539 }
540
541 #[cfg(any(not(feature = "logger"), doc))]
542 fn loglevel_set(&mut self, level: bounded_integer::BoundedU8<0, 5>) -> result::Result<(), Self::Error> {
543 self.0.loglevel_set(level).map_err(Self::Error::new)
544 }
545
546 #[cfg(any(not(feature = "logger"), doc))]
547 fn debug(&mut self, path: Option<&path::Path>) -> result::Result<(), Self::Error> {
548 self.0.debug(path).map_err(Self::Error::new)
549 }
550}