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
use doc::Object;
use query::Query;
use value::Set;
use value::fields::{Key, Path, Segment};

/// A data structure containing render context that can be "forked" and passed
/// to a child context.
///
/// This struct is helpful if you want recursively call [`Resource::to_object`] to render
/// a document's primary data and included resources.
///
/// Since the `Context` struct requires a mutable (unique) reference to a document's
/// included resources, only one context can be operated on at a time. In other words, if
/// you want to access a context, it cannot have any children in scope. Since you can
/// only operate on a single context at time, a recursive implementation of [included
/// resources] and [sparse field-sets] is much easier.
///
/// [`Resource::to_object`]: ../trait.Resource.html#tymethod.to_object
/// [included resources]: http://jsonapi.org/format/#fetching-includes
/// [sparse field-sets]: http://jsonapi.org/format/#fetching-sparse-fieldsets
#[derive(Debug)]
pub struct Context<'v> {
    incl: &'v mut Set<Object>,
    kind: Key,
    path: Path,
    query: Option<&'v Query>,
}

impl<'v> Context<'v> {
    /// Creates a new, root context.
    ///
    /// This constructor can only be used when creating a root context. A child context
    /// can be created with the `fork` method.
    ///
    /// # Example
    ///
    /// ```
    /// # extern crate json_api;
    /// #
    /// # use json_api::Error;
    /// #
    /// # fn example() -> Result<(), Error> {
    /// use json_api::value::Set;
    /// use json_api::view::Context;
    ///
    /// let mut included = Set::new();
    /// let mut ctx = Context::new("posts".parse()?, None, &mut included);
    /// #
    /// # Ok(())
    /// # }
    /// #
    /// # fn main() {
    /// # example().unwrap();
    /// # }
    /// ```
    pub fn new(
        kind: Key,
        query: Option<&'v Query>,
        included: &'v mut Set<Object>,
    ) -> Self {
        Context {
            kind,
            query,
            incl: included,
            path: Path::new(),
        }
    }

    /// Returns true if the field name is present in the current context's
    /// field-set or the current context's field-set does not exist.
    pub fn field(&self, name: &str) -> bool {
        self.query
            .and_then(|q| q.fields.get(&self.kind))
            .map_or(true, |f| f.contains(name))
    }

    /// Creates a new child context from `self`.
    pub fn fork(&mut self, kind: Key, key: &Key) -> Context {
        Context {
            kind,
            incl: self.incl,
            path: self.path.join(key),
            query: self.query,
        }
    }

    /// Adds the `value` to the context's included resource set.
    ///
    /// If the set did not have this value present, `true` is returned.
    ///
    /// If the set did have this value present, `false` is returned.
    pub fn include(&mut self, value: Object) -> bool {
        self.incl.insert(value)
    }

    /// Returns `true` if the context is valid with respect to parent context(s).
    ///
    /// If there is no parent context (i.e the current context represents the primary
    /// data of the document), this will always return `false`.
    ///
    /// if there is a parent context and this function returns `false`, this context can
    /// should be ignored.
    pub fn included(&self) -> bool {
        self.query.map_or(false, |q| q.include.contains(&self.path))
    }
}