aria_kit/speaker/
profile.rs1use std::time::SystemTime;
8
9use serde::{Deserialize, Serialize};
10
11const MAX_NAME_BYTES: usize = 64;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
18#[serde(rename_all = "snake_case")]
19pub enum Role {
20 Owner,
21 Guest,
22}
23
24impl std::fmt::Display for Role {
25 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26 formatter.write_str(match self {
27 Self::Owner => "owner",
28 Self::Guest => "guest",
29 })
30 }
31}
32
33#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
36pub struct SpeakerName(String);
37
38impl SpeakerName {
39 pub fn new(name: impl Into<String>) -> Result<Self, SpeakerNameError> {
40 let name = name.into();
41 if name.is_empty() {
42 return Err(SpeakerNameError::Empty);
43 }
44 if name.len() > MAX_NAME_BYTES {
45 return Err(SpeakerNameError::TooLong {
46 actual_bytes: name.len(),
47 maximum_bytes: MAX_NAME_BYTES,
48 });
49 }
50 if name.trim() != name {
51 return Err(SpeakerNameError::Padded);
52 }
53 if name.starts_with('.')
56 || name
57 .chars()
58 .any(|character| character.is_control() || matches!(character, '/' | '\\' | ':'))
59 {
60 return Err(SpeakerNameError::Unusable);
61 }
62 Ok(Self(name))
63 }
64
65 pub fn as_str(&self) -> &str {
66 &self.0
67 }
68}
69
70impl std::fmt::Display for SpeakerName {
71 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72 formatter.write_str(&self.0)
73 }
74}
75
76#[derive(Debug, Clone, PartialEq)]
78pub struct Profile {
79 pub name: SpeakerName,
80 pub role: Role,
81 pub engine: String,
83 pub enrolled_at: SystemTime,
84 pub vector: Vec<f32>,
86}
87
88#[cfg(any(test, feature = "test-support"))]
91pub fn enrolled_voice(name: &str, role: Role, vector: Vec<f32>) -> Profile {
92 Profile {
93 name: SpeakerName::new(name).expect("a test name is usable"),
94 role,
95 engine: "test".to_owned(),
96 enrolled_at: SystemTime::UNIX_EPOCH,
97 vector,
98 }
99}
100
101#[derive(Debug, thiserror::Error)]
102pub enum SpeakerNameError {
103 #[error("a speaker name cannot be empty")]
104 Empty,
105
106 #[error("a speaker name holds {actual_bytes} bytes; maximum is {maximum_bytes}")]
107 TooLong {
108 actual_bytes: usize,
109 maximum_bytes: usize,
110 },
111
112 #[error("a speaker name cannot start or end with a space")]
113 Padded,
114
115 #[error("a speaker name cannot start with a dot or hold a path separator")]
116 Unusable,
117}