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