1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
use super::{
    common, CompoundKey, Configuration, ConfigurationDefinition, ConfigurationDefinitionLens,
    ConfigurationRead, ConfigurationTree, Value,
};
use crate::error::ConfigurationError;
use serde::de::DeserializeOwned;
use std::convert::{From, TryFrom, TryInto};

/// Provides lensing capabilities to a configuration reader.
///
/// Allows scoping into configuration section of choice for read only access.
/// It can be seen as a borrowed version of [`Configuration`](super::Configuration).
///
/// To read values from `Lens` you need to pull [`ConfigurationRead`](super::ConfigurationRead) in scope.
///
/// # Example
///```rust
///use miau::configuration::{Configuration, ConfigurationRead};
///
///let configuration = Configuration::default(); //  aka empty
///
///let lens = configuration.lens();
///match lens.try_lens("key") {
///    Ok(descended) => {
///        let word: Option<String> = lens.get("word");
///        assert_eq!(None, word);
///    }
///    // lensing operation can fail if e.g key is unparsable
///    Err(e) => println!("Oh no! {}", e),
///};
///```
#[derive(Debug)]
pub struct Lens<'config> {
    roots: Vec<ConfigurationDefinitionLens<'config>>,
}

impl<'config> Lens<'config> {
    /// Creates new instance of `Lens` from a single configuration definition.
    ///
    /// Such lens might be useful to scope into configurations section of interest to reduce path length needed to retrive values.
    pub fn new_singular(def: &'config ConfigurationDefinition) -> Self {
        Lens {
            roots: vec![def.into()],
        }
    }

    /// Creates new instance of `Lens` from [`Configuration`](super::Configuration).
    ///
    /// It enables lensing into multiple configuration trees at once.
    /// It handles situation like possibility of absence of given subtree in subset of configuration trees.
    /// When substree is missing, given tree is simply ignored in lens.
    pub fn new(config: &'config Configuration) -> Self {
        Lens {
            roots: config.roots.iter().map(|r| r.into()).collect(),
        }
    }

    /// Attempts to lens into given `Lens`
    ///
    /// Function can only return error if transformation of `keys` failed.
    /// If none of configuration trees contains requested key, empty `Lens` will be returned.
    pub fn try_lens<S>(&self, keys: S) -> Result<Self, ConfigurationError>
    where
        S: TryInto<CompoundKey, Error = ConfigurationError>,
    {
        let keys = keys.try_into()?;

        let new_roots = self
            .roots
            .iter()
            .map(|def| def.mutate(|node| node.descend_many(&keys).ok()))
            .collect();

        Ok(Lens { roots: new_roots })
    }

    /// Deserializes `Lens` into strongly typed struct.
    ///
    /// It is only required that struct to be deserialized to implements `Deserialize`
    /// and contains no borrowed fields, for instance `&str`.
    /// Due to memory model of `miau` it is impossible to deserialize into such fields.
    pub fn try_convert_into<T: DeserializeOwned>(self) -> Result<T, ConfigurationError> {
        self.merge_cloned().and_then(|node| node.try_convert_into())
    }

    /// Merges trees contained in `Lens` into one tree by cloning them.
    pub fn merge_cloned(mut self) -> Result<ConfigurationTree, ConfigurationError> {
        common::merge_cloned(self.roots.drain(..).filter_map(|def| def.node))
    }
}

impl<'config, T, K> ConfigurationRead<'config, T, K> for Lens<'config>
where
    T: TryFrom<&'config Value, Error = ConfigurationError>,
    K: TryInto<CompoundKey, Error = ConfigurationError>,
{
    fn get_result(&'config self, keys: K) -> Result<Option<T>, ConfigurationError> {
        let keys = keys.try_into()?;
        common::get_result_internal(self.roots.iter().filter_map(|def| def.node), &keys)
    }
}

impl<'config> From<&'config ConfigurationDefinition> for Lens<'config> {
    fn from(config: &'config ConfigurationDefinition) -> Self {
        Lens::new_singular(config)
    }
}

impl<'config> From<&'config Configuration> for Lens<'config> {
    fn from(config: &'config Configuration) -> Self {
        Lens::new(config)
    }
}