1use super::{Expr, Projection};
2use crate::schema::app::{FieldId, ModelId, VariantId};
3
4#[derive(Debug, Clone, PartialEq)]
9pub enum PathRoot {
10 Model(ModelId),
12
13 Variant {
18 parent: Box<Path>,
19 variant_id: VariantId,
20 },
21}
22
23impl PathRoot {
24 pub fn expect_model(&self) -> ModelId {
26 match self {
27 PathRoot::Model(id) => *id,
28 PathRoot::Variant { .. } => panic!("expected Model root, got Variant root"),
29 }
30 }
31
32 pub fn as_model(&self) -> Option<ModelId> {
35 match self {
36 PathRoot::Model(id) => Some(*id),
37 PathRoot::Variant { .. } => None,
38 }
39 }
40}
41
42#[derive(Debug, Clone, PartialEq)]
46pub struct Path {
47 pub root: PathRoot,
49
50 pub projection: Projection,
52}
53
54impl Path {
55 pub fn model(root: impl Into<ModelId>) -> Self {
56 Self {
57 root: PathRoot::Model(root.into()),
58 projection: Projection::identity(),
59 }
60 }
61
62 pub fn field(root: impl Into<ModelId>, field: usize) -> Self {
63 Self {
64 root: PathRoot::Model(root.into()),
65 projection: Projection::single(field),
66 }
67 }
68
69 pub const fn from_index(root: ModelId, index: usize) -> Self {
70 Self {
71 root: PathRoot::Model(root),
72 projection: Projection::from_index(index),
73 }
74 }
75
76 pub fn from_variant(parent: Path, variant_id: VariantId) -> Self {
82 Self {
83 root: PathRoot::Variant {
84 parent: Box::new(parent),
85 variant_id,
86 },
87 projection: Projection::identity(),
88 }
89 }
90
91 pub fn is_empty(&self) -> bool {
92 self.projection.is_empty()
93 }
94
95 pub fn len(&self) -> usize {
96 self.projection.len()
97 }
98
99 pub fn chain(&mut self, other: &Self) {
100 for field in &other.projection[..] {
101 self.projection.push(*field);
102 }
103 }
104
105 pub fn into_stmt(self) -> Expr {
106 match self.root {
107 PathRoot::Model(model_id) => match self.projection.as_slice() {
108 [] => Expr::ref_ancestor_model(0),
109 [field, project @ ..] => {
110 let mut ret = Expr::ref_self_field(FieldId {
111 model: model_id,
112 index: *field,
113 });
114
115 if !project.is_empty() {
116 ret = Expr::project(ret, project);
117 }
118
119 ret
120 }
121 },
122 PathRoot::Variant { parent, .. } => {
123 let parent_expr = parent.into_stmt();
124 match self.projection.as_slice() {
125 [] => parent_expr,
126 [local_idx, rest @ ..] => {
127 let mut ret = Expr::project(parent_expr, Projection::single(local_idx + 1));
130
131 if !rest.is_empty() {
132 ret = Expr::project(ret, rest);
133 }
134
135 ret
136 }
137 }
138 }
139 }
140 }
141}