fog_pack/
query.rs

1//! Queries for finding Entries.
2//!
3//! A query can be used to find and return [Entries][crate::entry::Entry] that are attached to a
4//! [Document][crate::document::Document]. They are created by providing a single
5//! [`Validator`][crate::validator::Validator] to [`NewQuery::new`]. Queries must be validated by a
6//! [Schema][crate::schema::Schema] before they can be used.
7//!
8
9use std::collections::BTreeMap;
10
11use crate::entry::Entry;
12use crate::validator::Validator;
13use crate::{
14    de::FogDeserializer,
15    element::Parser,
16    error::{Error, Result},
17    ser::FogSerializer,
18    validator::{Checklist, DataChecklist},
19    value_ref::ValueRef,
20    MAX_QUERY_SIZE,
21};
22use fog_crypto::hash::Hash;
23use serde::{Deserialize, Serialize};
24
25#[derive(Clone, Debug, Serialize, Deserialize)]
26#[serde(deny_unknown_fields)]
27struct InnerQuery {
28    key: String,
29    query: Validator,
30}
31
32/// A new Query, ready for encoding.
33///
34/// New queries must first be encoded by a schema, and can then be decoded later by that same
35/// schema into a proper [`Query`][Query].
36///
37/// A Query contains a single validator and a key, which may be used for querying a set of Entries.
38/// Entries that pass the validator can be returned as the query results.
39///
40/// Queries are not meant to be used without associated context; they should be provided alongside
41/// information about what Document they are being used to query.
42#[derive(Clone, Debug)]
43pub struct NewQuery {
44    inner: InnerQuery,
45}
46
47impl NewQuery {
48    /// Create a new query given a validator to run against entries, and the key
49    /// for the entries on a document to check.
50    pub fn new(key: &str, query: Validator) -> Self {
51        Self {
52            inner: InnerQuery {
53                key: key.to_owned(),
54                query,
55            },
56        }
57    }
58
59    /// Get the validator of this query.
60    pub fn validator(&self) -> &Validator {
61        &self.inner.query
62    }
63
64    /// Get the key of the entries this query will be made against.
65    pub fn key(&self) -> &str {
66        &self.inner.key
67    }
68
69    pub(crate) fn complete(self, max_regex: u8) -> Result<Vec<u8>> {
70        fn parse_validator(v: &Validator) -> usize {
71            match v {
72                Validator::Str(val) => val.matches.is_some() as usize,
73                Validator::Map(val) => {
74                    let key_matches = if let Some(s) = val.keys.as_ref() {
75                        s.matches.is_some() as usize
76                    } else {
77                        0
78                    };
79                    key_matches
80                        + val
81                            .req
82                            .values()
83                            .fold(0, |acc, val| acc + parse_validator(val))
84                        + val
85                            .opt
86                            .values()
87                            .fold(0, |acc, val| acc + parse_validator(val))
88                        + val.values.as_ref().map_or(0, |val| parse_validator(val))
89                }
90                Validator::Array(val) => {
91                    val.contains
92                        .iter()
93                        .fold(0, |acc, val| acc + parse_validator(val))
94                        + parse_validator(val.items.as_ref())
95                        + val
96                            .prefix
97                            .iter()
98                            .fold(0, |acc, val| acc + parse_validator(val))
99                }
100                Validator::Hash(val) => val.link.as_ref().map_or(0, |val| parse_validator(val)),
101                Validator::Enum(val) => val
102                    .values()
103                    .fold(0, |acc, val| acc + val.as_ref().map_or(0, parse_validator)),
104                Validator::Multi(val) => val.iter().fold(0, |acc, val| acc + parse_validator(val)),
105                _ => 0,
106            }
107        }
108        let regexes = parse_validator(&self.inner.query);
109        if regexes > (max_regex as usize) {
110            return Err(Error::FailValidate(format!(
111                "Found {} regexes in query, only {} allowed",
112                regexes, max_regex
113            )));
114        }
115        let mut ser = FogSerializer::default();
116        self.inner.serialize(&mut ser)?;
117        let buf = ser.finish();
118        if buf.len() > MAX_QUERY_SIZE {
119            Err(Error::LengthTooLong {
120                max: MAX_QUERY_SIZE,
121                actual: buf.len(),
122            })
123        } else {
124            Ok(buf)
125        }
126    }
127}
128
129/// For querying Entries.
130///
131/// A Query contains a single validator and a key, which may be used for querying a set of Entries.
132/// Entries that pass the validator can be returned as the query results.
133///
134/// Queries are not meant to be used without associated context; they should be provided alongside
135/// information about what Document they are being used to query.
136#[derive(Clone, Debug)]
137pub struct Query {
138    inner: InnerQuery,
139    schema: Hash,
140    types: BTreeMap<String, Validator>,
141}
142
143impl Query {
144    pub(crate) fn new(buf: Vec<u8>, max_regex: u8) -> Result<Self> {
145        // Check to see how many regexes are in the validator
146        let mut de = FogDeserializer::new(&buf);
147        let regex_check = ValueRef::deserialize(&mut de)?;
148        let regexes = crate::count_regexes(&regex_check["query"]);
149        if regexes > (max_regex as usize) {
150            return Err(Error::FailValidate(format!(
151                "Found {} regexes in query, only {} allowed",
152                regexes, max_regex
153            )));
154        }
155
156        // Parse into an actual validator
157        let mut de = FogDeserializer::new(&buf);
158        let inner = InnerQuery::deserialize(&mut de)?;
159        Ok(Self {
160            inner,
161            schema: Hash::new([]),
162            types: BTreeMap::new(),
163        })
164    }
165
166    /// Get the validator of this query.
167    pub fn validator(&self) -> &Validator {
168        &self.inner.query
169    }
170
171    /// Get the key of the entries this query will be made against.
172    pub fn key(&self) -> &str {
173        &self.inner.key
174    }
175
176    /// Execute the query against a given entry and see if it potentially matches.
177    ///
178    /// The [`DataChecklist`] must be completed in order to fully determine if
179    /// the entry matches. If the checklist completes successfully, the entry is
180    /// a match for the query.
181    pub fn query(&self, entry: &Entry) -> Result<DataChecklist<()>> {
182        let parser = Parser::new(entry.data());
183        let checklist = Some(Checklist::new(&self.schema, &self.types));
184        let (_, checklist) = self.inner.query.validate(&self.types, parser, checklist)?;
185        Ok(DataChecklist::from_checklist(checklist.unwrap(), ()))
186    }
187}
188
189#[cfg(test)]
190mod test {
191    use regex::Regex;
192
193    use crate::validator::{MapValidator, StrValidator};
194
195    use super::*;
196
197    #[test]
198    fn max_regex_in_key() {
199        let validator = MapValidator {
200            keys: Some(Box::new(StrValidator {
201                matches: Some(Box::new(Regex::new("[a-z]").unwrap())),
202                ..Default::default()
203            })),
204            ..Default::default()
205        }
206        .build();
207
208        NewQuery::new("test", validator.clone())
209            .complete(0)
210            .unwrap_err();
211        let enc_query = NewQuery::new("test", validator).complete(1).unwrap();
212        assert!(Query::new(enc_query.clone(), 0).is_err());
213        assert!(Query::new(enc_query.clone(), 1).is_ok());
214        assert!(Query::new(enc_query, 2).is_ok());
215    }
216
217    #[test]
218    fn max_regex_in_str() {
219        let matches = Some(Box::new(Regex::new("[a-z]").unwrap()));
220        let validator = StrValidator {
221            matches,
222            ..Default::default()
223        }
224        .build();
225        NewQuery::new("test", validator.clone())
226            .complete(0)
227            .unwrap_err();
228        let enc_query = NewQuery::new("test", validator).complete(1).unwrap();
229        assert!(Query::new(enc_query.clone(), 0).is_err());
230        assert!(Query::new(enc_query.clone(), 1).is_ok());
231        assert!(Query::new(enc_query, 2).is_ok());
232    }
233}