reifydb_core/encoded/shape/
evolution.rs1use reifydb_type::value::constraint::TypeConstraint;
5
6use crate::encoded::shape::RowShape;
7
8#[derive(Debug, Clone)]
10pub enum FieldMapping {
11 Direct {
13 source_index: usize,
14 },
15 UseDefault,
17 Removed,
19}
20
21#[derive(Debug)]
26pub struct ShapeResolver {
27 source: RowShape,
29 target: RowShape,
31 mappings: Vec<FieldMapping>,
33}
34
35impl ShapeResolver {
36 pub fn new(source: RowShape, target: RowShape) -> Option<Self> {
41 if source.fingerprint() == target.fingerprint() {
43 return Some(Self {
44 mappings: (0..target.field_count())
45 .map(|i| FieldMapping::Direct {
46 source_index: i,
47 })
48 .collect(),
49 source,
50 target,
51 });
52 }
53
54 let mut mappings = Vec::with_capacity(target.field_count());
55
56 for target_field in target.fields() {
57 if let Some((shape_idx, source_field)) =
58 source.fields().iter().enumerate().find(|(_, f)| f.name == target_field.name)
59 {
60 if !Self::types_compatible(&source_field.constraint, &target_field.constraint) {
62 return None; }
64 mappings.push(FieldMapping::Direct {
65 source_index: shape_idx,
66 });
67 } else {
68 mappings.push(FieldMapping::UseDefault);
70 }
71 }
72
73 Some(Self {
74 source,
75 target,
76 mappings,
77 })
78 }
79
80 fn types_compatible(source: &TypeConstraint, target: &TypeConstraint) -> bool {
83 let shape_type = source.get_type();
84 let target_type = target.get_type();
85
86 if shape_type == target_type {
87 return true;
88 }
89
90 false
93 }
94
95 pub fn source(&self) -> &RowShape {
97 &self.source
98 }
99
100 pub fn target(&self) -> &RowShape {
102 &self.target
103 }
104
105 pub fn mappings(&self) -> &[FieldMapping] {
107 &self.mappings
108 }
109
110 pub fn is_identity(&self) -> bool {
112 self.source.fingerprint() == self.target.fingerprint()
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use reifydb_type::value::r#type::Type;
119
120 use super::*;
121 use crate::encoded::shape::RowShapeField;
122
123 #[test]
124 fn test_resolver_identity() {
125 let fields = vec![
126 RowShapeField::unconstrained("a", Type::Int4),
127 RowShapeField::unconstrained("b", Type::Utf8),
128 ];
129
130 let shape = RowShape::new(fields);
131 let resolver = ShapeResolver::new(shape.clone(), shape.clone()).unwrap();
132
133 assert!(resolver.is_identity());
134 assert_eq!(resolver.mappings().len(), 2);
135 }
136
137 #[test]
138 fn test_resolver_added_field() {
139 let source_fields = vec![RowShapeField::unconstrained("a", Type::Int4)];
140
141 let target_fields = vec![
142 RowShapeField::unconstrained("a", Type::Int4),
143 RowShapeField::unconstrained("b", Type::Utf8), ];
145
146 let source = RowShape::new(source_fields);
147 let target = RowShape::new(target_fields);
148
149 let resolver = ShapeResolver::new(source, target).unwrap();
150
151 assert!(!resolver.is_identity());
152 assert!(matches!(
153 resolver.mappings()[0],
154 FieldMapping::Direct {
155 source_index: 0
156 }
157 ));
158 assert!(matches!(resolver.mappings()[1], FieldMapping::UseDefault));
159 }
160
161 #[test]
162 fn test_resolver_incompatible_types() {
163 let source_fields = vec![RowShapeField::unconstrained("a", Type::Int4)];
164 let target_fields = vec![RowShapeField::unconstrained("a", Type::Utf8)]; let source = RowShape::new(source_fields);
167 let target = RowShape::new(target_fields);
168
169 assert!(ShapeResolver::new(source, target).is_none());
171 }
172}