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}