json_api/view/
context.rs

1use doc::Object;
2use query::Query;
3use value::Set;
4use value::fields::{Key, Path, Segment};
5
6/// A data structure containing render context that can be "forked" and passed
7/// to a child context.
8///
9/// This struct is helpful if you want recursively call [`Resource::to_object`] to render
10/// a document's primary data and included resources.
11///
12/// Since the `Context` struct requires a mutable (unique) reference to a document's
13/// included resources, only one context can be operated on at a time. In other words, if
14/// you want to access a context, it cannot have any children in scope. Since you can
15/// only operate on a single context at time, a recursive implementation of [included
16/// resources] and [sparse field-sets] is much easier.
17///
18/// [`Resource::to_object`]: ../trait.Resource.html#tymethod.to_object
19/// [included resources]: http://jsonapi.org/format/#fetching-includes
20/// [sparse field-sets]: http://jsonapi.org/format/#fetching-sparse-fieldsets
21#[derive(Debug)]
22pub struct Context<'v> {
23    incl: &'v mut Set<Object>,
24    kind: Key,
25    path: Path,
26    query: Option<&'v Query>,
27}
28
29impl<'v> Context<'v> {
30    /// Creates a new, root context.
31    ///
32    /// This constructor can only be used when creating a root context. A child context
33    /// can be created with the `fork` method.
34    ///
35    /// # Example
36    ///
37    /// ```
38    /// # extern crate json_api;
39    /// #
40    /// # use json_api::Error;
41    /// #
42    /// # fn example() -> Result<(), Error> {
43    /// use json_api::value::Set;
44    /// use json_api::view::Context;
45    ///
46    /// let mut included = Set::new();
47    /// let mut ctx = Context::new("posts".parse()?, None, &mut included);
48    /// #
49    /// # Ok(())
50    /// # }
51    /// #
52    /// # fn main() {
53    /// # example().unwrap();
54    /// # }
55    /// ```
56    pub fn new(
57        kind: Key,
58        query: Option<&'v Query>,
59        included: &'v mut Set<Object>,
60    ) -> Self {
61        Context {
62            kind,
63            query,
64            incl: included,
65            path: Path::new(),
66        }
67    }
68
69    /// Returns true if the field name is present in the current context's
70    /// field-set or the current context's field-set does not exist.
71    pub fn field(&self, name: &str) -> bool {
72        self.query
73            .and_then(|q| q.fields.get(&self.kind))
74            .map_or(true, |f| f.contains(name))
75    }
76
77    /// Creates a new child context from `self`.
78    pub fn fork(&mut self, kind: Key, key: &Key) -> Context {
79        Context {
80            kind,
81            incl: self.incl,
82            path: self.path.join(key),
83            query: self.query,
84        }
85    }
86
87    /// Adds the `value` to the context's included resource set.
88    ///
89    /// If the set did not have this value present, `true` is returned.
90    ///
91    /// If the set did have this value present, `false` is returned.
92    pub fn include(&mut self, value: Object) -> bool {
93        self.incl.insert(value)
94    }
95
96    /// Returns `true` if the context is valid with respect to parent context(s).
97    ///
98    /// If there is no parent context (i.e the current context represents the primary
99    /// data of the document), this will always return `false`.
100    ///
101    /// if there is a parent context and this function returns `false`, this context can
102    /// should be ignored.
103    pub fn included(&self) -> bool {
104        self.query.map_or(false, |q| q.include.contains(&self.path))
105    }
106}