server/utils/
env.rs

1//! Environment variable utilities for safe and validated access.
2//!
3//! This module provides utilities for safely accessing environment variables
4//! with proper validation and error handling. It ensures that environment
5//! variables are not only present but also contain valid, non-empty values.
6
7use thiserror::Error;
8
9/// Errors that can occur when accessing environment variables.
10///
11/// Provides detailed error information for different failure scenarios
12/// when working with environment variables, including missing variables,
13/// encoding issues, and empty values.
14#[derive(Debug, Error)]
15pub enum EnvVarError {
16    /// Environment variable is not set
17    #[error(
18        "Environment variable '{name}' not found. Please set this variable in your .env file or environment."
19    )]
20    NotFound { name: String },
21
22    /// Environment variable contains invalid UTF-8 characters
23    #[error(
24        "Environment variable '{name}' contains invalid UTF-8 characters. Please check the value."
25    )]
26    InvalidUtf8 { name: String },
27
28    /// Environment variable is set but contains only whitespace or is empty
29    #[error("Environment variable '{name}' is empty. Please provide a valid value.")]
30    Empty { name: String },
31}
32
33/// Utility functions for safe environment variable handling.
34///
35/// Provides methods for safely accessing environment variables with proper
36/// validation and error handling. All methods trim whitespace and validate
37/// that values are not empty.
38///
39/// # Examples
40///
41/// ```no_run
42/// use quetty_server::utils::EnvUtils;
43///
44/// // Check if a variable exists and has a value
45/// if EnvUtils::has_non_empty_var("DATABASE_URL") {
46///     // Get the validated value
47///     let url = EnvUtils::get_validated_var("DATABASE_URL")?;
48///     println!("Database URL: {}", url);
49/// }
50///
51/// // Get an optional variable
52/// if let Some(debug_level) = EnvUtils::get_optional_var("DEBUG_LEVEL") {
53///     println!("Debug level: {}", debug_level);
54/// }
55/// ```
56pub struct EnvUtils;
57
58impl EnvUtils {
59    /// Checks if an environment variable exists and has a non-empty value.
60    ///
61    /// This method checks both that the variable is set and that it contains
62    /// non-whitespace content after trimming.
63    ///
64    /// # Arguments
65    ///
66    /// * `name` - The name of the environment variable to check
67    ///
68    /// # Returns
69    ///
70    /// `true` if the variable exists and has a non-empty value, `false` otherwise
71    ///
72    /// # Examples
73    ///
74    /// ```no_run
75    /// use quetty_server::utils::EnvUtils;
76    ///
77    /// if EnvUtils::has_non_empty_var("API_KEY") {
78    ///     println!("API key is configured");
79    /// } else {
80    ///     println!("API key is missing or empty");
81    /// }
82    /// ```
83    pub fn has_non_empty_var(name: &str) -> bool {
84        match std::env::var(name) {
85            Ok(value) => !value.trim().is_empty(),
86            Err(_) => false,
87        }
88    }
89
90    /// Gets an environment variable with validation.
91    ///
92    /// Retrieves the environment variable, trims whitespace, and validates
93    /// that it contains a non-empty value. Returns detailed error information
94    /// if the variable is missing, empty, or contains invalid UTF-8.
95    ///
96    /// # Arguments
97    ///
98    /// * `name` - The name of the environment variable to retrieve
99    ///
100    /// # Returns
101    ///
102    /// The trimmed, validated environment variable value
103    ///
104    /// # Errors
105    ///
106    /// Returns [`EnvVarError`] if:
107    /// - The variable is not set ([`EnvVarError::NotFound`])
108    /// - The variable is empty or contains only whitespace ([`EnvVarError::Empty`])
109    /// - The variable contains invalid UTF-8 ([`EnvVarError::InvalidUtf8`])
110    ///
111    /// # Examples
112    ///
113    /// ```no_run
114    /// use quetty_server::utils::EnvUtils;
115    ///
116    /// match EnvUtils::get_validated_var("DATABASE_URL") {
117    ///     Ok(url) => println!("Database URL: {}", url),
118    ///     Err(e) => eprintln!("Configuration error: {}", e),
119    /// }
120    /// ```
121    pub fn get_validated_var(name: &str) -> Result<String, EnvVarError> {
122        match std::env::var(name) {
123            Ok(value) => {
124                let trimmed = value.trim();
125                if trimmed.is_empty() {
126                    Err(EnvVarError::Empty {
127                        name: name.to_string(),
128                    })
129                } else {
130                    Ok(trimmed.to_string())
131                }
132            }
133            Err(std::env::VarError::NotPresent) => Err(EnvVarError::NotFound {
134                name: name.to_string(),
135            }),
136            Err(std::env::VarError::NotUnicode(_)) => Err(EnvVarError::InvalidUtf8 {
137                name: name.to_string(),
138            }),
139        }
140    }
141
142    /// Gets an optional environment variable.
143    ///
144    /// Returns the validated environment variable value if it exists and is valid,
145    /// or `None` if it's missing, empty, or invalid. This is a convenience method
146    /// for cases where the absence of an environment variable is acceptable.
147    ///
148    /// # Arguments
149    ///
150    /// * `name` - The name of the environment variable to retrieve
151    ///
152    /// # Returns
153    ///
154    /// `Some(value)` if the variable exists and is valid, `None` otherwise
155    ///
156    /// # Examples
157    ///
158    /// ```no_run
159    /// use quetty_server::utils::EnvUtils;
160    ///
161    /// let debug_mode = EnvUtils::get_optional_var("DEBUG_MODE")
162    ///     .unwrap_or_else(|| "false".to_string());
163    ///
164    /// if let Some(custom_config) = EnvUtils::get_optional_var("CUSTOM_CONFIG") {
165    ///     println!("Using custom config: {}", custom_config);
166    /// } else {
167    ///     println!("Using default configuration");
168    /// }
169    /// ```
170    pub fn get_optional_var(name: &str) -> Option<String> {
171        Self::get_validated_var(name).ok()
172    }
173}