parquet/schema/
visitor.rs1use crate::basic::{ConvertedType, Repetition};
8use crate::errors::ParquetError::General;
9use crate::errors::Result;
10use crate::schema::types::{Type, TypePtr};
11
12pub trait TypeVisitor<R, C> {
14 fn visit_primitive(&mut self, primitive_type: TypePtr, context: C) -> Result<R>;
16
17 fn visit_list(&mut self, list_type: TypePtr, context: C) -> Result<R> {
39 match list_type.as_ref() {
40 Type::PrimitiveType { .. } => {
41 panic!("{list_type:?} is a list type and must be a group type")
42 }
43 Type::GroupType {
44 basic_info: _,
45 fields,
46 } if fields.len() == 1 => {
47 let list_item = fields.first().unwrap();
48
49 match list_item.as_ref() {
50 Type::PrimitiveType { .. } => {
51 if list_item.get_basic_info().repetition() == Repetition::REPEATED {
52 self.visit_list_with_item(list_type.clone(), list_item.clone(), context)
53 } else {
54 Err(General(
55 "Primitive element type of list must be repeated.".to_string(),
56 ))
57 }
58 }
59 Type::GroupType {
60 basic_info: _,
61 fields,
62 } => {
63 if fields.len() == 1
64 && list_item.name() != "array"
65 && list_item.name() != format!("{}_tuple", list_type.name())
66 {
67 self.visit_list_with_item(
68 list_type.clone(),
69 fields.first().unwrap().clone(),
70 context,
71 )
72 } else {
73 self.visit_list_with_item(list_type.clone(), list_item.clone(), context)
74 }
75 }
76 }
77 }
78 Type::GroupType { .. } => Err(General(
79 "Group element type of list can only contain one field.".to_string(),
80 )),
81 }
82 }
83
84 fn visit_struct(&mut self, struct_type: TypePtr, context: C) -> Result<R>;
86
87 fn visit_map(&mut self, map_type: TypePtr, context: C) -> Result<R>;
89
90 fn dispatch(&mut self, cur_type: TypePtr, context: C) -> Result<R> {
92 if cur_type.is_primitive() {
93 self.visit_primitive(cur_type, context)
94 } else {
95 match cur_type.get_basic_info().converted_type() {
96 ConvertedType::LIST => self.visit_list(cur_type, context),
97 ConvertedType::MAP | ConvertedType::MAP_KEY_VALUE => {
98 self.visit_map(cur_type, context)
99 }
100 _ => self.visit_struct(cur_type, context),
101 }
102 }
103 }
104
105 fn visit_list_with_item(
107 &mut self,
108 list_type: TypePtr,
109 item_type: TypePtr,
110 context: C,
111 ) -> Result<R>;
112}
113
114#[cfg(test)]
115mod tests {
116 use super::TypeVisitor;
117 use crate::basic::Type as PhysicalType;
118 use crate::errors::Result;
119 use crate::schema::parser::parse_message_type;
120 use crate::schema::types::TypePtr;
121 use std::sync::Arc;
122
123 struct TestVisitorContext {}
124 struct TestVisitor {
125 primitive_visited: bool,
126 struct_visited: bool,
127 list_visited: bool,
128 root_type: TypePtr,
129 }
130
131 impl TypeVisitor<bool, TestVisitorContext> for TestVisitor {
132 fn visit_primitive(
133 &mut self,
134 primitive_type: TypePtr,
135 _context: TestVisitorContext,
136 ) -> Result<bool> {
137 assert_eq!(
138 self.get_field_by_name(primitive_type.name()).as_ref(),
139 primitive_type.as_ref()
140 );
141 self.primitive_visited = true;
142 Ok(true)
143 }
144
145 fn visit_struct(
146 &mut self,
147 struct_type: TypePtr,
148 _context: TestVisitorContext,
149 ) -> Result<bool> {
150 assert_eq!(
151 self.get_field_by_name(struct_type.name()).as_ref(),
152 struct_type.as_ref()
153 );
154 self.struct_visited = true;
155 Ok(true)
156 }
157
158 fn visit_map(&mut self, _map_type: TypePtr, _context: TestVisitorContext) -> Result<bool> {
159 unimplemented!()
160 }
161
162 fn visit_list_with_item(
163 &mut self,
164 list_type: TypePtr,
165 item_type: TypePtr,
166 _context: TestVisitorContext,
167 ) -> Result<bool> {
168 assert_eq!(
169 self.get_field_by_name(list_type.name()).as_ref(),
170 list_type.as_ref()
171 );
172 assert_eq!("element", item_type.name());
173 assert_eq!(PhysicalType::INT32, item_type.get_physical_type());
174 self.list_visited = true;
175 Ok(true)
176 }
177 }
178
179 impl TestVisitor {
180 fn new(root: TypePtr) -> Self {
181 Self {
182 primitive_visited: false,
183 struct_visited: false,
184 list_visited: false,
185 root_type: root,
186 }
187 }
188
189 fn get_field_by_name(&self, name: &str) -> TypePtr {
190 self.root_type
191 .get_fields()
192 .iter()
193 .find(|t| t.name() == name)
194 .cloned()
195 .unwrap()
196 }
197 }
198
199 #[test]
200 fn test_visitor() {
201 let message_type = "
202 message spark_schema {
203 REQUIRED INT32 a;
204 OPTIONAL group inner_schema {
205 REQUIRED INT32 b;
206 REQUIRED DOUBLE c;
207 }
208
209 OPTIONAL group e (LIST) {
210 REPEATED group list {
211 REQUIRED INT32 element;
212 }
213 }
214 ";
215
216 let parquet_type = Arc::new(parse_message_type(message_type).unwrap());
217
218 let mut visitor = TestVisitor::new(parquet_type.clone());
219 for f in parquet_type.get_fields() {
220 let c = TestVisitorContext {};
221 assert!(visitor.dispatch(f.clone(), c).unwrap());
222 }
223
224 assert!(visitor.struct_visited);
225 assert!(visitor.primitive_visited);
226 assert!(visitor.list_visited);
227 }
228}