helix_dsl/
query_generator.rs1use serde::{Deserialize, Serialize};
2use std::collections::BTreeMap;
3use std::path::{Path, PathBuf};
4
5pub const QUERY_BUNDLE_VERSION: u32 = 3;
7
8#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
10pub enum QueryParamType {
11 Bool,
13 I64,
15 F64,
17 F32,
19 String,
21 Bytes,
23 Value,
25 Object,
27 Array(Box<QueryParamType>),
29}
30
31#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
33pub struct QueryParameter {
34 pub name: String,
36 pub ty: QueryParamType,
38}
39
40#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
42pub struct QueryBundle {
43 pub version: u32,
45 pub read_routes: BTreeMap<String, crate::ReadBatch>,
47 pub write_routes: BTreeMap<String, crate::WriteBatch>,
49 pub read_parameters: BTreeMap<String, Vec<QueryParameter>>,
51 pub write_parameters: BTreeMap<String, Vec<QueryParameter>>,
53}
54
55impl Default for QueryBundle {
56 fn default() -> Self {
57 Self {
58 version: QUERY_BUNDLE_VERSION,
59 read_routes: BTreeMap::new(),
60 write_routes: BTreeMap::new(),
61 read_parameters: BTreeMap::new(),
62 write_parameters: BTreeMap::new(),
63 }
64 }
65}
66
67pub struct RegisteredReadQuery {
69 pub name: &'static str,
71 pub build: fn() -> crate::ReadBatch,
73 pub parameters: fn() -> Vec<QueryParameter>,
75}
76
77pub struct RegisteredWriteQuery {
79 pub name: &'static str,
81 pub build: fn() -> crate::WriteBatch,
83 pub parameters: fn() -> Vec<QueryParameter>,
85}
86
87inventory::collect!(RegisteredReadQuery);
88inventory::collect!(RegisteredWriteQuery);
89
90#[derive(Debug)]
92pub enum GenerateError {
93 DuplicateQueryName(String),
95 Io(std::io::Error),
97 Json(sonic_rs::Error),
99 UnsupportedVersion {
101 found: u32,
103 expected: u32,
105 },
106}
107
108impl std::fmt::Display for GenerateError {
109 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110 match self {
111 Self::DuplicateQueryName(name) => {
112 write!(f, "duplicate generated query name: {name}")
113 }
114 Self::Io(err) => write!(f, "io error: {err}"),
115 Self::Json(err) => write!(f, "json error: {err}"),
116 Self::UnsupportedVersion { found, expected } => {
117 write!(
118 f,
119 "unsupported query bundle version {found} (expected {expected})"
120 )
121 }
122 }
123 }
124}
125
126impl std::error::Error for GenerateError {}
127
128impl From<std::io::Error> for GenerateError {
129 fn from(value: std::io::Error) -> Self {
130 Self::Io(value)
131 }
132}
133
134impl From<sonic_rs::Error> for GenerateError {
135 fn from(value: sonic_rs::Error) -> Self {
136 Self::Json(value)
137 }
138}
139
140pub fn build_query_bundle() -> Result<QueryBundle, GenerateError> {
142 let mut bundle = QueryBundle::default();
143
144 for registered in inventory::iter::<RegisteredReadQuery> {
145 if bundle.read_routes.contains_key(registered.name)
146 || bundle.write_routes.contains_key(registered.name)
147 {
148 return Err(GenerateError::DuplicateQueryName(
149 registered.name.to_string(),
150 ));
151 }
152
153 bundle
154 .read_routes
155 .insert(registered.name.to_string(), (registered.build)());
156 bundle
157 .read_parameters
158 .insert(registered.name.to_string(), (registered.parameters)());
159 }
160
161 for registered in inventory::iter::<RegisteredWriteQuery> {
162 if bundle.read_routes.contains_key(registered.name)
163 || bundle.write_routes.contains_key(registered.name)
164 {
165 return Err(GenerateError::DuplicateQueryName(
166 registered.name.to_string(),
167 ));
168 }
169
170 bundle
171 .write_routes
172 .insert(registered.name.to_string(), (registered.build)());
173 bundle
174 .write_parameters
175 .insert(registered.name.to_string(), (registered.parameters)());
176 }
177
178 Ok(bundle)
179}
180
181pub fn serialize_query_bundle(bundle: &QueryBundle) -> Result<Vec<u8>, GenerateError> {
183 Ok(sonic_rs::to_vec_pretty(bundle)?)
184}
185
186pub fn deserialize_query_bundle(bytes: &[u8]) -> Result<QueryBundle, GenerateError> {
188 let bundle: QueryBundle = sonic_rs::from_slice(bytes)?;
189
190 if bundle.version != QUERY_BUNDLE_VERSION {
191 return Err(GenerateError::UnsupportedVersion {
192 found: bundle.version,
193 expected: QUERY_BUNDLE_VERSION,
194 });
195 }
196
197 Ok(bundle)
198}
199
200pub fn write_query_bundle_to_path<P: AsRef<Path>>(
202 bundle: &QueryBundle,
203 path: P,
204) -> Result<(), GenerateError> {
205 let bytes = serialize_query_bundle(bundle)?;
206 std::fs::write(path, bytes)?;
207 Ok(())
208}
209
210pub fn read_query_bundle_from_path<P: AsRef<Path>>(path: P) -> Result<QueryBundle, GenerateError> {
212 let bytes = std::fs::read(path)?;
213 deserialize_query_bundle(&bytes)
214}
215
216pub fn generate() -> Result<PathBuf, GenerateError> {
218 generate_to_path("queries.json")
219}
220
221pub fn generate_to_path<P: AsRef<Path>>(path: P) -> Result<PathBuf, GenerateError> {
223 let path = path.as_ref();
224 let bundle = build_query_bundle()?;
225 write_query_bundle_to_path(&bundle, path)?;
226 Ok(path.to_path_buf())
227}