// 配置里的小值类型:认证凭据与响应体解码方式。它们与 Config 拆在不同文件,
// 便于各自独立演进(也满足 AGENTS.md RL-04 的 300 行上限)。

///|
/// `Authorization: Basic ...` 用的凭据。
///
/// 两个字段都是 `Option`,所以它的合并是逐字段的深合并:
/// 默认值只给了 `username`、请求只给了 `password`,合并后两者都在。
/// 这正是 axios `mergeDeepProperties` 对普通对象(plain object)的处理方式。
pub(all) struct Auth {
  username : String?
  password : String?
} derive(Eq, Default, Debug)

///|
pub extend Auth with Eq::{equal, not_equal}

///|
pub extend Auth with Default::{default}

///|
pub extend Auth with @debug.Debug::{to_repr}

///|
/// 响应体字节怎么解码成文本,对应 axios 的 `responseEncoding`(默认 `utf-8`)。
///
/// 它只描述「字节 → 文本」这一步:`Response::text()` 就是按它解码出来的字符串,
/// `Response::json()` 也先走这一步、再交给 `@json.parse`。
/// 本项目**不做自动解析**(axios 的 `responseType` / `transformResponse`
/// 没有对应物):`json()` 必须显式调用,且不拿 `Content-Type` 做判断。
///
/// **这里只描述怎么解码,不描述读不读。** 「读全量还是流」由入口决定:
/// `Client::request` 读全量并交出响应、`Client::stream` 交原始字节流、
/// `Client::sse` 按规范固定 UTF-8 解析事件。所以这个字段只在 `request`
/// 那条路上有落点——流式入口连 `Response` 都没有,解码不是它们的事。
///
/// 解码一律是 lossy 的:遇到该编码下非法的字节用替换字符 `U+FFFD`,
/// 不抛错。响应正文由服务端说了算,为了几个坏字节把整个响应判成失败
/// 得不偿失;要精确字节请读 `Response::bytes()`。
pub(all) enum ResponseEncoding {
  /// 默认:UTF-8
  Utf8
  /// ISO-8859-1:每个字节直接映射成 `U+00XX`,任何字节序列都能解出文本
  Latin1
  /// 只认 `0x00`–`0x7F`,更高的字节是替换字符
  Ascii
  /// UTF-16 小端(axios 里的 `utf16le` / `ucs2`)
  Utf16le
} derive(Eq, Debug)

///|
pub extend ResponseEncoding with Eq::{equal, not_equal}

///|
pub extend ResponseEncoding with @debug.Debug::{to_repr}

///|
/// 渲染成 axios `responseEncoding` 风格的取值,用于 `Config::to_string` 与日志。
pub fn ResponseEncoding::to_string(self : ResponseEncoding) -> String {
  match self {
    Utf8 => "utf-8"
    Latin1 => "latin1"
    Ascii => "ascii"
    Utf16le => "utf-16le"
  }
}

///|
/// 渲染认证信息:用户名照常显示,密码一律替换成 ``。
///
/// 配置经常整体进日志或断言失败信息,明文打印密码会把凭据泄漏到
/// 日志文件与 CI 输出里,所以这里主动脱敏。
pub fn Auth::to_string(self : Auth) -> String {
  let buf = StringBuilder()
  let mut first = true
  match self.username {
    Some(username) => first = push_field(buf, first, "username", username)
    None => ()
  }
  match self.password {
    Some(_) => first = push_field(buf, first, "password", "")
    None => ()
  }
  render_braced("Auth", buf.to_string(), first)
}