Skip to main content

envbind/
binder.rs

1//! Binding coordinator for typed variable specs.
2
3use crate::environment::Environment;
4use crate::error::BindError;
5
6/// Bind one typed value from an environment source.
7pub trait Binding<T> {
8    /// Load the value from the given environment.
9    fn bind<E: Environment>(&self, environment: &E) -> Result<T, BindError>;
10}
11
12/// Extension helpers for typed binding specs.
13pub trait BindingExt: Sized {
14    /// Treat missing or explicitly empty values as `None`.
15    ///
16    /// Parsing and validation errors still return `Err`.
17    #[must_use]
18    fn optional(self) -> OptionalVar<Self> {
19        OptionalVar { binding: self }
20    }
21}
22
23impl<B> BindingExt for B {}
24
25/// Optional wrapper for any binding spec.
26pub struct OptionalVar<B> {
27    binding: B,
28}
29
30impl<T, B> Binding<Option<T>> for OptionalVar<B>
31where
32    B: Binding<T>,
33{
34    fn bind<E: Environment>(&self, environment: &E) -> Result<Option<T>, BindError> {
35        match self.binding.bind(environment) {
36            Ok(value) => Ok(Some(value)),
37            Err(BindError::MissingVariable { .. } | BindError::EmptyVariable { .. }) => Ok(None),
38            Err(error) => Err(error),
39        }
40    }
41}
42
43/// Coordinate value specs against one environment source.
44pub struct Binder<E>
45where
46    E: Environment,
47{
48    environment: E,
49}
50
51impl<E> Binder<E>
52where
53    E: Environment,
54{
55    /// Build a binder from an environment source.
56    #[must_use]
57    pub fn new(environment: E) -> Self {
58        Self { environment }
59    }
60
61    /// Bind one typed value through a spec.
62    pub fn bind<T, B>(&self, binding: &B) -> Result<T, BindError>
63    where
64        B: Binding<T>,
65    {
66        binding.bind(&self.environment)
67    }
68
69    /// Return the underlying environment source.
70    #[must_use]
71    pub fn environment(&self) -> &E {
72        &self.environment
73    }
74}