brazen 0.0.4

A stateless, swiss-army-knife adapter for every LLM provider and protocol.
Documentation
//! The one `[[provider]]` array ⇄ row-list seam (config §2.2): the custom
//! `Deserialize` for [`PartialConfig`]. The array's ORDER is kept — it is the
//! routing priority list (arch §4.3.1), and its line order is priority's one
//! wire form. A `[[provider]]` row carries its own `name` (single source of
//! truth), so it cannot be a flatten; `deny_unknown_fields` on the row makes a
//! typo'd key a `MalformedFile` (§2.3). An unmodeled top-level key lands in
//! `extra` rather than erroring — the one sanctioned long-tail valve.

use std::collections::{BTreeMap, BTreeSet};
use std::fmt;

use serde::de::{self, Deserializer, MapAccess, SeqAccess, Visitor};
use serde::Deserialize;
use serde_json::{Map, Value};

use crate::auth::OAuthConfig;
use crate::config::partial::{PartialConfig, PartialProvider};
use crate::config::provider::{AuthId, HeaderSpec, ModelsOverride, ProtocolId, TransportSpec};
use crate::store::AmbientSpec;

/// One `[[provider]]` table on the wire: `name` plus the sparse row fields.
/// `deny_unknown_fields` makes a typo'd row key a parse error → `MalformedFile`
/// (config §2.3, §7); flatten is avoided precisely so the deny can fire.
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct ProviderRow {
    name: String,
    base_url: Option<String>,
    exec: Option<String>,
    transport: Option<TransportSpec>,
    protocol: Option<ProtocolId>,
    auth: Option<AuthId>,
    beta_headers: Option<Vec<(String, String)>>,
    generation_query: Option<Vec<(String, String)>>,
    api_header: Option<HeaderSpec>,
    model_aliases: Option<BTreeMap<String, String>>,
    model_prefixes: Option<Vec<String>>,
    #[serde(default)]
    body_defaults: Map<String, Value>,
    unsupported_body_keys: Option<Vec<String>>,
    models: Option<ModelsOverride>,
    oauth: Option<OAuthConfig>,
    ambient: Option<AmbientSpec>,
}

impl ProviderRow {
    fn into_pair(self) -> (String, PartialProvider) {
        (
            self.name,
            PartialProvider {
                base_url: self.base_url,
                exec: self.exec,
                transport: self.transport,
                protocol: self.protocol,
                auth: self.auth,
                api_header: self.api_header,
                beta_headers: self.beta_headers,
                generation_query: self.generation_query,
                model_aliases: self.model_aliases,
                model_prefixes: self.model_prefixes,
                body_defaults: self.body_defaults,
                unsupported_body_keys: self.unsupported_body_keys,
                models: self.models,
                oauth: self.oauth,
                ambient: self.ambient,
            },
        )
    }
}

/// The `provider` key is overloaded by value type: a string selects a provider
/// (`provider = "anthropic"`), an array-of-tables defines rows (`[[provider]]`).
/// A single TOML file can carry only one form, so the two never collide.
///
/// Dispatch is on the value's SHAPE — a hand-rolled visitor, NOT `serde(untagged)`
/// (the same ruling as `Tool`, arch §3.1: untagged gives unusable errors). Untagged
/// buffers the value into serde's private `Content` and then TRIES each variant,
/// discarding the loser's error: a single mistyped row key made the whole file
/// `data did not match any variant of untagged enum ProviderField`, pointed at line
/// 1 column 1, with the real cause (`unknown field ...`) and its span thrown away —
/// and the buffering left the parse hostage to serde's private re-encoding, so a
/// dependency bump could shift behavior under a lockfile-free `cargo install`
/// (bl-50af). Dispatching on the shape has no buffer, no losing branch, and
/// propagates the row's own error verbatim.
enum ProviderField {
    Selector(String),
    Rows(Vec<ProviderRow>),
}

impl<'de> Deserialize<'de> for ProviderField {
    fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
        d.deserialize_any(ProviderFieldVisitor)
    }
}

struct ProviderFieldVisitor;

impl<'de> Visitor<'de> for ProviderFieldVisitor {
    type Value = ProviderField;

    fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("a provider name or a list of [[provider]] tables")
    }

    fn visit_str<E: de::Error>(self, v: &str) -> Result<ProviderField, E> {
        Ok(ProviderField::Selector(v.to_owned()))
    }

    fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<ProviderField, A::Error> {
        let mut rows = Vec::new();
        while let Some(row) = seq.next_element()? {
            rows.push(row);
        }
        Ok(ProviderField::Rows(rows))
    }
}

impl<'de> Deserialize<'de> for PartialConfig {
    fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
        d.deserialize_map(PartialConfigVisitor)
    }
}

struct PartialConfigVisitor;

impl<'de> Visitor<'de> for PartialConfigVisitor {
    type Value = PartialConfig;

    fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("a brazen config table")
    }

    fn visit_map<M: MapAccess<'de>>(self, mut map: M) -> Result<PartialConfig, M::Error> {
        let mut cfg = PartialConfig::default();
        while let Some(key) = map.next_key::<String>()? {
            match key.as_str() {
                "provider" => match map.next_value::<ProviderField>()? {
                    ProviderField::Selector(name) => cfg.provider = Some(name),
                    ProviderField::Rows(rows) => {
                        // Rows land in DECLARATION order — that order is the routing
                        // priority (config §2.2), so nothing sorts or re-keys them. A
                        // duplicate `name` WITHIN this one layer is a contradiction and
                        // a `MalformedFile` (§2.2), surfaced against the seen-set; one
                        // name in TWO layers is not a duplicate — that is the fold's
                        // per-name merge (§3.2).
                        let mut seen: BTreeSet<String> = BTreeSet::new();
                        for row in rows {
                            let (name, partial) = row.into_pair();
                            if !seen.insert(name.clone()) {
                                return Err(de::Error::custom(format!(
                                    "duplicate provider name `{name}`"
                                )));
                            }
                            cfg.providers.push((name, partial));
                        }
                    }
                },
                "model" => cfg.model = Some(map.next_value()?),
                // A TOP-LEVEL `base_url` is the host-override scalar (config §4.5), NOT a
                // provider row's `base_url` (that rides inside a `[[provider]]` table); the
                // two keys never collide, so a file can carry both.
                "base_url" => cfg.base_url = Some(map.next_value()?),
                "api_key" => cfg.api_key = Some(map.next_value()?),
                "output" => cfg.output = Some(map.next_value()?),
                "thinking" => cfg.thinking = Some(map.next_value()?),
                "max_tokens" => cfg.max_tokens = Some(map.next_value()?),
                "temperature" => cfg.temperature = Some(map.next_value()?),
                "top_p" => cfg.top_p = Some(map.next_value()?),
                "reasoning" => cfg.reasoning = Some(map.next_value()?),
                "stream" => cfg.stream = Some(map.next_value()?),
                "timeout" => cfg.timeout = Some(map.next_value()?),
                "system" => cfg.system = Some(map.next_value()?),
                // The `[ingress]` table (ingress §6): a top-level sibling of
                // `[[provider]]`, deny_unknown_fields like a row — decoded here so
                // it never falls into the `extra` valve below.
                "ingress" => cfg.ingress = Some(map.next_value()?),
                // The one sanctioned long-tail: an unmodeled top-level key lands
                // in `extra` rather than erroring (config §2.3).
                _ => {
                    cfg.extra.insert(key, map.next_value()?);
                }
            }
        }
        Ok(cfg)
    }
}