1use std::fmt;
12use std::str::FromStr;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
22#[non_exhaustive]
23pub enum EngineFamily {
24 Whisper,
26 Qwen3Asr,
28}
29
30impl EngineFamily {
31 pub fn from_config(value: &str) -> Option<Self> {
35 match value.to_ascii_lowercase().as_str() {
36 "whisper" => Some(Self::Whisper),
37 "qwen3-asr" | "qwen3asr" | "qwen3_asr" => Some(Self::Qwen3Asr),
38 _ => None,
39 }
40 }
41
42 pub fn as_config(self) -> &'static str {
44 match self {
45 Self::Whisper => "whisper",
46 Self::Qwen3Asr => "qwen3-asr",
47 }
48 }
49
50 pub fn crate_label(self) -> &'static str {
52 match self {
53 Self::Whisper => "voxora-whisper",
54 Self::Qwen3Asr => "voxora-qwen3asr",
55 }
56 }
57}
58
59impl fmt::Display for EngineFamily {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 f.write_str(self.as_config())
62 }
63}
64
65impl FromStr for EngineFamily {
66 type Err = InvalidEngineFamily;
67
68 fn from_str(s: &str) -> Result<Self, Self::Err> {
69 Self::from_config(s).ok_or_else(|| InvalidEngineFamily(s.to_string()))
70 }
71}
72
73#[derive(Debug, thiserror::Error)]
77#[error("unknown engine_family {0:?}; expected one of `whisper` or `qwen3-asr`")]
78pub struct InvalidEngineFamily(pub String);
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 #[test]
85 fn from_config_accepts_canonical_and_aliases() {
86 assert_eq!(
87 EngineFamily::from_config("whisper"),
88 Some(EngineFamily::Whisper)
89 );
90 assert_eq!(
91 EngineFamily::from_config("qwen3-asr"),
92 Some(EngineFamily::Qwen3Asr)
93 );
94 assert_eq!(
95 EngineFamily::from_config("qwen3asr"),
96 Some(EngineFamily::Qwen3Asr)
97 );
98 assert_eq!(
99 EngineFamily::from_config("qwen3_asr"),
100 Some(EngineFamily::Qwen3Asr)
101 );
102 }
103
104 #[test]
105 fn from_config_is_case_insensitive() {
106 assert_eq!(
107 EngineFamily::from_config("WHISPER"),
108 Some(EngineFamily::Whisper)
109 );
110 assert_eq!(
111 EngineFamily::from_config("Qwen3-ASR"),
112 Some(EngineFamily::Qwen3Asr)
113 );
114 }
115
116 #[test]
117 fn from_config_rejects_unknown() {
118 assert_eq!(EngineFamily::from_config("parakeet"), None);
119 assert_eq!(EngineFamily::from_config(""), None);
120 }
121
122 #[test]
123 fn as_config_round_trips() {
124 for f in [EngineFamily::Whisper, EngineFamily::Qwen3Asr] {
125 assert_eq!(EngineFamily::from_config(f.as_config()), Some(f));
126 }
127 }
128
129 #[test]
130 fn from_str_error_carries_input() {
131 let err: InvalidEngineFamily = "bogus".parse::<EngineFamily>().unwrap_err();
132 assert_eq!(err.0, "bogus");
133 }
134
135 #[test]
136 fn crate_labels_match_workspace_member_names() {
137 assert_eq!(EngineFamily::Whisper.crate_label(), "voxora-whisper");
138 assert_eq!(EngineFamily::Qwen3Asr.crate_label(), "voxora-qwen3asr");
139 }
140}