bamboo-domain 2026.5.3

Domain models and shared types for the Bamboo agent framework
Documentation
use serde::{Deserialize, Serialize};
use std::fmt;
use std::str::FromStr;

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum ProviderFormat {
    #[serde(rename = "openai")]
    OpenAI,
    #[serde(rename = "anthropic")]
    Anthropic,
    #[serde(rename = "gemini")]
    Gemini,
    #[serde(rename = "copilot")]
    Copilot,
    #[serde(rename = "bodhi")]
    Bodhi,
}

impl fmt::Display for ProviderFormat {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let s = match self {
            ProviderFormat::OpenAI => "openai",
            ProviderFormat::Anthropic => "anthropic",
            ProviderFormat::Gemini => "gemini",
            ProviderFormat::Copilot => "copilot",
            ProviderFormat::Bodhi => "bodhi",
        };
        f.write_str(s)
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParseProviderFormatError(pub String);

impl fmt::Display for ParseProviderFormatError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "unknown provider format: {}", self.0)
    }
}
impl std::error::Error for ParseProviderFormatError {}

impl FromStr for ProviderFormat {
    type Err = ParseProviderFormatError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "openai" => Ok(Self::OpenAI),
            "anthropic" => Ok(Self::Anthropic),
            "gemini" => Ok(Self::Gemini),
            "copilot" => Ok(Self::Copilot),
            "bodhi" => Ok(Self::Bodhi),
            other => Err(ParseProviderFormatError(other.to_string())),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn display_roundtrip() {
        for f in [
            ProviderFormat::OpenAI,
            ProviderFormat::Anthropic,
            ProviderFormat::Gemini,
            ProviderFormat::Copilot,
            ProviderFormat::Bodhi,
        ] {
            assert_eq!(ProviderFormat::from_str(&f.to_string()).unwrap(), f);
        }
    }
    #[test]
    fn serde_snake_case() {
        let json = serde_json::to_string(&ProviderFormat::OpenAI).unwrap();
        assert_eq!(json, "\"openai\"");
        let parsed: ProviderFormat = serde_json::from_str("\"anthropic\"").unwrap();
        assert_eq!(parsed, ProviderFormat::Anthropic);
    }
    #[test]
    fn from_str_unknown() {
        assert!(ProviderFormat::from_str("xxx").is_err());
    }
}