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 = "tmp"
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 = info_span!("tmp", i = &self.config.idx, nm = %self.config.name);
624
625 let cache_span = template_span.in_scope(|| info_span!("cache"));
626
627 if let StoredCachePolicy::Permanent = stored_cache.policy {
628 } else {
630 let (mut clean_min, mut clean_max) =
632 cache_config_clone.clean_interval.unwrap_or((60, 60 * 3));
633
634 clean_min *= 1000;
635 clean_max *= 1000;
636
637 tokio::spawn(async move {
638 loop {
639 let cache_clean_interval_ms = rand::random_range(clean_min..clean_max);
642
643 tokio::time::sleep(Duration::from_millis(cache_clean_interval_ms)).await;
644
645 async {
646 if let Err(err) = storage_clone
647 .cache
648 .clean_cache(&CacheRead::Template, &cache_config_clone, idx)
649 .await
650 {
651 tracing::error!(%err, "failed to clean cache");
652 }
653 }
654 .instrument(cache_span.clone())
655 .await;
656 }
657 });
658 }
659 }
660 }
661
662 #[instrument(skip_all, err)]
663 pub async fn set_wasm(
664 &self,
665 src: &[u8],
666 app_config: &OrdinaryConfig,
667 csp_style: Option<Vec<String>>,
668 csp_script: Option<Vec<String>>,
669 secure: bool,
670 ) -> anyhow::Result<()> {
671 let storage_span = info_span!("storage");
672 let span = storage_span.in_scope(|| info_span!("artifact"));
673
674 #[cfg(feature = "studio")]
675 let script_urls = Some(vec![IFRAME_RESIZER_CHILD_URL.to_string()]);
676
677 #[cfg(not(feature = "studio"))]
678 let script_urls = None;
679
680 let csp_string = self.config.csp.clone().unwrap_or_default().build_string(
681 &app_config.csp.clone().unwrap_or_default(),
682 csp_style,
683 csp_script,
684 script_urls,
685 secure,
686 app_config.has_ordinary_actions() || app_config.auth.is_some(),
687 );
688
689 let mut builder = flexbuffers::Builder::new(&flexbuffers::BuilderOptions::SHARE_NONE);
690 let mut builder_vec = builder.start_vector();
691
692 builder_vec.push(csp_string.as_str());
693 builder_vec.push(flexbuffers::Blob(src));
694
695 builder_vec.end_vector();
696
697 span.in_scope(|| {
698 self.storage
699 .artifact
700 .put(self.idx, ArtifactKind::Template, builder.view())
701 })?;
702
703 {
704 let mut lock = self.module.write();
705 let module = Module::new(&self.engine, src)?;
706
707 *lock = Some(module);
708 }
709
710 if let Ok(mime) = self.mime.to_str()
711 && matches!(mime, "text/html" | "text/html; charset=utf-8" | "text/xml")
712 {
713 let mut lock = self.csp.write();
714
715 *lock = Some(HeaderValue::from_str(csp_string.as_str())?);
716 drop(lock);
717 }
718
719 let span = storage_span.in_scope(|| info_span!("cache"));
720
721 async {
722 self.storage
723 .cache
724 .artifact_evict(CacheRead::Template, self.idx)
725 .await
726 }
727 .instrument(span)
728 .await?;
729
730 Ok(())
731 }
732
733 #[instrument(skip_all, err)]
734 pub fn render(
735 &self,
736 host: &str,
737 path: String,
738 params: Option<String>,
739 error: Option<(String, u16)>,
741 totp: Option<(String, String, String)>,
743 claims: &Option<VectorReader<&[u8]>>,
744 ) -> anyhow::Result<TemplateResult> {
745 let args = self.query(host, path, params, error, totp, claims)?;
746
747 match self.config.ffi.version {
748 TemplateFfiVersion::V1 => {
749 #[cfg(not(feature = "studio"))]
750 {
751 ffi::v1::call(self, &args)
752 }
753
754 #[cfg(feature = "studio")]
755 {
756 if self.config.mime.contains("html") {
757 let res = ffi::v1::call(self, &args)?;
758
759 match res {
760 TemplateResult::StatusCode(_) => Ok(res),
761 TemplateResult::Result(bytes) => {
762 let str = std::str::from_utf8(bytes.as_ref())?;
763
764 let res = if str.contains("<head>") {
765 str.replace("<head>", &format!(r#"<head><script async src="{IFRAME_RESIZER_CHILD_URL}"></script>"#))
766 } else {
767 str.replace("<html lang=en>", &format!(r#"<html lang=en><script async src="{IFRAME_RESIZER_CHILD_URL}"></script>"#))
768 };
769
770 Ok(TemplateResult::Result(Bytes::copy_from_slice(
771 res.as_bytes(),
772 )))
773 }
774 }
775 } else {
776 ffi::v1::call(self, &args)
777 }
778 }
779 }
780 }
781 }
782
783 #[allow(clippy::too_many_lines)]
785 #[instrument(skip_all, err)]
786 pub fn query(
787 &self,
788 host: &str,
789 path: String,
790 params: Option<String>,
791 error: Option<(String, u16)>,
793 totp: Option<(String, String, String)>,
795 claims: &Option<VectorReader<&[u8]>>,
796 ) -> anyhow::Result<Bytes> {
797 let mut builder = flexbuffers::Builder::new(&flexbuffers::BuilderOptions::SHARE_NONE);
798 let mut vec_builder = builder.start_vector();
799
800 if let Some(queries) = &self.queries {
801 let segments: Vec<&str> = path.split('/').collect();
802
803 let _params_map = match params {
804 Some(_params) => {
805 let params_map: BTreeMap<String, String> = BTreeMap::new();
806 Some(params_map)
808 }
809 None => None,
810 };
811
812 for (query, query_kind, includes) in queries {
817 let kind = match query {
818 Query::Model(_, _, kind, _, _) | Query::Content(_, _, kind) => kind,
819 Query::ContentMulti(_) => &Kind::Void,
820 };
821
822 let indexed_val: Bytes = match query_kind {
823 QueryKind::Segment(pos, is_wildcard) => {
824 if pos <= &segments.len() {
825 let val = if *is_wildcard {
826 let mut out = String::new();
827
828 for (i, segment) in segments.iter().enumerate() {
829 if i == *pos {
830 (*segment).clone_into(&mut out);
831 } else if i > *pos {
832 out = format!("{out}/{segment}");
833 }
834 }
835
836 out
837 } else {
838 segments[*pos].to_string()
839 };
840
841 match kind {
842 Kind::Uuid => {
843 let uuid = uuid::Uuid::from_str(&val)?;
844 Bytes::copy_from_slice(uuid.as_bytes())
845 }
846 _ => Bytes::copy_from_slice(val.as_bytes()),
847 }
848 } else {
849 bail!("position out of bounds for route");
850 }
851 }
852 QueryKind::Token(field_idx) => {
853 if let Some(claims) = claims {
854 let reader = claims.idx(*field_idx as usize);
855
856 match kind {
857 Kind::Uuid => Bytes::copy_from_slice(reader.as_blob().0),
858 Kind::String => Bytes::copy_from_slice(reader.as_str().as_bytes()),
859 _ => Bytes::new(),
860 }
861 } else {
862 bail!("no claims on query");
863 }
864 }
865 QueryKind::None => Bytes::new(),
866 };
867
868 let span = info_span!("storage");
869
870 match query {
871 Query::Content(def_idx, field_idx, _kind) => {
872 let content_obj = span.in_scope(|| {
873 let content_span = info_span!("content");
874
875 content_span.in_scope(|| {
876 self.storage.content.get(*def_idx, *field_idx, &indexed_val)
877 })
878 })?;
879
880 let root = flexbuffers::Reader::get_root(&content_obj[..])?;
881
882 let mut fields_builder = vec_builder.start_vector();
883
884 for include in includes {
885 include.marry(&mut fields_builder, &root.as_vector())?;
886 }
887
888 fields_builder.end_vector();
889 }
890 Query::ContentMulti(def_idx) => {
891 let content_objs = span.in_scope(|| {
892 let content_span = info_span!("content");
893
894 content_span.in_scope(|| self.storage.content.list(*def_idx))
895 })?;
896
897 let root = flexbuffers::Reader::get_root(&content_objs[..])?;
898
899 let mut object_vector = vec_builder.start_vector();
900
901 for object in &root.as_vector() {
902 let obj_bytes = object.as_blob().0;
903
904 let object = flexbuffers::Reader::get_root(obj_bytes)?;
905
906 let mut fields_builder = object_vector.start_vector();
907
908 for include in includes {
909 include.marry(&mut fields_builder, &object.as_vector())?;
910 }
911
912 fields_builder.end_vector();
913 }
914
915 object_vector.end_vector();
916 }
917 Query::Model(model_kind, field_idx, _kind, ref_depth, expression) => {
918 if let Some(exp) = expression {
919 let items = span.in_scope(|| {
920 self.storage.model.query_model(
921 *model_kind,
922 *field_idx,
923 exp.as_byte(),
924 &indexed_val,
925 ref_depth,
926 None,
927 )
928 })?;
929
930 let root = flexbuffers::Reader::get_root(&items[..])?;
931
932 let mut item_builder = vec_builder.start_vector();
933
934 for item in &root.as_vector() {
935 let mut fields_builder = item_builder.start_vector();
936
937 for include in includes {
938 include.marry(&mut fields_builder, &item.as_vector())?;
939 }
940
941 fields_builder.end_vector();
942 }
943
944 item_builder.end_vector();
945 } else {
946 let model_item = span.in_scope(|| {
947 self.storage.model.get_model(
948 *model_kind,
949 *field_idx,
950 &indexed_val,
951 ref_depth,
952 None,
953 )
954 })?;
955
956 let root = flexbuffers::Reader::get_root(&model_item[..])?;
957
958 let mut fields_builder = vec_builder.start_vector();
959
960 for include in includes {
961 include.marry(&mut fields_builder, &root.as_vector())?;
962 }
963
964 fields_builder.end_vector();
965 }
966 }
967 }
968 }
969 }
970
971 if let Some(error) = error {
972 let mut error_builder = vec_builder.start_vector();
973
974 error_builder.push(error.0.as_str());
975 error_builder.push(error.1);
976
977 error_builder.end_vector();
978 }
979
980 if let Some(totp) = totp {
981 let mut totp_builder = vec_builder.start_vector();
982
983 totp_builder.push(totp.0.as_str());
984 totp_builder.push(totp.1.as_str());
985
986 let mut recovery_codes_vec = totp_builder.start_vector();
987
988 let mut chunk = String::new();
989
990 for (i, c) in totp.2.chars().enumerate() {
991 if i != 0 && i % 11 == 0 {
992 recovery_codes_vec.push(chunk.as_str());
993 chunk = String::new();
994 } else {
995 chunk = format!("{chunk}{c}");
996 }
997 }
998
999 recovery_codes_vec.end_vector();
1000
1001 totp_builder.end_vector();
1002 }
1003
1004 vec_builder.push(host);
1005
1006 if let Some(globals) = &self.globals {
1007 vec_builder.push(flexbuffers::Blob(globals.as_ref()));
1008 }
1009 if let Some(fields) = &self.fields {
1010 vec_builder.push(flexbuffers::Blob(fields.as_ref()));
1011 }
1012
1013 vec_builder.end_vector();
1014
1015 Ok(Bytes::copy_from_slice(builder.view()))
1016 }
1017}