wirefilter/
execution_context.rs

1use failure::Fail;
2use scheme::{Field, Scheme};
3use types::{GetType, LhsValue, Type};
4
5/// An error that occurs if the type of the value for the field doesn't
6/// match the type specified in the [`Scheme`](struct@Scheme).
7#[derive(Debug, PartialEq, Fail)]
8#[fail(
9    display = "the field should have {:?} type, but {:?} was provided",
10    field_type, value_type
11)]
12pub struct FieldValueTypeMismatchError {
13    /// The type of the field specified in the [`Scheme`](struct@Scheme).
14    pub field_type: Type,
15    /// Provided value type.
16    pub value_type: Type,
17}
18
19/// An execution context stores an associated [`Scheme`](struct@Scheme) and a
20/// set of runtime values to execute [`Filter`](::Filter) against.
21///
22/// It acts as a map in terms of public API, but provides a constant-time
23/// index-based access to values for a filter during execution.
24pub struct ExecutionContext<'e> {
25    scheme: &'e Scheme,
26    values: Box<[Option<LhsValue<'e>>]>,
27}
28
29impl<'e> ExecutionContext<'e> {
30    /// Creates an execution context associated with a given scheme.
31    ///
32    /// This scheme will be used for resolving any field names and indices.
33    pub fn new<'s: 'e>(scheme: &'s Scheme) -> Self {
34        ExecutionContext {
35            scheme,
36            values: vec![None; scheme.get_field_count()].into(),
37        }
38    }
39
40    /// Returns an associated scheme.
41    pub fn scheme(&self) -> &'e Scheme {
42        self.scheme
43    }
44
45    pub(crate) fn get_field_value_unchecked(&self, field: Field<'e>) -> &LhsValue<'e> {
46        // This is safe because this code is reachable only from Filter::execute
47        // which already performs the scheme compatibility check, but check that
48        // invariant holds in the future at least in the debug mode.
49        debug_assert!(self.scheme() == field.scheme());
50
51        // For now we panic in this, but later we are going to align behaviour
52        // with wireshark: resolve all subexpressions that don't have RHS value
53        // to `false`.
54        self.values[field.index()].as_ref().unwrap_or_else(|| {
55            panic!(
56                "Field {} was registered but not given a value",
57                field.name()
58            );
59        })
60    }
61
62    /// Sets a runtime value for a given field name.
63    pub fn set_field_value<'v: 'e, V: Into<LhsValue<'v>>>(
64        &mut self,
65        name: &str,
66        value: V,
67    ) -> Result<(), FieldValueTypeMismatchError> {
68        let field = self.scheme.get_field_index(name).unwrap();
69        let value = value.into();
70
71        let field_type = field.get_type();
72        let value_type = value.get_type();
73
74        if field_type == value_type {
75            self.values[field.index()] = Some(value);
76            Ok(())
77        } else {
78            Err(FieldValueTypeMismatchError {
79                field_type,
80                value_type,
81            })
82        }
83    }
84}
85
86#[test]
87fn test_field_value_type_mismatch() {
88    let scheme = Scheme! { foo: Int };
89
90    let mut ctx = ExecutionContext::new(&scheme);
91
92    assert_eq!(
93        ctx.set_field_value("foo", LhsValue::Bool(false)),
94        Err(FieldValueTypeMismatchError {
95            field_type: Type::Int,
96            value_type: Type::Bool
97        })
98    );
99}