trimmer/
vars.rs

1use std::rc::Rc;
2use std::fmt::{self, Debug};
3use std::iter::empty;
4
5use render_error::DataError;
6use owning_ref::{OwningRef, Erased};
7use {Var, Output, Number, Comparable};
8
9pub type VarRef<'render> = OwningRef<Rc<Erased+'render>,
10                                     Variable<'render>+'render>;
11
12
13pub struct RefVar<'render>(pub VarRef<'render>);
14
15pub enum Val<'a, 'render: 'a>{
16    Ref(&'a (Variable<'render> + 'render)),
17    Rc(VarRef<'render>),
18}
19
20#[derive(Debug)]
21pub struct Undefined;
22
23#[derive(Debug)]
24pub struct Empty;
25
26pub const UNDEFINED: &'static Undefined = &Undefined;
27pub const EMPTY: &'static Empty = &Empty;
28pub const TRUE: &'static bool = &true;
29pub const FALSE: &'static bool = &false;
30
31/// A trait that you need to implement to put variable into the rendering
32/// context
33///
34/// Note: by default all operations return unsupported error
35pub trait Variable<'render>: Debug {
36    /// Evaluates `a.b` operation
37    ///
38    /// Depending on your domain `a.x` may be equivalent of `a["x"]` or
39    /// maybe not. Integer arguments `a.0` are not supported.
40    fn attr<'x>(&'x self,  _attr: &str)
41        -> Result<Var<'x, 'render>, DataError>
42        where 'render: 'x
43    {
44        Err(DataError::AttrUnsupported(self.typename()))
45    }
46    /// Evaluates `a[b]`
47    ///
48    /// Depending on your domain `a["x"]` may be equivalent of `a.x` or
49    /// maybe not.
50    ///
51    /// You may exract string value for a key with `key.as_str_key()`
52    /// and `key.as_int_key()`.
53    ///
54    /// Note that actual key may have a (rust) type that is different from
55    /// type of self (i.e. may come from different library).
56    fn index<'x>(&'x self, _key: &(Variable<'render> + 'render))
57        -> Result<Var<'x, 'render>, DataError>
58        where 'render: 'x
59    {
60        Err(DataError::IndexUnsupported(self.typename()))
61    }
62    /// Evaluates `{{ x }}`
63    ///
64    /// This operation may not be useful for array-, and mapping-like values
65    fn output(&self) -> Result<Output, DataError> {
66        Err(DataError::OutputUnsupported(self.typename()))
67    }
68    /// Returns type name to use in error messages
69    ///
70    /// Note this must return actual type of value from user point of view
71    /// not just rust type. For example for `Json` type it should describe
72    /// `Json::Object` as a mapping and `Json::Array` as an array, not just
73    /// return `Json`
74    fn typename(&self) -> &'static str;
75    /// Return string value of the variable used as key in index
76    ///
77    /// String keys are used for indexing dicts
78    ///
79    /// It's okay not to implement this method for complex variables
80    fn as_str_key<'x>(&'x self) -> Result<&'x str, DataError> {
81        Err(DataError::StrKeyUnsupported(self.typename()))
82    }
83    /// Return intenger value of the variable used as key
84    ///
85    /// Integer keys are used for indexing arrays
86    ///
87    /// It's okay not to implement this method for complex variables
88    fn as_int_key(&self) -> Result<usize, DataError> {
89        Err(DataError::IntKeyUnsupported(self.typename()))
90    }
91    /// Return boolean value of this object
92    ///
93    /// This is used in conditions `## if x`
94    fn as_bool(&self) -> Result<bool, DataError> {
95        Err(DataError::BoolUnsupported(self.typename()))
96    }
97
98    /// Return value as it could be treated in numeric context
99    ///
100    /// Numeric context is where `+,-,*,/,%` operators are used. Use standard
101    /// `into()` conversion to convert built-in value into internal
102    /// representation.
103    fn as_number(&self) -> Result<Number, DataError> {
104        Err(DataError::NumberUnsupported(self.typename()))
105    }
106
107    /// Return value of the object that might be compared to another value
108    ///
109    /// Note we can only compare numbers with number and strings with
110    /// strings. All other types of comparisons are unsupported. Use standard
111    /// `into()` conversion to convert built-in value into internal
112    /// representation.
113    fn as_comparable(&self) -> Result<Comparable, DataError> {
114        Err(DataError::ComparisonUnsupported(self.typename()))
115    }
116
117    /// Return iterator over the value if appropriate
118    fn iterate<'x>(&'x self)
119        -> Result<Box<Iterator<Item=Var<'x, 'render>>+'x>, DataError>
120        where 'render: 'x
121    {
122        Err(DataError::IterationUnsupported(self.typename()))
123    }
124
125    /// Return iterator over pairs if appropriate
126    fn iterate_pairs<'x>(&'x self)
127        -> Result<Box<Iterator<Item=(Var<'x, 'render>, Var<'x, 'render>)>+'x>,
128                  DataError>
129        where 'render: 'x
130    {
131        Err(DataError::PairIterationUnsupported(self.typename()))
132    }
133}
134
135impl<'a> Variable<'a> for Undefined {
136    fn attr<'x>(&'x self, _attr: &str)
137        -> Result<Var<'x, 'a>, DataError>
138        where 'a: 'x
139    {
140        Ok(Var::undefined())
141    }
142    fn index<'x>(&'x self,  _key: &Variable)
143        -> Result<Var<'x, 'a>, DataError>
144        where 'a: 'x
145    {
146        Ok(Var::undefined())
147    }
148    fn output(&self) -> Result<Output, DataError> {
149        Ok(Output::empty())
150    }
151    fn typename(&self) -> &'static str {
152        "undefined"
153    }
154    fn as_bool(&self) -> Result<bool, DataError> {
155        Ok(false)
156    }
157    fn iterate<'x>(&'x self)
158        -> Result<Box<Iterator<Item=Var<'x, 'a>>+'x>, DataError>
159        where 'a: 'x
160    {
161        Ok(Box::new(empty()))
162    }
163    fn iterate_pairs<'x>(&'x self)
164        -> Result<Box<Iterator<Item=(Var<'x, 'a>, Var<'x, 'a>)>+'x>,
165                  DataError>
166        where 'a: 'x
167    {
168        Ok(Box::new(empty()))
169    }
170}
171
172impl<'a> Variable<'a> for Empty {
173    fn output(&self) -> Result<Output, DataError> {
174        Ok(Output::empty())
175    }
176    fn typename(&self) -> &'static str {
177        "str"
178    }
179    fn as_bool(&self) -> Result<bool, DataError> {
180        Ok(false)
181    }
182}
183
184impl<'a, 'render> Var<'a, 'render> {
185    /// Embed and owned reference to a value
186    pub fn owned<'x, 'y: 'x, T: Variable<'y>+'y>(x: T) -> Var<'x, 'y>
187        where 'y: 'x, T: 'y
188    {
189        Var(Val::Rc(OwningRef::new(Rc::new(x))
190                .map(|x| x as &Variable).erase_owner()))
191    }
192    /// Embed a static string as a variable
193    ///
194    /// Currently this uses reference counted object that contains pointer,
195    /// but we want to figure out better way to reference static strings
196    pub fn str(x: &'static str) -> Var<'a, 'render> {
197        // This is a limitation of a rust type system
198        Var(Val::Rc(OwningRef::new(Rc::new(x))
199                .map(|x| x as &Variable)
200                .erase_owner()))
201    }
202    /// Create a borrowed reference to the variable
203    pub fn borrow<'x, T: Variable<'render>+'render>(x: &'x T)
204        -> Var<'x, 'render>
205        where 'render: 'x
206    {
207        Var(Val::Ref(x))
208    }
209    /// Create an undefined variable reference
210    pub fn undefined<'x, 'y: 'x>() -> Var<'x, 'y> {
211        Var(Val::Ref(UNDEFINED))
212    }
213    /// Create a variable that contains an empty string
214    pub fn empty<'x, 'y: 'x>() -> Var<'x, 'y> {
215        Var(Val::Ref(EMPTY))
216    }
217    /// Create a variable that boolean true
218    pub fn bool_true<'x, 'y: 'x>() -> Var<'x, 'y> {
219        Var(Val::Ref(EMPTY))
220    }
221    /// Create a variable that boolean false
222    pub fn bool_false<'x, 'y: 'x>() -> Var<'x, 'y> {
223        Var(Val::Ref(EMPTY))
224    }
225}
226
227impl<'r> fmt::Debug for RefVar<'r> {
228    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
229        (*self.0).fmt(f)
230    }
231}
232
233impl<'render> Variable<'render> for RefVar<'render>
234{
235    fn attr<'x>(&'x self,  attr: &str)
236        -> Result<Var<'x, 'render>, DataError>
237        where 'render: 'x
238    {
239        self.0.attr(attr)
240    }
241    fn index<'x>(&'x self,
242        key: &(Variable<'render> + 'render))
243        -> Result<Var<'x, 'render>, DataError>
244        where 'render: 'x
245    {
246        self.0.index(key)
247    }
248    fn output(&self) -> Result<Output, DataError> {
249        self.0.output()
250    }
251    fn typename(&self) -> &'static str {
252        return stringify!(#name);
253    }
254    fn as_str_key<'x>(&'x self)
255        -> Result<&'x str, DataError>
256    {
257        self.0.as_str_key()
258    }
259    fn as_int_key(&self) -> Result<usize, DataError> {
260        self.0.as_int_key()
261    }
262    fn as_bool(&self) -> Result<bool, DataError> {
263        self.0.as_bool()
264    }
265    fn as_number(&self) -> Result<Number, DataError> {
266        self.0.as_number()
267    }
268    fn as_comparable(&self) -> Result<Comparable, DataError> {
269        self.0.as_comparable()
270    }
271
272    fn iterate<'x>(&'x self)
273        -> Result<Box<Iterator<Item=
274            Var<'x, 'render>>+'x>,
275            DataError>
276        where 'render: 'x
277    {
278        self.0.iterate()
279    }
280
281    fn iterate_pairs<'x>(&'x self)
282        -> Result<Box<Iterator<Item=(Var<'x, 'render>, Var<'x, 'render>)>+'x>,
283            DataError>
284        where 'render: 'x
285    {
286        self.0.iterate_pairs()
287    }
288}