openrouter-rust 0.1.0

A modular, type-safe Rust client for the OpenRouter API
Documentation
use crate::error::Result;
use reqwest::{header, Client};
use std::time::Duration;

const DEFAULT_BASE_URL: &str = "https://openrouter.ai/api/v1";
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(60);

#[derive(Clone, Debug)]
pub struct OpenRouterClient {
    pub(crate) client: Client,
    pub api_key: String,
    pub base_url: String,
    pub http_referer: Option<String>,
    pub x_title: Option<String>,
}

impl OpenRouterClient {
    pub fn builder() -> OpenRouterClientBuilder {
        OpenRouterClientBuilder::new()
    }

    pub fn build_headers(&self) -> Result<header::HeaderMap> {
        let mut headers = header::HeaderMap::new();

        let auth_value = format!("Bearer {}", self.api_key).parse().map_err(|e| {
            crate::error::OpenRouterError::ConfigError(format!("Invalid API key: {}", e))
        })?;
        headers.insert(header::AUTHORIZATION, auth_value);

        headers.insert(header::CONTENT_TYPE, "application/json".parse().unwrap());

        if let Some(ref referer) = self.http_referer {
            if let Ok(value) = referer.parse() {
                headers.insert("HTTP-Referer", value);
            }
        }

        if let Some(ref title) = self.x_title {
            if let Ok(value) = title.parse() {
                headers.insert("X-Title", value);
            }
        }

        Ok(headers)
    }
}

pub struct OpenRouterClientBuilder {
    api_key: Option<String>,
    base_url: Option<String>,
    http_referer: Option<String>,
    x_title: Option<String>,
    timeout: Option<Duration>,
}

impl OpenRouterClientBuilder {
    pub fn new() -> Self {
        Self {
            api_key: None,
            base_url: None,
            http_referer: None,
            x_title: None,
            timeout: None,
        }
    }

    pub fn api_key(mut self, key: impl Into<String>) -> Self {
        self.api_key = Some(key.into());
        self
    }

    pub fn base_url(mut self, url: impl Into<String>) -> Self {
        self.base_url = Some(url.into());
        self
    }

    pub fn http_referer(mut self, referer: impl Into<String>) -> Self {
        self.http_referer = Some(referer.into());
        self
    }

    pub fn x_title(mut self, title: impl Into<String>) -> Self {
        self.x_title = Some(title.into());
        self
    }

    pub fn timeout(mut self, timeout: Duration) -> Self {
        self.timeout = Some(timeout);
        self
    }

    pub fn build(self) -> Result<OpenRouterClient> {
        let api_key = self.api_key.ok_or_else(|| {
            crate::error::OpenRouterError::ConfigError("API key is required".to_string())
        })?;

        let client = Client::builder()
            .timeout(self.timeout.unwrap_or(DEFAULT_TIMEOUT))
            .build()
            .map_err(crate::error::OpenRouterError::HttpError)?;

        Ok(OpenRouterClient {
            client,
            api_key,
            base_url: self
                .base_url
                .unwrap_or_else(|| DEFAULT_BASE_URL.to_string()),
            http_referer: self.http_referer,
            x_title: self.x_title,
        })
    }
}

impl Default for OpenRouterClientBuilder {
    fn default() -> Self {
        Self::new()
    }
}