toasty_core/stmt/cx/
projected_field.rs1use super::ExprContext;
2use crate::{
3 schema::{app, mapping},
4 stmt::{Expr, ExprReference, Path},
5};
6
7#[derive(Debug)]
9pub struct ProjectedField<'a> {
10 pub field: &'a app::Field,
12
13 pub mapping: &'a mapping::Field,
15
16 pub path: Path,
22
23 pub nesting: usize,
27}
28
29impl<'a> ExprContext<'a> {
30 pub fn resolve_projected_field(&self, expr: &Expr) -> Option<ProjectedField<'a>> {
43 match expr {
44 Expr::Reference(ExprReference::Field { nesting, index }) => {
45 let model = self.target_at(*nesting).as_model()?;
46 Some(ProjectedField {
47 field: model.fields.get(*index)?,
48 mapping: self.schema.mapping_for(model.id).fields.get(*index)?,
49 path: Path::field(model.id, *index),
50 nesting: *nesting,
51 })
52 }
53 Expr::Project(project) => {
54 let projected = if let Expr::Variant(variant) = &*project.base {
55 let projected = self.resolve_projected_field(&variant.base)?;
56 let app::FieldTy::Embedded(embedded) = &projected.field.ty else {
57 return None;
58 };
59 if embedded.target != variant.variant.model {
60 return None;
61 }
62 let mapping::Field::Enum(mapping) = projected.mapping else {
63 return None;
64 };
65 let mapping = mapping.variants.get(variant.variant.index)?;
66 let model = self
67 .schema
68 .app
69 .model(embedded.target)
70 .as_embedded_enum_unwrap();
71 let fields = model.variant_fields(variant.variant.index);
72 let (&index, rest) = project.projection.as_slice().split_first()?;
73 let mut path = Path::from_variant(projected.path, variant.variant);
74 path.projection.push(index);
75
76 return self.resolve_projected_subfield(
77 ProjectedField {
78 field: fields.get(index)?,
79 mapping: mapping.fields.get(index)?,
80 path,
81 nesting: projected.nesting,
82 },
83 rest,
84 );
85 } else {
86 self.resolve_projected_field(&project.base)?
87 };
88 self.resolve_projected_subfield(projected, project.projection.as_slice())
89 }
90 _ => None,
91 }
92 }
93
94 fn resolve_projected_subfield(
95 &self,
96 mut projected: ProjectedField<'a>,
97 steps: &[usize],
98 ) -> Option<ProjectedField<'a>> {
99 for index in steps {
100 let app::FieldTy::Embedded(embedded) = &projected.field.ty else {
101 return None;
102 };
103 let mapping::Field::Struct(mapped) = projected.mapping else {
104 return None;
105 };
106 projected.field = self
107 .schema
108 .app
109 .model(embedded.target)
110 .fields()
111 .get(*index)?;
112 projected.mapping = mapped.fields.get(*index)?;
113 projected.path.projection.push(*index);
114 }
115 Some(projected)
116 }
117}