cactus_rs/model.rs
1//! Which of the two models a weight archive, an engine slot or an error is about.
2//!
3//! ## Overview
4//!
5//! The linked engine holds one model per kind: a text model (Needle) and a speech model
6//! (Whistle). Both ship as `.cact` archives with the same magic tag, so the crate names the model
7//! wherever the two could be confused, most visibly in
8//! [`Error::WrongModel`](crate::Error::WrongModel).
9//!
10//! ## Usage
11//!
12//! ```rust
13//! use cactus_rs::Model;
14//!
15//! assert_eq!(Model::Whistle.archive(), "whistle.cact");
16//! assert_eq!(Model::Needle.to_string(), "Needle");
17//! ```
18
19use std::fmt;
20
21/// One of the models the engine can hold.
22///
23/// The enum is `#[non_exhaustive]`: matching it needs a `_` arm, and a model added by a later
24/// engine is not a breaking change.
25///
26/// # Examples
27///
28/// ```rust
29/// use cactus_rs::Model;
30///
31/// let model = Model::Needle;
32/// assert_eq!(model.as_str(), "Needle");
33/// assert_eq!(model.archive(), "needle3.cact");
34/// ```
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
36#[non_exhaustive]
37pub enum Model {
38 /// Needle 3, the text model: tool calling, structured extraction and text embedding.
39 Needle,
40 /// Whistle, the speech model: transcription, word timestamps and speech embedding.
41 Whistle,
42}
43
44impl Model {
45 /// The model's name, as it appears in messages.
46 ///
47 /// # Examples
48 ///
49 /// ```rust
50 /// use cactus_rs::Model;
51 ///
52 /// assert_eq!(Model::Whistle.as_str(), "Whistle");
53 /// ```
54 #[inline]
55 #[must_use]
56 pub const fn as_str(self) -> &'static str {
57 match self {
58 Model::Needle => "Needle",
59 Model::Whistle => "Whistle",
60 }
61 }
62
63 /// The file name upstream publishes the model's weights under.
64 ///
65 /// # Examples
66 ///
67 /// ```rust
68 /// use cactus_rs::Model;
69 ///
70 /// assert_eq!(Model::Needle.archive(), "needle3.cact");
71 /// assert_eq!(Model::Whistle.archive(), "whistle.cact");
72 /// ```
73 #[inline]
74 #[must_use]
75 pub const fn archive(self) -> &'static str {
76 match self {
77 Model::Needle => "needle3.cact",
78 Model::Whistle => "whistle.cact",
79 }
80 }
81}
82
83/// Writes the model's name, the same text as [`Model::as_str`].
84impl fmt::Display for Model {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 f.write_str(self.as_str())
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn display_is_the_name() {
96 for model in [Model::Needle, Model::Whistle] {
97 assert_eq!(model.to_string(), model.as_str());
98 }
99 }
100
101 #[test]
102 fn archives_are_distinct() {
103 assert_ne!(Model::Needle.archive(), Model::Whistle.archive());
104 }
105}