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
use crate::value::{Object, Value};

/// A `Document` that can be evaluated by the solver.
///
/// Rules are solved against Documents, and thus to solve against a data type it must implement
/// `Document`. Implementing `Object` gives this trait for free. Most of the time this trait will
/// not need to be implmented, it is there to enable complex use cases.
///
/// # Implementations
///
/// The implementation for `Object` will just pass the `find` call along to the `Object`
/// implementation of find. If for some reason this was undesired or just not needed below is an
/// example of how to implement `Document`.
///
/// ```
/// use std::borrow::Cow;
///
/// use tau_engine::{Document, Value};
///
/// struct Foo {
///     bar: String,
///     baz: String,
/// }
///
/// impl Document for Foo {
///     fn find(&self, key: &str) -> Option<Value<'_>> {
///         match key {
///             "bar" => Some(Value::String(Cow::Borrowed(&self.bar))),
///             "baz" => Some(Value::String(Cow::Borrowed(&self.baz))),
///             _ => None,
///         }
///     }
/// }
/// ```
#[cfg(not(feature = "sync"))]
pub trait Document {
    /// Looks for a `Value` by key and returns it if found.
    fn find(&self, key: &str) -> Option<Value<'_>>;
}
#[cfg(feature = "sync")]
pub trait Document: Send + Sync {
    fn find(&self, key: &str) -> Option<Value<'_>>;
}

impl Document for &dyn Object {
    #[inline]
    fn find(&self, key: &str) -> Option<Value<'_>> {
        Object::find(*self, key)
    }
}

impl<O: Object> Document for O {
    #[inline]
    fn find(&self, key: &str) -> Option<Value<'_>> {
        Object::find(self, key)
    }
}

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

    use std::borrow::Cow;

    struct Foo {
        bar: String,
    }
    impl Document for Foo {
        fn find(&self, key: &str) -> Option<Value<'_>> {
            match key {
                "bar" => Some(Value::String(Cow::Borrowed(&self.bar))),
                _ => None,
            }
        }
    }

    #[test]
    fn find() {
        let foo = Foo {
            bar: "baz".to_owned(),
        };
        assert_eq!(foo.find("bar").unwrap().as_str().unwrap(), "baz");
    }
}