indradb/models/
queries.rs

1use std::str::FromStr;
2
3use crate::{errors, Edge, Identifier, Json};
4
5use uuid::Uuid;
6
7macro_rules! into_query {
8    ($name:ident, $variant:ident) => {
9        // we don't want to impl From since the reverse operation isn't allowed
10        #[allow(clippy::from_over_into)]
11        impl Into<Query> for $name {
12            fn into(self) -> Query {
13                Query::$variant(self)
14            }
15        }
16    };
17}
18
19macro_rules! nestable_query {
20    ($name:ident, $variant:ident) => {
21        impl QueryExt for $name {}
22        impl CountQueryExt for $name {}
23        into_query!($name, $variant);
24    };
25}
26
27/// Specifies what kind of items should be piped from one type of query to
28/// another.
29///
30/// Edge and vertex queries can build off of one another via pipes - e.g. you
31/// can get the outbound edges of a set of vertices by piping from a vertex
32/// query to an edge query. `EdgeDirection`s are used to specify which
33/// end of things you want to pipe - either the outbound items or the inbound
34/// items.
35#[derive(Eq, PartialEq, Clone, Debug, Hash, Copy)]
36pub enum EdgeDirection {
37    /// Outbound direction.
38    Outbound,
39    /// Inbound direction.
40    Inbound,
41}
42
43impl FromStr for EdgeDirection {
44    type Err = errors::ValidationError;
45
46    fn from_str(s: &str) -> Result<EdgeDirection, Self::Err> {
47        match s {
48            "outbound" => Ok(EdgeDirection::Outbound),
49            "inbound" => Ok(EdgeDirection::Inbound),
50            _ => Err(errors::ValidationError::InvalidValue),
51        }
52    }
53}
54
55impl From<EdgeDirection> for String {
56    fn from(d: EdgeDirection) -> Self {
57        match d {
58            EdgeDirection::Outbound => "outbound".to_string(),
59            EdgeDirection::Inbound => "inbound".to_string(),
60        }
61    }
62}
63
64/// A query to get a set of values from the database.
65#[derive(Eq, PartialEq, Clone, Debug)]
66pub enum Query {
67    /// Gets all vertices.
68    AllVertex,
69    /// Gets a range of vertices.
70    RangeVertex(RangeVertexQuery),
71    /// Gets a specific set of vertices.
72    SpecificVertex(SpecificVertexQuery),
73    /// Gets vertices with or without a given property.
74    VertexWithPropertyPresence(VertexWithPropertyPresenceQuery),
75    /// Gets vertices with a property equal to a given value.
76    VertexWithPropertyValue(VertexWithPropertyValueQuery),
77
78    /// Gets all edges.
79    AllEdge,
80    /// Gets a specific set of edges.
81    SpecificEdge(SpecificEdgeQuery),
82    /// Gets edges with or without a given property.
83    EdgeWithPropertyPresence(EdgeWithPropertyPresenceQuery),
84    /// Gets edges with a property equal to a given value.
85    EdgeWithPropertyValue(EdgeWithPropertyValueQuery),
86
87    /// Gets the vertices associated with edges, or edges associated with
88    /// vertices.
89    Pipe(PipeQuery),
90    /// Returns the properties associated with a vertex or edge.
91    PipeProperty(PipePropertyQuery),
92    /// Gets vertices or edges with or without a property.
93    PipeWithPropertyPresence(PipeWithPropertyPresenceQuery),
94    /// Gets vertices or edges with a property equal to a given value.
95    PipeWithPropertyValue(PipeWithPropertyValueQuery),
96
97    /// Includes the results of a query in output.
98    Include(IncludeQuery),
99    /// Counts the number of items returned from a query.
100    Count(CountQuery),
101}
102
103impl Query {
104    /// Determines the number of output values the query will produce without
105    /// running it, so we can allocate a `Vec` with the correct capacity
106    /// ahead-of-time.
107    pub(crate) fn output_len(&self) -> usize {
108        match self {
109            Query::AllVertex
110            | Query::RangeVertex(_)
111            | Query::SpecificVertex(_)
112            | Query::VertexWithPropertyPresence(_)
113            | Query::VertexWithPropertyValue(_)
114            | Query::AllEdge
115            | Query::SpecificEdge(_)
116            | Query::EdgeWithPropertyPresence(_)
117            | Query::EdgeWithPropertyValue(_)
118            | Query::Count(_) => 1,
119            Query::Pipe(q) => q.inner.output_len(),
120            Query::PipeProperty(q) => q.inner.output_len(),
121            Query::PipeWithPropertyPresence(q) => q.inner.output_len(),
122            Query::PipeWithPropertyValue(q) => q.inner.output_len(),
123            Query::Include(q) => 1 + q.inner.output_len(),
124        }
125    }
126
127    /// Determines the `QueryOutputValue` variant that will be produced
128    /// without running the query, which can help validate the query
129    /// ahead-of-time.
130    pub(crate) fn output_type(&self) -> errors::ValidationResult<QueryOutputValue> {
131        match self {
132            Query::AllVertex
133            | Query::RangeVertex(_)
134            | Query::SpecificVertex(_)
135            | Query::VertexWithPropertyPresence(_)
136            | Query::VertexWithPropertyValue(_) => Ok(QueryOutputValue::Vertices(Vec::default())),
137            Query::AllEdge
138            | Query::SpecificEdge(_)
139            | Query::EdgeWithPropertyPresence(_)
140            | Query::EdgeWithPropertyValue(_) => Ok(QueryOutputValue::Edges(Vec::default())),
141            Query::Count(_) => Ok(QueryOutputValue::Count(0)),
142            Query::Pipe(q) => q.inner.output_type(),
143            Query::PipeProperty(q) => match q.inner.output_type()? {
144                QueryOutputValue::Vertices(_) => Ok(QueryOutputValue::VertexProperties(Vec::default())),
145                QueryOutputValue::Edges(_) => Ok(QueryOutputValue::EdgeProperties(Vec::default())),
146                _ => Err(errors::ValidationError::InnerQuery),
147            },
148            Query::PipeWithPropertyPresence(q) => q.inner.output_type(),
149            Query::PipeWithPropertyValue(q) => q.inner.output_type(),
150            Query::Include(q) => q.inner.output_type(),
151        }
152    }
153}
154
155/// Extension trait containing common functions for all query structs.
156pub trait QueryExt: Into<Query> {
157    /// Gets the outbound vertices or edges associated with this query.
158    fn outbound(self) -> errors::ValidationResult<PipeQuery> {
159        PipeQuery::new(Box::new(self.into()), EdgeDirection::Outbound)
160    }
161
162    /// Gets the inbound vertices or edges associated with this query.
163    fn inbound(self) -> errors::ValidationResult<PipeQuery> {
164        PipeQuery::new(Box::new(self.into()), EdgeDirection::Inbound)
165    }
166
167    /// Gets values with a property.
168    ///
169    /// # Arguments
170    /// * `name`: The name of the property.
171    fn with_property<T: Into<Identifier>>(self, name: T) -> errors::ValidationResult<PipeWithPropertyPresenceQuery> {
172        PipeWithPropertyPresenceQuery::new(Box::new(self.into()), name, true)
173    }
174
175    /// Gets values without a property.
176    ///
177    /// # Arguments
178    /// * `name`: The name of the property.
179    fn without_property<T: Into<Identifier>>(self, name: T) -> errors::ValidationResult<PipeWithPropertyPresenceQuery> {
180        PipeWithPropertyPresenceQuery::new(Box::new(self.into()), name, false)
181    }
182
183    /// Gets values with a property equal to a given value.
184    ///
185    /// # Arguments
186    /// * `name`: The name of the property.
187    /// * `value`: The value of the property.
188    fn with_property_equal_to<T: Into<Identifier>>(
189        self,
190        name: T,
191        value: Json,
192    ) -> errors::ValidationResult<PipeWithPropertyValueQuery> {
193        PipeWithPropertyValueQuery::new(Box::new(self.into()), name, value, true)
194    }
195
196    /// Gets values with a property not equal to a given value.
197    ///
198    /// # Arguments
199    /// * `name`: The name of the property.
200    /// * `value`: The value of the property.
201    fn with_property_not_equal_to<T: Into<Identifier>>(
202        self,
203        name: T,
204        value: Json,
205    ) -> errors::ValidationResult<PipeWithPropertyValueQuery> {
206        PipeWithPropertyValueQuery::new(Box::new(self.into()), name, value, false)
207    }
208
209    /// Gets the properties associated with the query results.
210    fn properties(self) -> errors::ValidationResult<PipePropertyQuery> {
211        PipePropertyQuery::new(Box::new(self.into()))
212    }
213
214    /// Include this query's output, even if it is an intermediate result.
215    fn include(self) -> IncludeQuery {
216        IncludeQuery::new(Box::new(self.into()))
217    }
218}
219
220pub trait CountQueryExt: Into<Query> {
221    /// Gets the count from this query.
222    fn count(self) -> errors::ValidationResult<CountQuery> {
223        CountQuery::new(Box::new(self.into()))
224    }
225}
226
227/// Gets all vertices.
228#[derive(Eq, PartialEq, Clone, Debug)]
229pub struct AllVertexQuery;
230
231impl QueryExt for AllVertexQuery {}
232impl CountQueryExt for AllVertexQuery {}
233
234// we don't want to impl From since the reverse operation isn't allowed
235#[allow(clippy::from_over_into)]
236impl Into<Query> for AllVertexQuery {
237    fn into(self) -> Query {
238        Query::AllVertex
239    }
240}
241
242/// Gets a range of vertices.
243#[derive(Eq, PartialEq, Clone, Debug)]
244pub struct RangeVertexQuery {
245    /// Limits the number of vertices to get.
246    pub limit: u32,
247
248    /// Filters the type of vertices returned.
249    pub t: Option<Identifier>,
250
251    /// Sets the lowest vertex ID to return.
252    pub start_id: Option<Uuid>,
253}
254
255nestable_query!(RangeVertexQuery, RangeVertex);
256
257impl Default for RangeVertexQuery {
258    fn default() -> Self {
259        Self::new()
260    }
261}
262
263impl RangeVertexQuery {
264    /// Creates a new vertex range query.
265    pub fn new() -> Self {
266        Self {
267            limit: u32::MAX,
268            t: None,
269            start_id: None,
270        }
271    }
272
273    /// Sets the limit.
274    ///
275    /// # Arguments
276    /// * `limit`: Limits the number of returned results.
277    pub fn limit(self, limit: u32) -> Self {
278        Self {
279            limit,
280            t: self.t,
281            start_id: self.start_id,
282        }
283    }
284
285    /// Filter the type of vertices returned.
286    ///
287    /// # Arguments
288    /// * `t`: Sets the type filter.
289    pub fn t(self, t: Identifier) -> Self {
290        Self {
291            limit: self.limit,
292            t: Some(t),
293            start_id: self.start_id,
294        }
295    }
296
297    /// Sets the lowest vertex ID to return.
298    ///
299    /// # Arguments
300    /// * `start_id`: The lowest vertex ID to return.
301    pub fn start_id(self, start_id: Uuid) -> Self {
302        Self {
303            limit: self.limit,
304            t: self.t,
305            start_id: Some(start_id),
306        }
307    }
308}
309
310/// Gets a specific set of vertices.
311#[derive(Eq, PartialEq, Clone, Debug)]
312pub struct SpecificVertexQuery {
313    /// The IDs of the vertices to get.
314    pub ids: Vec<Uuid>,
315}
316
317nestable_query!(SpecificVertexQuery, SpecificVertex);
318
319impl SpecificVertexQuery {
320    /// Creates a new vertex query for getting a list of vertices by their
321    /// IDs.
322    ///
323    /// Arguments
324    /// * `ids`: The IDs of the vertices to get.
325    pub fn new(ids: Vec<Uuid>) -> Self {
326        Self { ids }
327    }
328
329    /// Creates a new vertex query for getting a single vertex.
330    ///
331    /// Arguments
332    /// * `id`: The ID of the vertex to get.
333    pub fn single(id: Uuid) -> Self {
334        Self { ids: vec![id] }
335    }
336}
337
338/// Gets vertices with or without a given property.
339#[derive(Eq, PartialEq, Clone, Debug)]
340pub struct VertexWithPropertyPresenceQuery {
341    /// The name of the property.
342    pub name: Identifier,
343}
344
345nestable_query!(VertexWithPropertyPresenceQuery, VertexWithPropertyPresence);
346
347impl VertexWithPropertyPresenceQuery {
348    /// Creates a new vertex with property presence query.
349    ///
350    /// # Arguments
351    /// * `name`: The property name.
352    pub fn new<T: Into<Identifier>>(name: T) -> Self {
353        Self { name: name.into() }
354    }
355}
356
357/// Gets vertices with a property equal to a given value.
358#[derive(Eq, PartialEq, Clone, Debug)]
359pub struct VertexWithPropertyValueQuery {
360    /// The name of the property.
361    pub name: Identifier,
362    /// The value of the property.
363    pub value: Json,
364}
365
366nestable_query!(VertexWithPropertyValueQuery, VertexWithPropertyValue);
367
368impl VertexWithPropertyValueQuery {
369    /// Creates a new vertex with property value query.
370    ///
371    /// # Arguments
372    /// * `name`: The property name.
373    /// * `value`: The property value.
374    pub fn new<T: Into<Identifier>>(name: T, value: Json) -> Self {
375        Self {
376            name: name.into(),
377            value,
378        }
379    }
380}
381
382/// Gets all edges.
383#[derive(Eq, PartialEq, Clone, Debug)]
384pub struct AllEdgeQuery;
385
386impl QueryExt for AllEdgeQuery {}
387impl CountQueryExt for AllEdgeQuery {}
388
389// we don't want to impl From since the reverse operation isn't allowed
390#[allow(clippy::from_over_into)]
391impl Into<Query> for AllEdgeQuery {
392    fn into(self) -> Query {
393        Query::AllEdge
394    }
395}
396
397/// Gets a specific set of edges.
398#[derive(Eq, PartialEq, Clone, Debug)]
399pub struct SpecificEdgeQuery {
400    /// The edges to get.
401    pub edges: Vec<Edge>,
402}
403
404nestable_query!(SpecificEdgeQuery, SpecificEdge);
405
406impl SpecificEdgeQuery {
407    /// Creates a new edge query for getting a list of specific edges.
408    ///
409    /// Arguments
410    /// * `edges`: The edges to get.
411    pub fn new(edges: Vec<Edge>) -> Self {
412        Self { edges }
413    }
414
415    /// Creates a new edge query for getting a single edge.
416    ///
417    /// Arguments
418    /// * `edge`: The edge to get.
419    pub fn single(edge: Edge) -> Self {
420        Self { edges: vec![edge] }
421    }
422}
423
424/// Gets edges with or without a given property.
425#[derive(Eq, PartialEq, Clone, Debug)]
426pub struct EdgeWithPropertyPresenceQuery {
427    /// The name of the property.
428    pub name: Identifier,
429}
430
431nestable_query!(EdgeWithPropertyPresenceQuery, EdgeWithPropertyPresence);
432
433impl EdgeWithPropertyPresenceQuery {
434    /// Creates a new edge with property presence query.
435    ///
436    /// # Arguments
437    /// * `name`: The property name.
438    pub fn new<T: Into<Identifier>>(name: T) -> Self {
439        Self { name: name.into() }
440    }
441}
442
443/// Gets edges with a property equal to a given value.
444#[derive(Eq, PartialEq, Clone, Debug)]
445pub struct EdgeWithPropertyValueQuery {
446    /// The name of the property.
447    pub name: Identifier,
448    /// The value of the property.
449    pub value: Json,
450}
451
452nestable_query!(EdgeWithPropertyValueQuery, EdgeWithPropertyValue);
453
454impl EdgeWithPropertyValueQuery {
455    /// Creates a new edge with property value query.
456    ///
457    /// # Arguments
458    /// * `name`: The property name.
459    /// * `value`: The property value.
460    pub fn new<T: Into<Identifier>>(name: T, value: Json) -> Self {
461        Self {
462            name: name.into(),
463            value,
464        }
465    }
466}
467
468/// Gets the vertices associated with edges, or edges associated with
469/// vertices.
470///
471/// Generally, you shouldn't need to construct this directly, but rather call
472/// `.outbound()` or `.inbound()`.
473#[derive(Eq, PartialEq, Clone, Debug)]
474pub struct PipeQuery {
475    /// The edge query to build off of.
476    pub inner: Box<Query>,
477
478    /// Whether to get outbound or inbound values.
479    pub direction: EdgeDirection,
480
481    /// Limits the number of values to get.
482    pub limit: u32,
483
484    /// Filters the type of values returned.
485    pub t: Option<Identifier>,
486}
487
488nestable_query!(PipeQuery, Pipe);
489
490impl PipeQuery {
491    /// Constructs a new pipe query.
492    ///
493    /// # Arguments
494    /// * `inner`: The inner query.
495    /// * `direction`: Which direction to pipe from.
496    pub fn new(inner: Box<Query>, direction: EdgeDirection) -> errors::ValidationResult<Self> {
497        match inner.output_type()? {
498            QueryOutputValue::Vertices(_) | QueryOutputValue::Edges(_) => {}
499            _ => return Err(errors::ValidationError::InnerQuery),
500        }
501
502        Ok(Self {
503            inner,
504            direction,
505            limit: u32::MAX,
506            t: None,
507        })
508    }
509
510    /// Sets the limit.
511    ///
512    /// # Arguments
513    /// * `limit`: Limits the number of returned results.
514    pub fn limit(self, limit: u32) -> Self {
515        Self {
516            inner: self.inner,
517            direction: self.direction,
518            limit,
519            t: self.t,
520        }
521    }
522
523    /// Filter the type of values returned.
524    ///
525    /// # Arguments
526    /// * `t`: Sets the type filter.
527    pub fn t(self, t: Identifier) -> Self {
528        Self {
529            inner: self.inner,
530            direction: self.direction,
531            limit: self.limit,
532            t: Some(t),
533        }
534    }
535}
536
537/// Returns the properties associated with a vertex or edge.
538#[derive(Eq, PartialEq, Clone, Debug)]
539pub struct PipePropertyQuery {
540    /// The inner query.
541    pub inner: Box<Query>,
542    /// The property name to get. If `None`, all properties will be fetched.
543    pub name: Option<Identifier>,
544}
545
546into_query!(PipePropertyQuery, PipeProperty);
547impl CountQueryExt for PipePropertyQuery {}
548
549impl PipePropertyQuery {
550    /// Creates a new pipe property query.
551    ///
552    /// # Arguments
553    /// * `inner`: The query to pipe.
554    pub fn new(inner: Box<Query>) -> errors::ValidationResult<Self> {
555        match inner.output_type()? {
556            QueryOutputValue::Vertices(_) | QueryOutputValue::Edges(_) => {}
557            _ => return Err(errors::ValidationError::InnerQuery),
558        }
559        Ok(Self { inner, name: None })
560    }
561
562    /// Only include properties with a given name.
563    ///
564    /// # Arguments
565    /// * `name`: The name filter.
566    pub fn name(self, name: Identifier) -> Self {
567        Self {
568            inner: self.inner,
569            name: Some(name),
570        }
571    }
572}
573
574/// Gets vertices or edges with or without a property.
575#[derive(Eq, PartialEq, Clone, Debug)]
576pub struct PipeWithPropertyPresenceQuery {
577    /// The query to filter.
578    pub inner: Box<Query>,
579    /// The name of the property.
580    pub name: Identifier,
581    /// Whether we should look for property presence or lack thereof.
582    pub exists: bool,
583}
584
585nestable_query!(PipeWithPropertyPresenceQuery, PipeWithPropertyPresence);
586
587impl PipeWithPropertyPresenceQuery {
588    /// Gets vertices with a property.
589    ///
590    /// Arguments
591    /// * `inner`: The query to filter.
592    /// * `name`: The name of the property.
593    /// * `exists`: Whether we should look for property presence or lack thereof.
594    pub fn new<T: Into<Identifier>>(inner: Box<Query>, name: T, exists: bool) -> errors::ValidationResult<Self> {
595        match inner.output_type()? {
596            QueryOutputValue::Vertices(_) | QueryOutputValue::Edges(_) => {}
597            _ => return Err(errors::ValidationError::InnerQuery),
598        }
599        Ok(Self {
600            inner,
601            name: name.into(),
602            exists,
603        })
604    }
605}
606
607/// Gets vertices or edges with a property equal to a given value.
608#[derive(Eq, PartialEq, Clone, Debug)]
609pub struct PipeWithPropertyValueQuery {
610    /// The query to filter.
611    pub inner: Box<Query>,
612    /// The name of the property.
613    pub name: Identifier,
614    /// The value of the property.
615    pub value: Json,
616    /// Whether we should look for property equality or non-equality.
617    pub equal: bool,
618}
619
620nestable_query!(PipeWithPropertyValueQuery, PipeWithPropertyValue);
621
622impl PipeWithPropertyValueQuery {
623    /// Constructs a new pipe with property value query.
624    ///
625    /// # Arguments
626    /// * `inner`: The inner query.
627    /// * `name`: The property name to filter.
628    /// * `value`: The property value to filter.
629    /// * `equal`: Whether the value should be equal, or not equal.
630    pub fn new<T: Into<Identifier>>(
631        inner: Box<Query>,
632        name: T,
633        value: Json,
634        equal: bool,
635    ) -> errors::ValidationResult<Self> {
636        match inner.output_type()? {
637            QueryOutputValue::Vertices(_) | QueryOutputValue::Edges(_) => {}
638            _ => return Err(errors::ValidationError::InnerQuery),
639        }
640        Ok(Self {
641            inner,
642            name: name.into(),
643            value,
644            equal,
645        })
646    }
647}
648
649/// Includes the results of a query in output.
650///
651/// The outermost part of a query will always be explicitly included. This
652/// allows you to also output an intermediate result.
653///
654/// # Examples
655/// ```
656/// use indradb::{AllVertexQuery, QueryExt};
657/// // A query to return all edges in the database, which are implicitly
658/// // included as the outermost results.
659/// let q = AllVertexQuery.outbound();
660/// // A query to return all vertices and all edges in the database, because
661/// // vertices are explicitly included as intermediate results.
662/// let q = AllVertexQuery.include().outbound();
663/// ```
664#[derive(Eq, PartialEq, Clone, Debug)]
665pub struct IncludeQuery {
666    /// The query to export.
667    pub inner: Box<Query>,
668}
669
670nestable_query!(IncludeQuery, Include);
671
672impl IncludeQuery {
673    /// Marks a query as exported.
674    ///
675    /// Arguments
676    /// * `inner`: The query to export.
677    pub fn new(inner: Box<Query>) -> Self {
678        Self { inner }
679    }
680}
681
682/// Counts the number of items returned from a query.
683///
684/// # Examples
685/// ```
686/// use indradb::{AllVertexQuery, CountQueryExt};
687/// // A query to return the total number of vertices in the database.
688/// let q = AllVertexQuery.count();
689/// ```
690#[derive(Eq, PartialEq, Clone, Debug)]
691pub struct CountQuery {
692    /// The query to export.
693    pub inner: Box<Query>,
694}
695
696into_query!(CountQuery, Count);
697
698impl CountQuery {
699    /// Marks a query as exported.
700    ///
701    /// Arguments
702    /// * `inner`: The query to export.
703    pub fn new(inner: Box<Query>) -> errors::ValidationResult<Self> {
704        match inner.output_type()? {
705            QueryOutputValue::Vertices(_)
706            | QueryOutputValue::Edges(_)
707            | QueryOutputValue::VertexProperties(_)
708            | QueryOutputValue::EdgeProperties(_) => {}
709            _ => return Err(errors::ValidationError::InnerQuery),
710        }
711        Ok(Self { inner })
712    }
713}
714
715/// Value(s) returned from a query.
716#[derive(Clone, Debug, PartialEq)]
717pub enum QueryOutputValue {
718    /// Vertices.
719    Vertices(Vec<crate::Vertex>),
720    /// Edges.
721    Edges(Vec<crate::Edge>),
722    /// A Count.
723    Count(u64),
724    /// Vertex properties.
725    VertexProperties(Vec<crate::VertexProperties>),
726    /// Edge properties.
727    EdgeProperties(Vec<crate::EdgeProperties>),
728}
729
730#[cfg(test)]
731mod tests {
732    use crate::{
733        ijson, AllVertexQuery, CountQuery, CountQueryExt, EdgeDirection, Identifier, PipePropertyQuery, PipeQuery,
734        PipeWithPropertyPresenceQuery, PipeWithPropertyValueQuery, Query, ValidationError,
735    };
736    use std::str::FromStr;
737
738    fn expect_inner_query_err<T: core::fmt::Debug>(result: Result<T, ValidationError>) {
739        match result {
740            Err(ValidationError::InnerQuery) => (),
741            _ => assert!(false, "unexpected result: {:?}", result),
742        }
743    }
744
745    #[test]
746    fn should_convert_str_to_edge_direction() {
747        assert_eq!(EdgeDirection::from_str("outbound").unwrap(), EdgeDirection::Outbound);
748        assert_eq!(EdgeDirection::from_str("inbound").unwrap(), EdgeDirection::Inbound);
749        assert!(EdgeDirection::from_str("foo").is_err());
750    }
751
752    #[test]
753    fn should_convert_edge_direction_to_string() {
754        let s: String = EdgeDirection::Outbound.into();
755        assert_eq!(s, "outbound".to_string());
756        let s: String = EdgeDirection::Inbound.into();
757        assert_eq!(s, "inbound".to_string());
758    }
759
760    #[test]
761    fn should_fail_for_nested_count_queries() {
762        let q: Query = AllVertexQuery.count().unwrap().into();
763        expect_inner_query_err(CountQuery::new(Box::new(q.clone())));
764        expect_inner_query_err(PipeQuery::new(Box::new(q.clone()), EdgeDirection::Outbound));
765        expect_inner_query_err(PipePropertyQuery::new(Box::new(q.clone())));
766        expect_inner_query_err(PipeWithPropertyPresenceQuery::new(
767            Box::new(q.clone()),
768            Identifier::new("foo").unwrap(),
769            true,
770        ));
771        expect_inner_query_err(PipeWithPropertyValueQuery::new(
772            Box::new(q.clone()),
773            Identifier::new("foo").unwrap(),
774            ijson!("bar"),
775            true,
776        ));
777    }
778}