Skip to main content

fatoora_core/
config.rs

1//! Configuration and environment selection.
2use serde::{Deserialize, Serialize};
3use std::str::FromStr;
4use thiserror::Error;
5
6/// ZATCA environment selection for API endpoints.
7/// This determines which URL the API client will use and also is needed for the template format
8/// used in CSR generation.
9/// - NonProduction: This is what ZATCA refers to as the "Integration Sandbox".
10/// - Simulation: This is the "Simulation Test Environment" provided by ZATCA which you need to
11///   sign up for.
12/// - Production: The live production environment.
13/// # Examples
14/// ```rust
15/// use std::str::FromStr;
16/// use fatoora_core::config::EnvironmentType;
17///
18/// let env = EnvironmentType::from_str("simulation")?;
19/// assert_eq!(env, EnvironmentType::Simulation);
20/// use fatoora_core::config::EnvironmentParseError;
21/// # Ok::<(), EnvironmentParseError>(())
22/// ```
23#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
24pub enum EnvironmentType {
25    NonProduction,
26    Simulation,
27    Production,
28}
29
30/// Error returned when parsing an [`EnvironmentType`] from a string.
31#[derive(Debug, Clone, PartialEq, Eq, Hash, Error)]
32pub enum EnvironmentParseError {
33    #[error("invalid environment type: {input}")]
34    Invalid { input: String },
35}
36
37impl FromStr for EnvironmentType {
38    type Err = EnvironmentParseError;
39    fn from_str(env: &str) -> Result<EnvironmentType, EnvironmentParseError> {
40        match env.to_ascii_lowercase().as_str() {
41            "non_production" => Ok(EnvironmentType::NonProduction),
42            "simulation" => Ok(EnvironmentType::Simulation),
43            "production" => Ok(EnvironmentType::Production),
44            _ => Err(EnvironmentParseError::Invalid {
45                input: env.to_string(),
46            }),
47        }
48    }
49}
50
51impl EnvironmentType {
52    pub fn as_str(&self) -> &'static str {
53        match self {
54            EnvironmentType::NonProduction => "non_production",
55            EnvironmentType::Simulation => "simulation",
56            EnvironmentType::Production => "production",
57        }
58    }
59
60    pub fn endpoint_url(&self) -> &'static str {
61        match self {
62            EnvironmentType::NonProduction => {
63                "https://gw-fatoora.zatca.gov.sa/e-invoicing/developer-portal/"
64            }
65            EnvironmentType::Simulation => {
66                "https://gw-fatoora.zatca.gov.sa/e-invoicing/simulation/"
67            }
68            EnvironmentType::Production => "https://gw-fatoora.zatca.gov.sa/e-invoicing/core/",
69        }
70    }
71}
72
73/// Configuration for validation and API clients.
74///
75/// # Examples
76/// ```rust
77/// use fatoora_core::config::{Config, EnvironmentType};
78///
79/// let config = Config::new(EnvironmentType::NonProduction);
80/// # let _ = config;
81/// ```
82#[derive(Debug, Clone, PartialEq, Eq, Hash)]
83pub struct Config {
84    env: EnvironmentType,
85}
86
87impl Config {
88    /// Create a config using the bundled UBL XSD.
89    pub fn new(env: EnvironmentType) -> Self {
90        Self { env }
91    }
92
93    pub fn env(&self) -> EnvironmentType {
94        self.env
95    }
96}
97
98// static function to get default config
99impl Default for Config {
100    fn default() -> Self {
101        Config {
102            env: EnvironmentType::NonProduction,
103        }
104    }
105}