gq_core/
query.rs

1use std::collections::HashSet;
2
3use derive_builder::{Builder, UninitializedFieldError};
4use derive_getters::Getters;
5use query_key::{QueryKey, RawKey};
6use thiserror::Error;
7
8pub mod apply;
9mod context;
10pub mod format;
11pub mod query_arguments;
12pub mod query_key;
13
14pub use self::context::OwnedJsonPath;
15use self::query_arguments::QueryArguments;
16
17#[derive(Debug, Error)]
18pub enum Error {
19    #[error("root query builder error: {0}")]
20    RootBuilderError(#[from] RootQueryBuilderError),
21    #[error("child query builder error: {0}")]
22    ChildBuilderError(#[from] ChildQueryBuilderError),
23}
24
25#[derive(Debug, Error)]
26pub enum RootQueryValidationError {
27    // TODO: maybe we shouldnt wrap RawKey between ' '?
28    #[error("root query has children with duplicated output keys: '{0}'")]
29    DuplicatedOutputKeyInRoot(RawKey),
30}
31
32#[derive(Debug, Error)]
33pub enum RootQueryBuilderError {
34    #[error("{0}")]
35    UninitializedFields(#[from] UninitializedFieldError),
36    #[error("{0}")]
37    ValidationError(#[from] RootQueryValidationError),
38}
39
40#[derive(Clone, Getters, Debug, Builder)]
41#[builder(
42    pattern = "owned",
43    build_fn(validate = "Self::validate", error = "RootQueryBuilderError")
44)]
45pub struct Query {
46    #[builder(default)]
47    pub arguments: QueryArguments,
48    #[builder(default)]
49    pub key: QueryKey,
50    #[builder(default)]
51    pub children: Vec<ChildQuery>,
52}
53
54impl QueryBuilder {
55    fn validate(&self) -> Result<(), RootQueryValidationError> {
56        self.validate_children()
57    }
58    fn validate_children(&self) -> Result<(), RootQueryValidationError> {
59        let mut output_keys = HashSet::new();
60        let Some(children) = self.children.as_ref() else {
61            return Ok(());
62        };
63        for child in children {
64            let child_query_key = child.output_key();
65            if !output_keys.insert(child_query_key) {
66                return Err(RootQueryValidationError::DuplicatedOutputKeyInRoot(
67                    child_query_key.clone(),
68                ));
69            }
70        }
71        Ok(())
72    }
73}
74
75#[derive(Debug, Error)]
76pub enum ChildQueryValidationError {
77    // TODO: maybe we shouldnt wrap RawKey between ' '?
78    #[error("query '{0}' has children with duplicated output keys: '{1}'")]
79    DuplicatedOutputKey(String, RawKey),
80}
81
82#[derive(Debug, Error)]
83pub enum ChildQueryBuilderError {
84    #[error("{0}")]
85    UninitializedFields(#[from] UninitializedFieldError),
86    #[error("{0}")]
87    ValidationError(#[from] ChildQueryValidationError),
88}
89
90#[derive(Clone, Getters, Debug, Builder)]
91#[builder(
92    pattern = "owned",
93    build_fn(validate = "Self::validate", error = "ChildQueryBuilderError")
94)]
95pub struct ChildQuery {
96    #[builder(default)]
97    alias: Option<RawKey>,
98    // TODO: those fields should not be pub, they must be validated
99    pub key: QueryKey,
100    #[builder(default)]
101    pub children: Vec<ChildQuery>,
102}
103
104impl ChildQueryBuilder {
105    fn validate(&self) -> Result<(), ChildQueryValidationError> {
106        self.validate_children()
107    }
108    fn validate_children(&self) -> Result<(), ChildQueryValidationError> {
109        let mut output_keys = HashSet::new();
110        let Some(children) = self.children.as_ref() else {
111            return Ok(());
112        };
113        for child in children {
114            let child_output_key = child.output_key();
115            if !output_keys.insert(child_output_key) {
116                let child_key = self.key.as_ref().expect("child key must be defined");
117                return Err(ChildQueryValidationError::DuplicatedOutputKey(
118                    child_key.to_string(),
119                    child_output_key.clone(),
120                ));
121            }
122        }
123        Ok(())
124    }
125}
126
127impl ChildQuery {
128    pub fn output_key(&self) -> &RawKey {
129        self.alias()
130            .as_ref()
131            .unwrap_or_else(|| self.key().last_key().key())
132    }
133}