use std::collections::BTreeSet;
use std::fmt;
use serde::{Deserialize, Serialize};
pub const ANTHROPIC_BETA_HEADER: &str = "anthropic-beta";
#[derive(
Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, schemars::JsonSchema,
)]
#[serde(transparent)]
pub struct AnthropicBeta(String);
impl AnthropicBeta {
#[must_use]
pub fn new(flag: impl Into<String>) -> Self {
Self(flag.into())
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
impl fmt::Display for AnthropicBeta {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct BetaHeader(Vec<AnthropicBeta>);
impl BetaHeader {
#[must_use]
pub fn parse(value: &str) -> Self {
let mut betas: Vec<AnthropicBeta> = Vec::new();
for flag in value.split(',').map(str::trim).filter(|f| !f.is_empty()) {
if !betas.iter().any(|b| b.as_str() == flag) {
betas.push(AnthropicBeta::new(flag));
}
}
Self(betas)
}
#[must_use]
pub fn admitted_by(self, policy: &BetaPolicy) -> Self {
Self(self.0.into_iter().filter(|b| policy.admits(b)).collect())
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[must_use]
pub fn betas(&self) -> &[AnthropicBeta] {
&self.0
}
#[must_use]
pub fn render(&self) -> Option<String> {
(!self.0.is_empty()).then(|| {
self.0
.iter()
.map(AnthropicBeta::as_str)
.collect::<Vec<_>>()
.join(",")
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BetaPolicy {
ForwardAll,
Only(BTreeSet<AnthropicBeta>),
}
impl BetaPolicy {
#[must_use]
pub fn admits(&self, beta: &AnthropicBeta) -> bool {
match self {
Self::ForwardAll => true,
Self::Only(accepted) => accepted.contains(beta),
}
}
}