1#![doc = include_str!("../README.md")]
2#![warn(clippy::all, clippy::pedantic)]
3#![allow(clippy::missing_errors_doc, clippy::cast_sign_loss)]
4
5mod ffi;
10
11use anyhow::bail;
12use bytes::Bytes;
13use flexbuffers::VectorReader;
14use hashbrown::HashMap;
15use http::{HeaderName, HeaderValue, StatusCode};
16use parking_lot::RwLock;
17use std::collections::BTreeMap;
18use std::str::FromStr;
19use std::sync::Arc;
20use std::time::Duration;
21use tracing::{Instrument, info_span, instrument};
22
23use ordinary_auth::Auth;
24use ordinary_config::{
25 ContentDefinition, ModelConfig, OrdinaryConfig, QueryExpression, StoredCachePolicy,
26 TemplateConfig, TemplateFfiVersion, TemplateRef, TemplateRefField, TemplateRefFieldBind,
27};
28use ordinary_storage::{ArtifactKind, CacheRead, RefDepth, Storage};
29use ordinary_types::{Field, Kind};
30
31use wasmtime::Module;
32
33use ordinary_utils::REPORTING_ENDPOINTS;
34pub use wasmtime::Engine;
35
36#[cfg(feature = "studio")]
37const IFRAME_RESIZER_CHILD_URL: &str = "https://cdn.jsdelivr.net/npm/@iframe-resizer/child@5.5.9";
38
39#[derive(Clone, Debug, PartialEq)]
40pub enum TemplateResult {
41 Result(Bytes),
42 StatusCode(StatusCode),
43}
44
45#[derive(Clone, Debug)]
46enum Include {
47 Field(u8, Kind),
48 Fields(u8, Vec<Include>, bool),
49}
50
51impl Include {
52 fn build_for_field(
53 field: &Field,
54 ref_field: &TemplateRefField,
55 model_map: &HashMap<String, ModelConfig>,
56 ref_depth: &mut RefDepth,
57 ) -> anyhow::Result<Self> {
58 let field_idx = field.idx;
59
60 match &ref_field.fields {
61 Some(nested_ref_fields) => {
62 let mut nested = vec![];
63 let mut is_list = false;
64
65 let mut nested_field_map = HashMap::new();
66
67 match &field.kind {
68 Kind::List { kind } => {
69 if let Kind::Object { name: _, fields } = &**kind {
70 for nested_field in fields {
71 nested_field_map.insert(nested_field.name.clone(), nested_field);
72 }
73
74 is_list = true;
75 } else {
76 bail!("cannot have nested fields on non object");
77 }
78 }
79 Kind::Object { name: _, fields } => {
80 for nested_field in fields {
81 nested_field_map.insert(nested_field.name.clone(), nested_field);
82 }
83 }
84 Kind::Ref {
85 model,
86 field: _,
87 many: _,
88 } => {
89 if let Some(model) = model_map.get(model) {
90 for nested_field in &model.fields {
91 nested_field_map.insert(nested_field.name.clone(), nested_field);
92 }
93
94 ref_depth.0.push(((field.idx, 1, None), RefDepth(vec![])));
95 }
96 }
97 _ => bail!("cannot have nested fields on non object"),
98 }
99
100 let mut fields_clone = nested_ref_fields.clone();
101 fields_clone.sort_by_key(|a| a.idx);
102
103 for nested_ref_field in fields_clone {
104 if let Some(nested_field) = nested_field_map.get(&nested_ref_field.name) {
105 nested.push(Self::build_for_field(
106 nested_field,
107 &nested_ref_field,
108 model_map,
109 ref_depth,
110 )?);
111 }
112 }
113
114 nested.shrink_to_fit();
115 Ok(Include::Fields(field_idx, nested, is_list))
116 }
117 None => Ok(Include::Field(field_idx, field.kind.clone())),
118 }
119 }
120
121 fn build_for_content(
122 content_def: &ContentDefinition,
123 template_ref: &TemplateRef,
124 ) -> anyhow::Result<Vec<Self>> {
125 let mut include_fields = vec![];
126
127 let mut fields_clone = template_ref.fields.clone();
128 fields_clone.sort_by_key(|a| a.idx);
129
130 let mut content_def_field_map = HashMap::new();
131 for field in &content_def.fields {
132 content_def_field_map.insert(field.name.clone(), field);
133 }
134
135 for field in fields_clone {
136 if let Some(def_field) = content_def_field_map.get(&field.name) {
137 let include = Self::build_for_field(
138 def_field,
139 &field,
140 &HashMap::new(),
141 &mut RefDepth(vec![]),
142 )?;
143 include_fields.push(include);
144 }
145 }
146
147 include_fields.shrink_to_fit();
148 Ok(include_fields)
149 }
150
151 fn build_for_model(
152 model_config: &ModelConfig,
153 template_ref: &TemplateRef,
154 model_map: &HashMap<String, ModelConfig>,
155 ) -> anyhow::Result<(Vec<Self>, RefDepth)> {
156 let mut include_fields = vec![];
157
158 let mut fields_clone = template_ref.fields.clone();
159 fields_clone.sort_by_key(|a| a.idx);
160
161 let mut model_config_field_map = HashMap::new();
162 for field in &model_config.fields {
163 model_config_field_map.insert(field.name.clone(), field);
164 }
165 let mut ref_depth = RefDepth(vec![]);
166
167 for field in fields_clone {
168 if let Some(def_field) = model_config_field_map.get(&field.name) {
169 let include = Self::build_for_field(def_field, &field, model_map, &mut ref_depth)?;
170 include_fields.push(include);
171 }
172 }
173
174 include_fields.shrink_to_fit();
175 Ok((include_fields, ref_depth))
176 }
177
178 fn marry(
179 &self,
180 builder: &mut flexbuffers::VectorBuilder,
181 reader: &VectorReader<&[u8]>,
182 ) -> anyhow::Result<()> {
183 match self {
184 Include::Field(idx, kind) => {
185 kind.copy_to(&reader.idx(*idx as usize), builder, None)?;
186 }
187 Include::Fields(idx, fields, is_list) => {
188 let mut nested = builder.start_vector();
189 let reader = reader.idx(*idx as usize).as_vector();
190
191 if *is_list {
192 for list_reader in &reader {
193 let mut builder = nested.start_vector();
194
195 for field in fields {
196 field.marry(&mut builder, &list_reader.as_vector())?;
197 }
198
199 builder.end_vector();
200 }
201 } else {
202 for field in fields {
203 field.marry(&mut nested, &reader)?;
204 }
205 }
206
207 nested.end_vector();
208 }
209 }
210
211 Ok(())
212 }
213}
214
215#[derive(Clone, Debug)]
216enum QueryKind {
217 Segment(usize, bool),
219 Token(u8),
221 None,
223}
224
225#[derive(Clone, Debug)]
226enum Query {
227 Model(
229 u8,
230 u8,
231 Kind,
232 RefDepth,
233 Option<ordinary_storage::QueryExpression>,
234 ),
235 Content(u8, u8, Kind),
237
238 ContentMulti(u8),
240}
241
242#[derive(Clone)]
243pub struct Template {
244 pub idx: u8,
245 pub mime: HeaderValue,
246 pub reporting_endpoints: (HeaderName, HeaderValue),
247 pub config: TemplateConfig,
248
249 pub cache_control: Option<String>,
250 pub csp: Arc<RwLock<Option<HeaderValue>>>,
251
252 globals: Option<Bytes>,
253 fields: Option<Bytes>,
254
255 engine: Engine,
256 module: Arc<RwLock<Option<Module>>>,
257
258 storage: Arc<Storage>,
259
260 queries: Option<Vec<(Query, QueryKind, Vec<Include>)>>,
261}
262
263impl Template {
264 #[allow(clippy::too_many_lines, clippy::too_many_arguments)]
265 #[instrument(
266 name = "template"
267 skip_all,
268 fields(i, nm),
269 err
270 )]
271 pub fn new(
272 domain: &str,
273 secure: bool,
274
275 src: Option<Bytes>,
276
277 config: TemplateConfig,
278 auth: Arc<Auth>,
279 model_map: &HashMap<String, ModelConfig>,
280 content_map: &HashMap<String, ContentDefinition>,
281
282 globals: Option<Bytes>,
283 fields: Option<Bytes>,
284
285 engine: Engine,
286 storage: Arc<Storage>,
287 ) -> anyhow::Result<Template> {
288 tracing::Span::current().record("i", config.idx);
289 tracing::Span::current().record("nm", tracing::field::display(&config.name));
290
291 let (module, csp) = if let Some(src) = src {
292 let root = flexbuffers::Reader::get_root(src.as_ref())?;
293 let root_vec = root.as_vector();
294
295 if let Ok(module) = Module::new(&engine, root_vec.idx(1).as_blob().0) {
296 let csp = if matches!(
297 config.mime.as_str(),
298 "text/html" | "text/html; charset=utf-8" | "text/xml"
299 ) {
300 Some(HeaderValue::from_str(root_vec.idx(0).as_str())?)
301 } else {
302 None
303 };
304
305 (
306 Arc::new(RwLock::new(Some(module))),
307 Arc::new(RwLock::new(csp)),
308 )
309 } else {
310 (Arc::new(RwLock::new(None)), Arc::new(RwLock::new(None)))
311 }
312 } else {
313 (Arc::new(RwLock::new(None)), Arc::new(RwLock::new(None)))
314 };
315
316 tracing::info!("init");
317
318 let mut queries = vec![];
319
320 if let Some(content_refs) = &config.content {
323 for content_ref in content_refs {
324 if let Some(content_def) = content_map.get(&content_ref.name) {
325 let mut def_fields_map = BTreeMap::new();
326
327 for field in &content_def.fields {
328 def_fields_map.insert(field.name.clone(), field);
329 }
330
331 let include_fields = Include::build_for_content(content_def, content_ref)?;
332
333 if content_ref.all.is_some() {
334 queries.push((
335 content_ref.idx,
336 Query::ContentMulti(content_def.idx),
337 QueryKind::None,
338 include_fields.clone(),
339 ));
340 }
341
342 for ref_field in &content_ref.fields {
343 if let Some(def_field) = def_fields_map.get(&ref_field.name)
344 && let Some(binding) = &ref_field.bind
345 {
346 match binding {
347 TemplateRefFieldBind::Segment {
348 name,
349 expression: _,
350 } => {
351 let mut path_pos = None;
352
353 for (i, segment) in config.route.split('/').enumerate() {
354 if segment == format!("{{{name}}}") {
355 path_pos = Some((i, name.starts_with('*')));
356 }
357 }
358
359 if let Some((path_pos, is_wildcard)) = path_pos {
360 queries.push((
361 content_ref.idx,
362 Query::Content(
363 content_def.idx,
364 def_field.idx,
365 def_field.kind.clone(),
366 ),
367 QueryKind::Segment(path_pos, is_wildcard),
368 include_fields.clone(),
369 ));
370 } else {
371 bail!(
372 "cannot bind to segment {{{name}}} as it is not present in the route"
373 );
374 }
375 }
376 TemplateRefFieldBind::Token {
377 field,
378 expression: _,
379 } => {
380 let mut auth_field_idx = None;
381
382 for auth_field in &auth.config.access_token.claims {
383 if &auth_field.name == field {
384 auth_field_idx = Some(auth_field.idx);
385 }
386 }
387
388 if let Some(idx) = auth_field_idx {
389 queries.push((
390 content_ref.idx,
391 Query::Content(
392 content_def.idx,
393 def_field.idx,
394 def_field.kind.clone(),
395 ),
396 QueryKind::Token(idx),
397 include_fields.clone(),
398 ));
399 } else {
400 bail!("auth field {field} does not exist");
401 }
402 }
403 }
404 }
405 }
406 }
407 }
408 }
409
410 if let Some(model_refs) = &config.models {
411 for model_ref in model_refs {
412 if let Some(model_config) = model_map.get(&model_ref.name) {
413 let mut model_config_fields_map = BTreeMap::new();
414
415 let mut model_config = (*model_config).clone();
416
417 let uuid_field = Field {
418 idx: 0,
419 name: "uuid".into(),
420 kind: Kind::Uuid,
421 indexed: Some(true),
422 queryable: None,
423 searchable: None,
424 mapping: None,
425 doc: None,
426 encrypted: None,
427 compressed: None,
428 };
429
430 let mut new_fields = model_config.fields.clone();
431 new_fields.splice(0..0, vec![uuid_field]);
432
433 model_config.fields = new_fields;
434
435 for field in &model_config.fields {
436 model_config_fields_map.insert(field.name.clone(), field);
437 }
438
439 let (include_fields, ref_depth) =
440 Include::build_for_model(&model_config, model_ref, model_map)?;
441
442 for ref_field in &model_ref.fields {
443 if let Some(config_field) = model_config_fields_map.get(&ref_field.name)
444 && let Some(binding) = &ref_field.bind
445 {
446 match binding {
447 TemplateRefFieldBind::Segment { name, expression } => {
448 let mut path_pos = None;
449
450 for (i, segment) in config.route.split('/').enumerate() {
451 if segment == format!("{{{name}}}") {
452 path_pos = Some((i, name.starts_with('*')));
453 }
454 }
455
456 let exp = match expression {
457 Some(exp) => match exp {
458 QueryExpression::Gte => {
459 Some(ordinary_storage::QueryExpression::Gte)
460 }
461 QueryExpression::Lte => {
462 Some(ordinary_storage::QueryExpression::Lte)
463 }
464 QueryExpression::Gt => {
465 Some(ordinary_storage::QueryExpression::Gt)
466 }
467 QueryExpression::Lt => {
468 Some(ordinary_storage::QueryExpression::Lt)
469 }
470 QueryExpression::Eq => {
471 Some(ordinary_storage::QueryExpression::Eq)
472 }
473 QueryExpression::BeginsWith => {
474 Some(ordinary_storage::QueryExpression::BeginsWith)
475 }
476 },
477 None => None,
478 };
479
480 if let Some((path_pos, is_wildcard)) = path_pos {
481 queries.push((
482 model_ref.idx,
483 Query::Model(
484 model_config.idx,
485 config_field.idx,
486 config_field.kind.clone(),
487 ref_depth.clone(),
488 exp,
489 ),
490 QueryKind::Segment(path_pos, is_wildcard),
491 include_fields.clone(),
492 ));
493 } else {
494 bail!(
495 "cannot bind to segment {{{name}}} as it is not present in the route"
496 );
497 }
498 }
499 TemplateRefFieldBind::Token { field, expression } => {
500 let mut auth_field_idx = None;
501
502 for auth_field in &auth.config.access_token.claims {
503 if &auth_field.name == field {
504 auth_field_idx = Some(auth_field.idx);
505 }
506 }
507
508 let exp = match expression {
509 Some(exp) => match exp {
510 QueryExpression::Gte => {
511 Some(ordinary_storage::QueryExpression::Gte)
512 }
513 QueryExpression::Lte => {
514 Some(ordinary_storage::QueryExpression::Lte)
515 }
516 QueryExpression::Gt => {
517 Some(ordinary_storage::QueryExpression::Gt)
518 }
519 QueryExpression::Lt => {
520 Some(ordinary_storage::QueryExpression::Lt)
521 }
522 QueryExpression::Eq => {
523 Some(ordinary_storage::QueryExpression::Eq)
524 }
525 QueryExpression::BeginsWith => {
526 Some(ordinary_storage::QueryExpression::BeginsWith)
527 }
528 },
529 None => None,
530 };
531
532 if let Some(idx) = auth_field_idx {
533 queries.push((
534 model_ref.idx,
535 Query::Model(
536 model_config.idx,
537 config_field.idx,
538 config_field.kind.clone(),
539 ref_depth.clone(),
540 exp,
541 ),
542 QueryKind::Token(idx),
543 include_fields.clone(),
544 ));
545 } else {
546 bail!("auth field {field} does not exist");
547 }
548 }
549 }
550 }
551 }
552 }
553 }
554 }
555
556 let mut cache_control = String::new();
557
558 if let Some(cache) = &config.cache
559 && let Some(http_cache) = &cache.http
560 && let Some(http_cache_control) = &http_cache.cache_control
561 {
562 http_cache_control.header_value(&mut cache_control, "")?;
564 } else {
565 }
567
568 let reporting_endpoints = format!(
569 "csp=\"http{}://{domain}/.ordinary/reports/csp\"",
570 if secure { "s" } else { "" }
571 );
572
573 Ok(Template {
574 idx: config.idx,
575 mime: HeaderValue::from_str(config.mime.as_str())?,
576 reporting_endpoints: (
577 REPORTING_ENDPOINTS,
578 HeaderValue::from_str(reporting_endpoints.as_str())?,
579 ),
580
581 config,
582
583 cache_control: if cache_control.is_empty() {
584 None
585 } else {
586 Some(cache_control)
587 },
588
589 csp,
590
591 engine,
592 module,
593 storage,
594
595 globals,
596 fields,
597
598 queries: if queries.is_empty() {
599 None
600 } else {
601 queries.sort_by_key(|a| a.0);
602 let mut queries = queries
603 .iter()
604 .map(|(_, q, qk, i)| (q.clone(), qk.clone(), i.clone()))
605 .collect::<Vec<_>>();
606
607 queries.shrink_to_fit();
608
609 Some(queries)
610 },
611 })
612 }
613
614 pub fn start_tasks(&self) {
615 if let Some(cache_config) = &self.config.cache
616 && let Some(stored_cache) = &cache_config.stored
617 {
618 let storage_clone = self.storage.clone();
619
620 let cache_config_clone = stored_cache.clone();
621 let idx = self.config.idx;
622
623 let template_span =
624 info_span!("template", i = &self.config.idx, nm = %self.config.name);
625
626 let cache_span = template_span.in_scope(|| info_span!("cache"));
627
628 if let StoredCachePolicy::Permanent = stored_cache.policy {
629 } else {
631 let (mut clean_min, mut clean_max) =
633 cache_config_clone.clean_interval.unwrap_or((60, 60 * 3));
634
635 clean_min *= 1000;
636 clean_max *= 1000;
637
638 tokio::spawn(async move {
639 loop {
640 let cache_clean_interval_ms = rand::random_range(clean_min..clean_max);
643
644 tokio::time::sleep(Duration::from_millis(cache_clean_interval_ms)).await;
645
646 async {
647 if let Err(err) = storage_clone
648 .cache
649 .clean_cache(&CacheRead::Template, &cache_config_clone, idx)
650 .await
651 {
652 tracing::error!(%err, "failed to clean cache");
653 }
654 }
655 .instrument(cache_span.clone())
656 .await;
657 }
658 });
659 }
660 }
661 }
662
663 #[instrument(skip_all, err)]
664 pub async fn set_wasm(
665 &self,
666 src: &[u8],
667 app_config: &OrdinaryConfig,
668 csp_style: Option<Vec<String>>,
669 csp_script: Option<Vec<String>>,
670 secure: bool,
671 ) -> anyhow::Result<()> {
672 let storage_span = info_span!("storage");
673 let span = storage_span.in_scope(|| info_span!("artifact"));
674
675 #[cfg(feature = "studio")]
676 let script_urls = Some(vec![IFRAME_RESIZER_CHILD_URL.to_string()]);
677
678 #[cfg(not(feature = "studio"))]
679 let script_urls = None;
680
681 let csp_string = self.config.csp.clone().unwrap_or_default().build_string(
682 &app_config.csp.clone().unwrap_or_default(),
683 csp_style,
684 csp_script,
685 script_urls,
686 secure,
687 app_config.has_ordinary_actions() || app_config.auth.is_some(),
688 );
689
690 let mut builder = flexbuffers::Builder::new(&flexbuffers::BuilderOptions::SHARE_NONE);
691 let mut builder_vec = builder.start_vector();
692
693 builder_vec.push(csp_string.as_str());
694 builder_vec.push(flexbuffers::Blob(src));
695
696 builder_vec.end_vector();
697
698 span.in_scope(|| {
699 self.storage
700 .artifact
701 .put(self.idx, ArtifactKind::Template, builder.view())
702 })?;
703
704 {
705 let mut lock = self.module.write();
706 let module = Module::new(&self.engine, src)?;
707
708 *lock = Some(module);
709 }
710
711 if let Ok(mime) = self.mime.to_str()
712 && matches!(mime, "text/html" | "text/html; charset=utf-8" | "text/xml")
713 {
714 let mut lock = self.csp.write();
715
716 *lock = Some(HeaderValue::from_str(csp_string.as_str())?);
717 drop(lock);
718 }
719
720 let span = storage_span.in_scope(|| info_span!("cache"));
721
722 async {
723 self.storage
724 .cache
725 .artifact_evict(CacheRead::Template, self.idx)
726 .await
727 }
728 .instrument(span)
729 .await?;
730
731 Ok(())
732 }
733
734 #[instrument(skip_all, err)]
735 pub fn render(
736 &self,
737 host: &str,
738 path: String,
739 params: Option<String>,
740 error: Option<(String, u16)>,
742 totp: Option<(String, String, String)>,
744 claims: &Option<VectorReader<&[u8]>>,
745 ) -> anyhow::Result<TemplateResult> {
746 let args = self.query(host, path, params, error, totp, claims)?;
747
748 match self.config.ffi.version {
749 TemplateFfiVersion::V1 => {
750 #[cfg(not(feature = "studio"))]
751 {
752 ffi::v1::call(self, &args)
753 }
754
755 #[cfg(feature = "studio")]
756 {
757 if self.config.mime.contains("html") {
758 let res = ffi::v1::call(self, &args)?;
759
760 match res {
761 TemplateResult::StatusCode(_) => Ok(res),
762 TemplateResult::Result(bytes) => {
763 let str = std::str::from_utf8(bytes.as_ref())?;
764
765 let res = if str.contains("<head>") {
766 str.replace("<head>", &format!(r#"<head><script async src="{IFRAME_RESIZER_CHILD_URL}"></script>"#))
767 } else {
768 str.replace("<html lang=en>", &format!(r#"<html lang=en><script async src="{IFRAME_RESIZER_CHILD_URL}"></script>"#))
769 };
770
771 Ok(TemplateResult::Result(Bytes::copy_from_slice(
772 res.as_bytes(),
773 )))
774 }
775 }
776 } else {
777 ffi::v1::call(self, &args)
778 }
779 }
780 }
781 }
782 }
783
784 #[allow(clippy::too_many_lines)]
786 #[instrument(skip_all, err)]
787 pub fn query(
788 &self,
789 host: &str,
790 path: String,
791 params: Option<String>,
792 error: Option<(String, u16)>,
794 totp: Option<(String, String, String)>,
796 claims: &Option<VectorReader<&[u8]>>,
797 ) -> anyhow::Result<Bytes> {
798 let mut builder = flexbuffers::Builder::new(&flexbuffers::BuilderOptions::SHARE_NONE);
799 let mut vec_builder = builder.start_vector();
800
801 if let Some(queries) = &self.queries {
802 let segments: Vec<&str> = path.split('/').collect();
803
804 let _params_map = match params {
805 Some(_params) => {
806 let params_map: BTreeMap<String, String> = BTreeMap::new();
807 Some(params_map)
809 }
810 None => None,
811 };
812
813 for (query, query_kind, includes) in queries {
818 let kind = match query {
819 Query::Model(_, _, kind, _, _) | Query::Content(_, _, kind) => kind,
820 Query::ContentMulti(_) => &Kind::Void,
821 };
822
823 let indexed_val: Bytes = match query_kind {
824 QueryKind::Segment(pos, is_wildcard) => {
825 if pos <= &segments.len() {
826 let val = if *is_wildcard {
827 let mut out = String::new();
828
829 for (i, segment) in segments.iter().enumerate() {
830 if i == *pos {
831 (*segment).clone_into(&mut out);
832 } else if i > *pos {
833 out = format!("{out}/{segment}");
834 }
835 }
836
837 out
838 } else {
839 segments[*pos].to_string()
840 };
841
842 match kind {
843 Kind::Uuid => {
844 let uuid = uuid::Uuid::from_str(&val)?;
845 Bytes::copy_from_slice(uuid.as_bytes())
846 }
847 _ => Bytes::copy_from_slice(val.as_bytes()),
848 }
849 } else {
850 bail!("position out of bounds for route");
851 }
852 }
853 QueryKind::Token(field_idx) => {
854 if let Some(claims) = claims {
855 let reader = claims.idx(*field_idx as usize);
856
857 match kind {
858 Kind::Uuid => Bytes::copy_from_slice(reader.as_blob().0),
859 Kind::String => Bytes::copy_from_slice(reader.as_str().as_bytes()),
860 _ => Bytes::new(),
861 }
862 } else {
863 bail!("no claims on query");
864 }
865 }
866 QueryKind::None => Bytes::new(),
867 };
868
869 let span = info_span!("storage");
870
871 match query {
872 Query::Content(def_idx, field_idx, _kind) => {
873 let content_obj = span.in_scope(|| {
874 let content_span = info_span!("content");
875
876 content_span.in_scope(|| {
877 self.storage.content.get(*def_idx, *field_idx, &indexed_val)
878 })
879 })?;
880
881 let root = flexbuffers::Reader::get_root(&content_obj[..])?;
882
883 let mut fields_builder = vec_builder.start_vector();
884
885 for include in includes {
886 include.marry(&mut fields_builder, &root.as_vector())?;
887 }
888
889 fields_builder.end_vector();
890 }
891 Query::ContentMulti(def_idx) => {
892 let content_objs = span.in_scope(|| {
893 let content_span = info_span!("content");
894
895 content_span.in_scope(|| self.storage.content.list(*def_idx))
896 })?;
897
898 let root = flexbuffers::Reader::get_root(&content_objs[..])?;
899
900 let mut object_vector = vec_builder.start_vector();
901
902 for object in &root.as_vector() {
903 let obj_bytes = object.as_blob().0;
904
905 let object = flexbuffers::Reader::get_root(obj_bytes)?;
906
907 let mut fields_builder = object_vector.start_vector();
908
909 for include in includes {
910 include.marry(&mut fields_builder, &object.as_vector())?;
911 }
912
913 fields_builder.end_vector();
914 }
915
916 object_vector.end_vector();
917 }
918 Query::Model(model_kind, field_idx, _kind, ref_depth, expression) => {
919 if let Some(exp) = expression {
920 let items = span.in_scope(|| {
921 self.storage.model.query_model(
922 *model_kind,
923 *field_idx,
924 exp.as_byte(),
925 &indexed_val,
926 ref_depth,
927 None,
928 )
929 })?;
930
931 let root = flexbuffers::Reader::get_root(&items[..])?;
932
933 let mut item_builder = vec_builder.start_vector();
934
935 for item in &root.as_vector() {
936 let mut fields_builder = item_builder.start_vector();
937
938 for include in includes {
939 include.marry(&mut fields_builder, &item.as_vector())?;
940 }
941
942 fields_builder.end_vector();
943 }
944
945 item_builder.end_vector();
946 } else {
947 let model_item = span.in_scope(|| {
948 self.storage.model.get_model(
949 *model_kind,
950 *field_idx,
951 &indexed_val,
952 ref_depth,
953 None,
954 )
955 })?;
956
957 let root = flexbuffers::Reader::get_root(&model_item[..])?;
958
959 let mut fields_builder = vec_builder.start_vector();
960
961 for include in includes {
962 include.marry(&mut fields_builder, &root.as_vector())?;
963 }
964
965 fields_builder.end_vector();
966 }
967 }
968 }
969 }
970 }
971
972 if let Some(error) = error {
973 let mut error_builder = vec_builder.start_vector();
974
975 error_builder.push(error.0.as_str());
976 error_builder.push(error.1);
977
978 error_builder.end_vector();
979 }
980
981 if let Some(totp) = totp {
982 let mut totp_builder = vec_builder.start_vector();
983
984 totp_builder.push(totp.0.as_str());
985 totp_builder.push(totp.1.as_str());
986
987 let mut recovery_codes_vec = totp_builder.start_vector();
988
989 let mut chunk = String::new();
990
991 for (i, c) in totp.2.chars().enumerate() {
992 if i != 0 && i % 11 == 0 {
993 recovery_codes_vec.push(chunk.as_str());
994 chunk = String::new();
995 } else {
996 chunk = format!("{chunk}{c}");
997 }
998 }
999
1000 recovery_codes_vec.end_vector();
1001
1002 totp_builder.end_vector();
1003 }
1004
1005 vec_builder.push(host);
1006
1007 if let Some(globals) = &self.globals {
1008 vec_builder.push(flexbuffers::Blob(globals.as_ref()));
1009 }
1010 if let Some(fields) = &self.fields {
1011 vec_builder.push(flexbuffers::Blob(fields.as_ref()));
1012 }
1013
1014 vec_builder.end_vector();
1015
1016 Ok(Bytes::copy_from_slice(builder.view()))
1017 }
1018}