use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Profile {
Mobile,
Balanced,
Fast,
Custom,
}
impl Profile {
pub const fn embedding_dim(self) -> usize {
match self {
Profile::Mobile | Profile::Balanced | Profile::Fast => 256,
Profile::Custom => 0,
}
}
pub const fn default_threshold(self) -> f32 {
match self {
Profile::Mobile => 0.55,
Profile::Balanced => super::config::DEFAULT_AHC_THRESHOLD,
Profile::Fast => super::config::DEFAULT_AHC_THRESHOLD,
Profile::Custom => 0.5,
}
}
pub const fn manifest_id(self) -> &'static str {
match self {
Profile::Mobile => "mobile",
Profile::Balanced => "balanced",
Profile::Fast => "fast",
Profile::Custom => "custom",
}
}
}
impl std::str::FromStr for Profile {
type Err = ProfileParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_ascii_lowercase().as_str() {
"mobile" => Ok(Profile::Mobile),
"balanced" => Ok(Profile::Balanced),
"fast" => Ok(Profile::Fast),
"custom" => Ok(Profile::Custom),
other => Err(ProfileParseError(other.to_owned())),
}
}
}
#[derive(Debug, Clone)]
pub struct ProfileParseError(pub String);
impl std::fmt::Display for ProfileParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"unknown profile '{}': expected mobile|balanced|fast|custom",
self.0
)
}
}
impl std::error::Error for ProfileParseError {}
#[allow(clippy::unwrap_used)]
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_str_is_case_insensitive() {
assert_eq!("mobile".parse::<Profile>().unwrap(), Profile::Mobile);
assert_eq!("BALANCED".parse::<Profile>().unwrap(), Profile::Balanced);
assert_eq!("Fast".parse::<Profile>().unwrap(), Profile::Fast);
assert_eq!("CUSTOM".parse::<Profile>().unwrap(), Profile::Custom);
}
#[test]
fn from_str_unknown_profile_reports_expected_set() {
let err = "weird".parse::<Profile>().unwrap_err();
assert_eq!(
err.to_string(),
"unknown profile 'weird': expected mobile|balanced|fast|custom"
);
let _: &dyn std::error::Error = &err;
}
#[test]
fn manifest_ids_parse_back_to_their_profile() {
for p in [
Profile::Mobile,
Profile::Balanced,
Profile::Fast,
Profile::Custom,
] {
assert_eq!(p.manifest_id().parse::<Profile>().unwrap(), p);
}
}
#[test]
fn embedding_dims_and_thresholds_match_model_bundles() {
assert_eq!(Profile::Mobile.embedding_dim(), 256);
assert_eq!(Profile::Balanced.embedding_dim(), 256);
assert_eq!(Profile::Fast.embedding_dim(), 256);
assert_eq!(Profile::Custom.embedding_dim(), 0);
assert_eq!(Profile::Mobile.default_threshold(), 0.55);
assert_eq!(
Profile::Balanced.default_threshold(),
crate::types::config::DEFAULT_AHC_THRESHOLD
);
assert_eq!(
Profile::Fast.default_threshold(),
crate::types::config::DEFAULT_AHC_THRESHOLD
);
assert_eq!(Profile::Custom.default_threshold(), 0.5);
}
#[test]
fn profile_serde_roundtrip() {
for p in [
Profile::Mobile,
Profile::Balanced,
Profile::Fast,
Profile::Custom,
] {
let json = serde_json::to_string(&p).unwrap();
assert_eq!(serde_json::from_str::<Profile>(&json).unwrap(), p);
}
}
}