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