1use datafusion_physical_plan::{DisplayAs, DisplayFormatType};
19
20use crate::file_groups::FileGroup;
21use std::fmt::{Debug, Formatter, Result as FmtResult};
22
23#[derive(Debug)]
30pub(crate) struct FileGroupsDisplay<'a>(pub(crate) &'a [FileGroup]);
31
32impl DisplayAs for FileGroupsDisplay<'_> {
33 fn fmt_as(&self, t: DisplayFormatType, f: &mut Formatter) -> FmtResult {
34 let n_groups = self.0.len();
35 let groups = if n_groups == 1 { "group" } else { "groups" };
36 write!(f, "{{{n_groups} {groups}: [")?;
37 match t {
38 DisplayFormatType::Default | DisplayFormatType::TreeRender => {
39 let max_groups = 5;
41 fmt_up_to_n_elements(self.0, max_groups, f, |group, f| {
42 FileGroupDisplay(group).fmt_as(t, f)
43 })?;
44 }
45 DisplayFormatType::Verbose => {
46 fmt_elements_split_by_commas(self.0.iter(), f, |group, f| {
47 FileGroupDisplay(group).fmt_as(t, f)
48 })?
49 }
50 }
51 write!(f, "]}}")
52 }
53}
54
55#[derive(Debug)]
62pub struct FileGroupDisplay<'a>(pub &'a FileGroup);
63
64impl DisplayAs for FileGroupDisplay<'_> {
65 fn fmt_as(&self, t: DisplayFormatType, f: &mut Formatter) -> FmtResult {
66 write!(f, "[")?;
67 match t {
68 DisplayFormatType::Default | DisplayFormatType::TreeRender => {
69 let max_files = 5;
71 fmt_up_to_n_elements(self.0.files(), max_files, f, |pf, f| {
72 write!(f, "{}", pf.object_meta.location.as_ref())?;
73 if let Some(range) = pf.range.as_ref() {
74 write!(f, ":{}..{}", range.start, range.end)?;
75 }
76 Ok(())
77 })?
78 }
79 DisplayFormatType::Verbose => {
80 fmt_elements_split_by_commas(self.0.iter(), f, |pf, f| {
81 write!(f, "{}", pf.object_meta.location.as_ref())?;
82 if let Some(range) = pf.range.as_ref() {
83 write!(f, ":{}..{}", range.start, range.end)?;
84 }
85 Ok(())
86 })?
87 }
88 }
89 write!(f, "]")
90 }
91}
92
93fn fmt_up_to_n_elements<E, F>(
95 elements: &[E],
96 n: usize,
97 f: &mut Formatter,
98 format_element: F,
99) -> FmtResult
100where
101 F: Fn(&E, &mut Formatter) -> FmtResult,
102{
103 let len = elements.len();
104 fmt_elements_split_by_commas(elements.iter().take(n), f, |element, f| {
105 format_element(element, f)
106 })?;
107 if len > n {
109 write!(f, ", ...")?;
110 }
111 Ok(())
112}
113
114fn fmt_elements_split_by_commas<E, I, F>(
116 iter: I,
117 f: &mut Formatter,
118 format_element: F,
119) -> FmtResult
120where
121 I: Iterator<Item = E>,
122 F: Fn(E, &mut Formatter) -> FmtResult,
123{
124 for (idx, element) in iter.enumerate() {
125 if idx > 0 {
126 write!(f, ", ")?;
127 }
128 format_element(element, f)?;
129 }
130 Ok(())
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136
137 use datafusion_physical_plan::{DefaultDisplay, VerboseDisplay};
138 use object_store::{path::Path, ObjectMeta};
139
140 use crate::PartitionedFile;
141 use chrono::Utc;
142
143 #[test]
144 fn file_groups_display_empty() {
145 let expected = "{0 groups: []}";
146 assert_eq!(DefaultDisplay(FileGroupsDisplay(&[])).to_string(), expected);
147 }
148
149 #[test]
150 fn file_groups_display_one() {
151 let files = [FileGroup::new(vec![
152 partitioned_file("foo"),
153 partitioned_file("bar"),
154 ])];
155
156 let expected = "{1 group: [[foo, bar]]}";
157 assert_eq!(
158 DefaultDisplay(FileGroupsDisplay(&files)).to_string(),
159 expected
160 );
161 }
162
163 #[test]
164 fn file_groups_display_many_default() {
165 let files = [
166 FileGroup::new(vec![partitioned_file("foo"), partitioned_file("bar")]),
167 FileGroup::new(vec![partitioned_file("baz")]),
168 FileGroup::default(),
169 ];
170
171 let expected = "{3 groups: [[foo, bar], [baz], []]}";
172 assert_eq!(
173 DefaultDisplay(FileGroupsDisplay(&files)).to_string(),
174 expected
175 );
176 }
177
178 #[test]
179 fn file_groups_display_many_verbose() {
180 let files = [
181 FileGroup::new(vec![partitioned_file("foo"), partitioned_file("bar")]),
182 FileGroup::new(vec![partitioned_file("baz")]),
183 FileGroup::default(),
184 ];
185
186 let expected = "{3 groups: [[foo, bar], [baz], []]}";
187 assert_eq!(
188 VerboseDisplay(FileGroupsDisplay(&files)).to_string(),
189 expected
190 );
191 }
192
193 #[test]
194 fn file_groups_display_too_many_default() {
195 let files = [
196 FileGroup::new(vec![partitioned_file("foo"), partitioned_file("bar")]),
197 FileGroup::new(vec![partitioned_file("baz")]),
198 FileGroup::new(vec![partitioned_file("qux")]),
199 FileGroup::new(vec![partitioned_file("quux")]),
200 FileGroup::new(vec![partitioned_file("quuux")]),
201 FileGroup::new(vec![partitioned_file("quuuux")]),
202 FileGroup::default(),
203 ];
204
205 let expected = "{7 groups: [[foo, bar], [baz], [qux], [quux], [quuux], ...]}";
206 assert_eq!(
207 DefaultDisplay(FileGroupsDisplay(&files)).to_string(),
208 expected
209 );
210 }
211
212 #[test]
213 fn file_groups_display_too_many_verbose() {
214 let files = [
215 FileGroup::new(vec![partitioned_file("foo"), partitioned_file("bar")]),
216 FileGroup::new(vec![partitioned_file("baz")]),
217 FileGroup::new(vec![partitioned_file("qux")]),
218 FileGroup::new(vec![partitioned_file("quux")]),
219 FileGroup::new(vec![partitioned_file("quuux")]),
220 FileGroup::new(vec![partitioned_file("quuuux")]),
221 FileGroup::default(),
222 ];
223
224 let expected =
225 "{7 groups: [[foo, bar], [baz], [qux], [quux], [quuux], [quuuux], []]}";
226 assert_eq!(
227 VerboseDisplay(FileGroupsDisplay(&files)).to_string(),
228 expected
229 );
230 }
231
232 #[test]
233 fn file_group_display_many_default() {
234 let files =
235 FileGroup::new(vec![partitioned_file("foo"), partitioned_file("bar")]);
236
237 let expected = "[foo, bar]";
238 assert_eq!(
239 DefaultDisplay(FileGroupDisplay(&files)).to_string(),
240 expected
241 );
242 }
243
244 #[test]
245 fn file_group_display_too_many_default() {
246 let files = FileGroup::new(vec![
247 partitioned_file("foo"),
248 partitioned_file("bar"),
249 partitioned_file("baz"),
250 partitioned_file("qux"),
251 partitioned_file("quux"),
252 partitioned_file("quuux"),
253 ]);
254
255 let expected = "[foo, bar, baz, qux, quux, ...]";
256 assert_eq!(
257 DefaultDisplay(FileGroupDisplay(&files)).to_string(),
258 expected
259 );
260 }
261
262 #[test]
263 fn file_group_display_too_many_verbose() {
264 let files = FileGroup::new(vec![
265 partitioned_file("foo"),
266 partitioned_file("bar"),
267 partitioned_file("baz"),
268 partitioned_file("qux"),
269 partitioned_file("quux"),
270 partitioned_file("quuux"),
271 ]);
272
273 let expected = "[foo, bar, baz, qux, quux, quuux]";
274 assert_eq!(
275 VerboseDisplay(FileGroupDisplay(&files)).to_string(),
276 expected
277 );
278 }
279
280 fn partitioned_file(path: &str) -> PartitionedFile {
282 let object_meta = ObjectMeta {
283 location: Path::parse(path).unwrap(),
284 last_modified: Utc::now(),
285 size: 42,
286 e_tag: None,
287 version: None,
288 };
289
290 PartitionedFile {
291 object_meta,
292 partition_values: vec![],
293 range: None,
294 statistics: None,
295 extensions: None,
296 metadata_size_hint: None,
297 }
298 }
299}