Skip to main content

ordinary_template/
lib.rs

1#![doc = include_str!("../README.md")]
2#![warn(clippy::all, clippy::pedantic)]
3#![allow(clippy::missing_errors_doc, clippy::cast_sign_loss)]
4
5// Copyright (C) 2026 Ordinary Labs, LLC.
6//
7// SPDX-License-Identifier: AGPL-3.0-only
8
9mod 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    /// path position, whether it's a wildcard
218    Segment(usize, bool),
219    /// token field idx
220    Token(u8),
221    /// if there is no query
222    None,
223}
224
225#[derive(Clone, Debug)]
226enum Query {
227    /// model kind, field idx, type
228    Model(
229        u8,
230        u8,
231        Kind,
232        RefDepth,
233        Option<ordinary_storage::QueryExpression>,
234    ),
235    /// def idx, field idx, type
236    Content(u8, u8, Kind),
237
238    /// def idx
239    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        // todo: disallow conflicting bindings
321
322        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            // todo: make the default configurable by the API server
563            http_cache_control.header_value(&mut cache_control, "")?;
564        } else {
565            // todo: set a default for the cache control (dictated by Ordinary API Server)
566        }
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                // don't start the cache_clean task
630            } else {
631                // todo: pass this default and a range check from Ordinary API
632                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                        // todo: break on app kill
641
642                        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        // message and code
741        error: Option<(String, u16)>,
742        // svg qr_code, account and recovery_codes
743        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    // accepts token + bound arguments and then uses config to go request all the stuff
785    #[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        // message and code
793        error: Option<(String, u16)>,
794        // svg qr_code, account and recovery_codes
795        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                    // todo: update params map
808                    Some(params_map)
809                }
810                None => None,
811            };
812
813            // todo: push params into
814
815            // todo: push flags into
816
817            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}