serverless-fn 0.1.0

A Rust library for simplifying serverless function development and invocation
Documentation
//! Client runtime for invoking serverless functions.
//!
//! Handles initiating remote calls to serverless functions.

use std::collections::HashMap;

use crate::{
    config::{Config, DeployStrategy},
    error::ServerlessError,
    serializer::{Serializer, get_default_serializer},
    transport::{Transport, get_default_transport},
};

/// Client for invoking serverless functions.
pub struct Client {
    config: Config,
    transport: Box<dyn Transport>,
    serializer: Serializer,
}

impl Client {
    /// Creates a new client with default configuration.
    ///
    /// # Errors
    ///
    /// Returns an error if the client cannot be created.
    pub fn new() -> Result<Self, ServerlessError> {
        let config = Config::from_env();
        let serializer = get_default_serializer();
        let transport = get_default_transport(config.timeout, config.retries);

        Ok(Self {
            config,
            transport,
            serializer,
        })
    }

    /// Creates a new client with custom configuration.
    ///
    /// # Errors
    ///
    /// Returns an error if the client cannot be created.
    pub fn with_config(config: Config) -> Result<Self, ServerlessError> {
        let serializer = get_default_serializer();
        let transport = get_default_transport(config.timeout, config.retries);

        Ok(Self {
            config,
            transport,
            serializer,
        })
    }

    /// Calls a serverless function by name with the given payload.
    ///
    /// # Arguments
    ///
    /// * `function_name` - Name of the function to call
    /// * `payload` - Serialized function arguments
    /// * `headers` - Optional HTTP headers for the request
    ///
    /// # Errors
    ///
    /// Returns an error if the function call fails.
    pub async fn call_function(
        &self,
        function_name: &str,
        payload: Vec<u8>,
        headers: Option<HashMap<String, String>>,
    ) -> Result<Vec<u8>, ServerlessError> {
        match self.config.deploy_strategy() {
            DeployStrategy::Remote => self.transport.call(function_name, payload, headers).await,
            DeployStrategy::Local => Err(ServerlessError::Generic(
                "Local call mode should not reach client runtime".into(),
            )),
            DeployStrategy::Mock => Ok(b"{}".to_vec()),
        }
    }

    /// Returns a reference to the underlying transport.
    #[must_use]
    pub fn transport(&self) -> &dyn Transport {
        self.transport.as_ref()
    }

    /// Returns a reference to the underlying serializer.
    #[must_use]
    pub fn serializer(&self) -> &Serializer {
        &self.serializer
    }
}

impl Default for Client {
    fn default() -> Self {
        Self::new().expect("Failed to create default Client")
    }
}

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

    #[test]
    fn test_client_default() {
        let client = Client::default();
        assert_eq!(client.config.base_url, "http://localhost:3000");
    }
}