gq_core/query/
context.rs

1use std::{
2    fmt::{self, Display, Formatter},
3    rc::Rc,
4};
5
6use derive_getters::Getters;
7
8use super::query_key::{QueryKey, RawKey};
9
10#[derive(Debug, Clone, Copy)]
11pub enum JsonPathEntry<'a> {
12    // TODO: should we use &str or RawKey?
13    Key(&'a str),
14    Index(usize),
15}
16
17// TODO: use Cow instead of an owned version like the AtomicQueryKey
18#[derive(Debug, Clone)]
19pub enum OwnedJsonPathEntry {
20    Key(String),
21    Index(usize),
22}
23
24impl From<&JsonPathEntry<'_>> for OwnedJsonPathEntry {
25    fn from(entry: &JsonPathEntry) -> Self {
26        match entry {
27            JsonPathEntry::Key(key) => OwnedJsonPathEntry::Key(key.to_string()),
28            JsonPathEntry::Index(index) => OwnedJsonPathEntry::Index(*index),
29        }
30    }
31}
32
33#[derive(Debug, Clone)]
34pub enum JsonPath<'a> {
35    Root,
36    Node {
37        entry: JsonPathEntry<'a>,
38        parent: Rc<JsonPath<'a>>,
39    },
40}
41
42impl<'a> JsonPath<'a> {
43    pub fn push(&self, entry: JsonPathEntry<'a>) -> Self {
44        Self::Node {
45            entry,
46            parent: Rc::new(self.clone()),
47        }
48    }
49}
50
51// TODO: check if this works ok for keys with \n and etc
52impl Display for JsonPath<'_> {
53    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
54        match self {
55            JsonPath::Root => write!(f, "."),
56            JsonPath::Node { entry, parent } => {
57                match **parent {
58                    JsonPath::Root => {}
59                    _ => parent.fmt(f)?,
60                }
61                match entry {
62                    JsonPathEntry::Key(key) => write!(f, ".{key}"),
63                    JsonPathEntry::Index(index) => write!(f, "[{index}]"),
64                }
65            }
66        }
67    }
68}
69
70#[derive(Debug, Clone)]
71pub struct OwnedJsonPath(pub Vec<OwnedJsonPathEntry>);
72
73impl From<&JsonPath<'_>> for OwnedJsonPath {
74    fn from(json_path: &JsonPath) -> Self {
75        let mut key_path = Vec::new();
76        let mut current = json_path;
77        while let JsonPath::Node { entry, parent } = current {
78            key_path.push(OwnedJsonPathEntry::from(entry));
79            current = parent;
80        }
81        key_path.reverse();
82        OwnedJsonPath(key_path)
83    }
84}
85
86// TODO: check if this works ok for keys with \n and etc
87impl Display for OwnedJsonPath {
88    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
89        if self.0.is_empty() {
90            return write!(f, ".");
91        }
92        for entry in &self.0 {
93            match entry {
94                OwnedJsonPathEntry::Key(key) => write!(f, ".{key}")?,
95                OwnedJsonPathEntry::Index(index) => write!(f, "[{index}]")?,
96            }
97        }
98        Ok(())
99    }
100}
101
102#[derive(Debug, Clone, Getters)]
103pub struct ArrayContext<'a> {
104    path: JsonPath<'a>,
105}
106
107#[derive(Debug, Clone, Getters)]
108pub struct Context<'a> {
109    path: JsonPath<'a>,
110    array_context: Option<ArrayContext<'a>>,
111}
112
113impl<'a> Context<'a> {
114    pub fn new() -> Self {
115        Self::default()
116    }
117
118    // TODO: see if &'a is necessary
119    pub fn push_raw_key(&self, raw_key: &'a RawKey) -> Context<'a> {
120        let entry = JsonPathEntry::Key(raw_key.as_str());
121        self.push_entry(entry)
122    }
123
124    pub fn push_index(&self, index: usize) -> Context<'a> {
125        let entry = JsonPathEntry::Index(index);
126        self.push_entry(entry)
127    }
128
129    // TODO: see if &'a is necessary
130    pub fn push_query_key(&self, query_key: &'a QueryKey) -> Context<'a> {
131        let mut path = self.path.clone();
132        // TODO: cleanup here
133        for atomic_query_key in query_key.keys() {
134            path = path.push(JsonPathEntry::Key(atomic_query_key.key().as_str()));
135        }
136        Self {
137            path,
138            ..self.clone()
139        }
140    }
141
142    pub fn enter_array(&self) -> Self {
143        Self {
144            array_context: Some(ArrayContext {
145                path: self.path.clone(),
146            }),
147            ..self.clone()
148        }
149    }
150
151    fn push_entry(&self, entry: JsonPathEntry<'a>) -> Context<'a> {
152        Self {
153            path: self.path.push(entry),
154            ..self.clone()
155        }
156    }
157}
158
159impl Default for Context<'_> {
160    fn default() -> Self {
161        Self {
162            path: JsonPath::Root,
163            array_context: None,
164        }
165    }
166}