koios-sdk 0.1.1

A Rust SDK for the Koios Cardano API
Documentation
// src/client/builder.rs

use crate::{
    client::{AuthConfig, Client, Config},
    error::Result,
    network::Network,
};
use chrono::{DateTime, Utc};
use governor::Quota;
use std::time::Duration;

/// Builder for creating a configured Client instance
#[derive(Debug, Default)]
pub struct ClientBuilder {
    config: Config,
}

impl ClientBuilder {
    /// Create a new client builder with default configuration
    pub fn new() -> Self {
        Self {
            config: Config::default(),
        }
    }

    /// Set the network to use (mainnet, preprod, preview, or guild)
    pub fn network(mut self, network: Network) -> Self {
        self.config.network = network;
        self.config.custom_url = None; // Clear any custom URL when setting network
        self
    }

    /// Set a custom base URL (overrides network setting)
    pub fn base_url<S: Into<String>>(mut self, url: S) -> Self {
        self.config.custom_url = Some(url.into());
        self
    }

    /// Set the authentication token without expiry
    pub fn auth_token<S: Into<String>>(mut self, token: S) -> Self {
        self.config.auth = Some(AuthConfig::new(token.into()));
        self
    }

    /// Set the authentication token with expiry
    pub fn auth_token_with_expiry<S: Into<String>>(
        mut self,
        token: S,
        expiry: DateTime<Utc>,
    ) -> Self {
        self.config.auth = Some(AuthConfig::with_expiry(token.into(), expiry));
        self
    }

    /// Set authentication configuration directly
    pub fn auth(mut self, auth: AuthConfig) -> Self {
        self.config.auth = Some(auth);
        self
    }

    /// Set the request timeout
    pub fn timeout(mut self, timeout: Duration) -> Self {
        self.config.timeout = timeout;
        self
    }

    /// Set the rate limit quota
    pub fn rate_limit(mut self, quota: Quota) -> Self {
        self.config.rate_limit = quota;
        self
    }

    /// Build the client with the current configuration
    pub fn build(self) -> Result<Client> {
        Client::with_config(self.config)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::time::Duration;

    #[test]
    fn test_builder_default() {
        let client = ClientBuilder::new().build().unwrap();
        assert_eq!(client.base_url(), crate::DEFAULT_API_URL);
    }

    #[test]
    fn test_builder_with_auth() {
        let client = ClientBuilder::new()
            .auth_token("test-token")
            .build()
            .unwrap();

        // Use tokio runtime for async auth check
        let rt = tokio::runtime::Runtime::new().unwrap();
        assert!(rt.block_on(client.has_valid_auth()));
        assert_eq!(client.auth_token(), Some("test-token".to_string()));
    }

    #[test]
    fn test_builder_with_auth_expiry() {
        let expiry = Utc::now() + chrono::Duration::hours(1);
        let client = ClientBuilder::new()
            .auth_token_with_expiry("test-token", expiry)
            .build()
            .unwrap();

        let rt = tokio::runtime::Runtime::new().unwrap();
        assert!(rt.block_on(client.has_valid_auth()));
        assert_eq!(client.auth_token(), Some("test-token".to_string()));
    }

    #[test]
    fn test_builder_with_expired_auth() {
        let expiry = Utc::now() - chrono::Duration::hours(1);
        let client = ClientBuilder::new()
            .auth_token_with_expiry("test-token", expiry)
            .build()
            .unwrap();

        let rt = tokio::runtime::Runtime::new().unwrap();
        assert!(!rt.block_on(client.has_valid_auth()));
        assert_eq!(client.auth_token(), None);
    }

    #[test]
    fn test_builder_with_custom_timeout() {
        let timeout = Duration::from_secs(60);
        let client = ClientBuilder::new().timeout(timeout).build().unwrap();

        // Note: We can't directly test the timeout value as it's private
        // but we can verify the client builds successfully
        assert_eq!(client.base_url(), crate::DEFAULT_API_URL);
    }

    #[test]
    fn test_builder_with_rate_limit() {
        let quota = Quota::per_second(50u32.try_into().unwrap());
        let client = ClientBuilder::new().rate_limit(quota).build().unwrap();

        assert_eq!(client.base_url(), crate::DEFAULT_API_URL);
    }

    #[test]
    fn test_builder_with_all_options() {
        let expiry = Utc::now() + chrono::Duration::hours(1);
        let client = ClientBuilder::new()
            .base_url("https://custom.api.com")
            .auth_token_with_expiry("test-token", expiry)
            .timeout(Duration::from_secs(60))
            .rate_limit(Quota::per_second(50u32.try_into().unwrap()))
            .build()
            .unwrap();

        assert_eq!(client.base_url(), "https://custom.api.com");
        assert_eq!(client.auth_token(), Some("test-token".to_string()));
    }

    #[test]
    fn test_builder_custom_auth_config() {
        let auth = AuthConfig::new("test-token".to_string());
        let client = ClientBuilder::new().auth(auth).build().unwrap();

        assert_eq!(client.auth_token(), Some("test-token".to_string()));
    }

    #[test]
    fn test_builder_chaining() {
        let client = ClientBuilder::new()
            .base_url("https://custom.api.com")
            .auth_token("test-token")
            .timeout(Duration::from_secs(60))
            .build()
            .unwrap();

        assert_eq!(client.base_url(), "https://custom.api.com");
        assert_eq!(client.auth_token(), Some("test-token".to_string()));
    }
}