plansolve 0.25.6

Official Rust client library for the PlanSolve optimization API.
Documentation
use crate::error::{Error, Result};
use crate::field_service::FieldServiceClient;
use crate::professional_services::ProfessionalServicesClient;
use crate::shift::ShiftClient;
use crate::transport::Transport;

/// A PlanSolve API environment. Each has a single fixed base URL.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Environment {
    /// Production — `https://plansolve.app`.
    #[default]
    Production,
    /// Internal test environment — `https://test.plansolve.app`.
    Test,
}

impl Environment {
    /// The fixed base URL for this environment.
    pub fn base_url(self) -> &'static str {
        match self {
            Environment::Production => "https://plansolve.app",
            Environment::Test => "https://test.plansolve.app",
        }
    }
}

/// The main PlanSolve API client. Exposes one sub-client per optimization type.
pub struct Client {
    pub field_service: FieldServiceClient,
    pub professional_services: ProfessionalServicesClient,
    pub shift: ShiftClient,
}

impl Client {
    /// Creates a new client for the production environment.
    pub fn new(api_key: impl Into<String>) -> Self {
        Self::with_environment(api_key, Environment::Production)
    }

    /// Creates a new client for a specific environment.
    pub fn with_environment(api_key: impl Into<String>, environment: Environment) -> Self {
        let transport = Transport::new(environment.base_url().to_string(), api_key.into());

        Self {
            field_service: FieldServiceClient::new(transport.clone()),
            professional_services: ProfessionalServicesClient::new(transport.clone()),
            shift: ShiftClient::new(transport),
        }
    }

    /// Creates a production client using the `PLANSOLVE_API_KEY` environment
    /// variable for the API key. The base URL is always the fixed production URL.
    ///
    /// Returns [`Error::MissingApiKey`] if the variable is unset or empty.
    pub fn from_env() -> Result<Self> {
        let api_key = std::env::var("PLANSOLVE_API_KEY").unwrap_or_default();
        if api_key.is_empty() {
            return Err(Error::MissingApiKey);
        }
        Ok(Self::new(api_key))
    }
}

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

    #[test]
    fn environment_urls_are_fixed() {
        assert_eq!(Environment::Production.base_url(), "https://plansolve.app");
        assert_eq!(Environment::Test.base_url(), "https://test.plansolve.app");
        assert_eq!(Environment::default(), Environment::Production);
    }
}