use std::{collections::HashMap, time::Duration};
use http::HeaderMap;
pub const DEFAULT_BASE_URL: &str = "http://localhost:54321";
pub const DEFAULT_TIMEOUT: Duration = Duration::from_mins(1);
pub const DEFAULT_MAX_RETRIES: u32 = 2;
pub const ENV_BASE_URL: &str = "OPENCODE_BASE_URL";
#[derive(Debug, Clone)]
pub struct ClientOptions {
pub base_url: Option<String>,
pub timeout: Option<Duration>,
pub max_retries: Option<u32>,
pub default_headers: Option<HeaderMap>,
pub default_query: Option<HashMap<String, String>>,
}
impl Default for ClientOptions {
fn default() -> Self {
let base_url = std::env::var(ENV_BASE_URL).ok();
Self {
base_url,
timeout: None,
max_retries: None,
default_headers: None,
default_query: None,
}
}
}
impl ClientOptions {
#[must_use]
pub const fn empty() -> Self {
Self {
base_url: None,
timeout: None,
max_retries: None,
default_headers: None,
default_query: None,
}
}
#[must_use]
pub(crate) fn resolve_base_url(&self) -> &str {
self.base_url.as_deref().unwrap_or(DEFAULT_BASE_URL)
}
#[must_use]
pub(crate) fn resolve_timeout(&self) -> Duration {
self.timeout.unwrap_or(DEFAULT_TIMEOUT)
}
#[must_use]
pub(crate) fn resolve_max_retries(&self) -> u32 {
self.max_retries.unwrap_or(DEFAULT_MAX_RETRIES)
}
#[must_use]
pub(crate) fn resolve_default_headers(&self) -> HeaderMap {
self.default_headers.clone().unwrap_or_default()
}
#[must_use]
pub(crate) fn resolve_default_query(&self) -> HashMap<String, String> {
self.default_query.clone().unwrap_or_default()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_has_all_none() {
let opts = ClientOptions::empty();
assert!(opts.base_url.is_none());
assert!(opts.timeout.is_none());
assert!(opts.max_retries.is_none());
assert!(opts.default_headers.is_none());
assert!(opts.default_query.is_none());
}
#[test]
fn resolve_falls_back_to_defaults() {
let opts = ClientOptions::empty();
assert_eq!(opts.resolve_base_url(), DEFAULT_BASE_URL);
assert_eq!(opts.resolve_timeout(), DEFAULT_TIMEOUT);
assert_eq!(opts.resolve_max_retries(), DEFAULT_MAX_RETRIES);
assert!(opts.resolve_default_headers().is_empty());
assert!(opts.resolve_default_query().is_empty());
}
#[test]
fn resolve_uses_provided_values() {
let opts = ClientOptions {
base_url: Some("http://example.com".to_owned()),
timeout: Some(Duration::from_secs(30)),
max_retries: Some(5),
default_headers: None,
default_query: None,
};
assert_eq!(opts.resolve_base_url(), "http://example.com");
assert_eq!(opts.resolve_timeout(), Duration::from_secs(30));
assert_eq!(opts.resolve_max_retries(), 5);
}
}