1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum Provider {
10 Cohere,
12 OpenAiCompatible,
15}
16
17impl Provider {
18 pub fn as_str(self) -> &'static str {
20 match self {
21 Provider::Cohere => "cohere",
22 Provider::OpenAiCompatible => "openai-compatible",
23 }
24 }
25
26 pub const ALL: &'static [Provider] = &[Provider::Cohere, Provider::OpenAiCompatible];
28
29 pub const ACCEPTED_NAMES: &'static [&'static str] = &["cohere", "openai"];
37}
38
39impl std::fmt::Display for Provider {
40 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41 f.write_str(self.as_str())
42 }
43}
44
45#[derive(Debug, Clone, PartialEq, Eq)]
47pub struct UnknownProvider(pub String);
48
49impl std::fmt::Display for UnknownProvider {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 write!(f, "unknown provider {:?} (expected one of: ", self.0)?;
52 for (i, name) in Provider::ACCEPTED_NAMES.iter().enumerate() {
53 if i > 0 {
54 f.write_str(", ")?;
55 }
56 write!(f, "{name}")?;
57 }
58 f.write_str(")")
59 }
60}
61
62impl std::error::Error for UnknownProvider {}
63
64impl std::str::FromStr for Provider {
65 type Err = UnknownProvider;
66
67 fn from_str(s: &str) -> Result<Self, Self::Err> {
71 match s {
72 "cohere" => Ok(Provider::Cohere),
73 "openai" | "openai-compatible" => Ok(Provider::OpenAiCompatible),
74 other => Err(UnknownProvider(other.to_string())),
75 }
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::Provider;
82
83 #[test]
84 fn every_tag_round_trips_through_from_str() {
85 for &p in Provider::ALL {
86 assert_eq!(
87 p.as_str().parse::<Provider>(),
88 Ok(p),
89 "{p} did not round-trip"
90 );
91 }
92 }
93
94 #[test]
95 fn the_user_facing_openai_spelling_is_accepted() {
96 assert_eq!("openai".parse::<Provider>(), Ok(Provider::OpenAiCompatible));
97 }
98
99 #[test]
102 fn every_advertised_name_parses() {
103 for name in Provider::ACCEPTED_NAMES {
104 assert!(
105 name.parse::<Provider>().is_ok(),
106 "advertised {name:?} but it does not parse"
107 );
108 }
109 }
110
111 #[test]
115 fn every_variant_is_reachable_by_an_advertised_name() {
116 for &p in Provider::ALL {
117 let reachable = Provider::ACCEPTED_NAMES
118 .iter()
119 .any(|n| n.parse::<Provider>() == Ok(p));
120 assert!(
121 reachable,
122 "{p} has no entry in ACCEPTED_NAMES, so no documented spelling reaches it"
123 );
124 }
125 }
126
127 #[test]
128 fn an_unknown_name_lists_the_names_users_actually_type() {
129 let err = "nope".parse::<Provider>().unwrap_err();
130 let msg = err.to_string();
131 assert!(msg.contains("nope"), "{msg}");
132 assert!(msg.contains("cohere"), "{msg}");
133 assert!(msg.contains("openai"), "{msg}");
136 assert!(!msg.contains("openai-compatible"), "{msg}");
137 }
138}