1use anyhow::{anyhow, Result};
2use fuzzy_matcher::skim::SkimMatcherV2;
3use fuzzy_matcher::FuzzyMatcher;
4use serde_json::{json, Value};
5use std::collections::BTreeMap;
6use std::sync::Arc;
7use tracing::{debug, info};
8
9use crate::data::data_provider::DataProvider;
10use crate::data::datatable::{DataRow, DataTable, DataValue};
11use crate::data::datavalue_compare::{compare_datavalues, compare_optional_datavalues};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum SortOrder {
16 Ascending,
17 Descending,
18 None,
19}
20
21#[derive(Debug, Clone)]
23pub struct SortState {
24 pub column: Option<usize>,
26 pub order: SortOrder,
28}
29
30#[derive(Debug, Clone, PartialEq)]
32pub enum VirtualColumnPosition {
33 Left,
35 Right,
37 Index(usize),
39}
40
41#[derive(Clone)]
43pub struct VirtualColumn {
44 pub name: String,
46 pub generator: Arc<dyn Fn(usize) -> String + Send + Sync>,
48 pub width: Option<usize>,
50 pub position: VirtualColumnPosition,
52}
53
54impl std::fmt::Debug for VirtualColumn {
55 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56 f.debug_struct("VirtualColumn")
57 .field("name", &self.name)
58 .field("width", &self.width)
59 .field("position", &self.position)
60 .finish()
61 }
62}
63
64impl Default for SortState {
65 fn default() -> Self {
66 Self {
67 column: None,
68 order: SortOrder::None,
69 }
70 }
71}
72
73#[derive(Debug, Clone, PartialEq)]
75pub struct GroupKey(pub Vec<DataValue>);
76
77impl Eq for GroupKey {}
79
80impl PartialOrd for GroupKey {
82 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
83 Some(self.cmp(other))
84 }
85}
86
87impl Ord for GroupKey {
88 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
89 for (a, b) in self.0.iter().zip(&other.0) {
91 let ordering = compare_datavalues(a, b);
92 if ordering != std::cmp::Ordering::Equal {
93 return ordering;
94 }
95 }
96 self.0.len().cmp(&other.0.len())
98 }
99}
100
101#[derive(Clone)]
104pub struct DataView {
105 source: Arc<DataTable>,
107
108 visible_rows: Vec<usize>,
110
111 visible_columns: Vec<usize>,
113
114 limit: Option<usize>,
116 offset: usize,
117
118 base_rows: Vec<usize>,
121
122 base_columns: Vec<usize>,
125
126 filter_pattern: Option<String>,
128
129 fuzzy_filter_pattern: Option<String>,
131
132 column_search_pattern: Option<String>,
134 matching_columns: Vec<(usize, String)>,
136 current_column_match: usize,
138
139 pinned_columns: Vec<usize>,
141 max_pinned_columns: usize,
143
144 sort_state: SortState,
146
147 virtual_columns: Vec<VirtualColumn>,
149}
150
151impl DataView {
152 #[must_use]
154 pub fn new(source: Arc<DataTable>) -> Self {
155 let row_count = source.row_count();
156 let col_count = source.column_count();
157 let all_rows: Vec<usize> = (0..row_count).collect();
158 let all_columns: Vec<usize> = (0..col_count).collect();
159
160 Self {
161 source,
162 visible_rows: all_rows.clone(),
163 visible_columns: all_columns.clone(),
164 limit: None,
165 offset: 0,
166 base_rows: all_rows,
167 base_columns: all_columns,
168 filter_pattern: None,
169 fuzzy_filter_pattern: None,
170 column_search_pattern: None,
171 matching_columns: Vec::new(),
172 current_column_match: 0,
173 pinned_columns: Vec::new(),
174 max_pinned_columns: 4,
175 sort_state: SortState::default(),
176 virtual_columns: Vec::new(),
177 }
178 }
179
180 #[must_use]
182 pub fn with_columns(mut self, columns: Vec<usize>) -> Self {
183 self.visible_columns = columns.clone();
184 self.base_columns = columns; self
186 }
187
188 pub fn hide_column(&mut self, display_index: usize) -> bool {
190 if let Some(&source_column_index) = self.visible_columns.get(display_index) {
192 if self.pinned_columns.contains(&source_column_index) {
194 return false;
195 }
196
197 self.visible_columns.remove(display_index);
199 true
200 } else {
201 false
202 }
203 }
204
205 pub fn hide_column_by_name(&mut self, column_name: &str) -> bool {
207 if let Some(source_idx) = self.source.get_column_index(column_name) {
208 if let Some(display_idx) = self
210 .visible_columns
211 .iter()
212 .position(|&idx| idx == source_idx)
213 {
214 self.hide_column(display_idx)
215 } else {
216 false }
218 } else {
219 false
220 }
221 }
222
223 pub fn detect_empty_columns(&self) -> Vec<usize> {
230 let mut empty_columns = Vec::new();
231
232 let column_names = self.source.column_names();
234
235 for &col_idx in &self.visible_columns {
237 let column_name = column_names
238 .get(col_idx)
239 .map_or("unknown", std::string::String::as_str);
240 let mut is_empty = true;
241 let mut sample_values = Vec::new();
242
243 for &row_idx in self.visible_rows.iter() {
252 if let Some(value) = self.source.get_value(row_idx, col_idx) {
253 if sample_values.len() < 3 {
255 sample_values.push(format!("{value:?}"));
256 }
257
258 match value {
259 DataValue::Null => continue,
260 DataValue::String(s) if s.is_empty() => continue,
261 DataValue::String(s) if s.trim().is_empty() => continue, DataValue::String(s) if s.eq_ignore_ascii_case("null") => continue, DataValue::String(s) if s == "NULL" => continue, DataValue::String(s) if s == "nil" => continue, DataValue::String(s) if s == "undefined" => continue, _ => {
267 is_empty = false;
269 break; }
271 }
272 }
273 }
274
275 if is_empty {
276 tracing::debug!(
277 "Column '{}' (idx {}) detected as empty across all {} visible rows. Sample values: {:?}",
278 column_name,
279 col_idx,
280 self.visible_rows.len(),
281 sample_values
282 );
283 empty_columns.push(col_idx);
284 } else {
285 tracing::debug!(
286 "Column '{}' (idx {}) has non-empty values. Sample values: {:?}",
287 column_name,
288 col_idx,
289 sample_values
290 );
291 }
292 }
293
294 tracing::info!(
295 "Detected {} empty columns out of {} visible columns (checked {} rows)",
296 empty_columns.len(),
297 self.visible_columns.len(),
298 self.visible_rows.len()
299 );
300 empty_columns
301 }
302
303 pub fn hide_empty_columns(&mut self) -> usize {
306 let empty_columns = self.detect_empty_columns();
307 let count = empty_columns.len();
308
309 let column_names = self.source.column_names();
313 for col_idx in empty_columns {
314 if let Some(column_name) = column_names.get(col_idx) {
315 tracing::debug!(
316 "Hiding empty column '{}' (source index {})",
317 column_name,
318 col_idx
319 );
320 self.hide_column_by_name(column_name);
321 }
322 }
323
324 count
325 }
326
327 pub fn unhide_all_columns(&mut self) {
330 self.visible_columns = self.base_columns.clone();
331 }
332
333 pub fn hide_all_columns(&mut self) {
335 self.visible_columns.clear();
336 }
337
338 #[must_use]
340 pub fn has_visible_columns(&self) -> bool {
341 !self.visible_columns.is_empty()
342 }
343
344 pub fn move_column_left(&mut self, display_column_index: usize) -> bool {
347 if display_column_index >= self.visible_columns.len() {
348 return false;
349 }
350
351 let pinned_count = self.pinned_columns.len();
352
353 if display_column_index < pinned_count {
355 if display_column_index == 0 {
357 if pinned_count > 1 {
359 let col = self.pinned_columns.remove(0);
360 self.pinned_columns.push(col);
361 self.rebuild_visible_columns();
362 }
363 } else {
364 self.pinned_columns
366 .swap(display_column_index - 1, display_column_index);
367 self.rebuild_visible_columns();
368 }
369 return true;
370 }
371
372 if display_column_index == pinned_count {
374 let col = self.visible_columns.remove(display_column_index);
376 self.visible_columns.push(col);
377 } else {
378 self.visible_columns
380 .swap(display_column_index - 1, display_column_index);
381 }
382 true
383 }
384
385 pub fn move_column_right(&mut self, display_column_index: usize) -> bool {
388 if display_column_index >= self.visible_columns.len() {
389 return false;
390 }
391
392 let pinned_count = self.pinned_columns.len();
393
394 if display_column_index < pinned_count {
396 if display_column_index == pinned_count - 1 {
398 if pinned_count > 1 {
400 let col = self.pinned_columns.pop().unwrap();
401 self.pinned_columns.insert(0, col);
402 self.rebuild_visible_columns();
403 }
404 } else {
405 self.pinned_columns
407 .swap(display_column_index, display_column_index + 1);
408 self.rebuild_visible_columns();
409 }
410 return true;
411 }
412
413 if display_column_index == self.visible_columns.len() - 1 {
415 let col = self.visible_columns.pop().unwrap();
417 self.visible_columns.insert(pinned_count, col);
418 } else {
419 self.visible_columns
421 .swap(display_column_index, display_column_index + 1);
422 }
423 true
424 }
425
426 pub fn move_column_left_by_name(&mut self, column_name: &str) -> bool {
428 if let Some(source_idx) = self.source.get_column_index(column_name) {
429 if let Some(visible_idx) = self
430 .visible_columns
431 .iter()
432 .position(|&idx| idx == source_idx)
433 {
434 return self.move_column_left(visible_idx);
435 }
436 }
437 false
438 }
439
440 pub fn move_column_right_by_name(&mut self, column_name: &str) -> bool {
442 if let Some(source_idx) = self.source.get_column_index(column_name) {
443 if let Some(visible_idx) = self
444 .visible_columns
445 .iter()
446 .position(|&idx| idx == source_idx)
447 {
448 return self.move_column_right(visible_idx);
449 }
450 }
451 false
452 }
453
454 #[must_use]
456 pub fn get_hidden_column_names(&self) -> Vec<String> {
457 let all_columns = self.source.column_names();
458 let visible_columns = self.column_names();
459
460 all_columns
461 .into_iter()
462 .filter(|col| !visible_columns.contains(col))
463 .collect()
464 }
465
466 #[must_use]
468 pub fn has_hidden_columns(&self) -> bool {
469 self.visible_columns.len() < self.source.column_count()
470 }
471
472 pub fn pin_column(&mut self, display_index: usize) -> Result<()> {
476 let source_column_index = if let Some(&idx) = self.visible_columns.get(display_index) {
478 idx
479 } else {
480 return Err(anyhow::anyhow!(
481 "Display index {} out of bounds",
482 display_index
483 ));
484 };
485
486 if self.pinned_columns.len() >= self.max_pinned_columns {
488 return Err(anyhow::anyhow!(
489 "Maximum {} pinned columns allowed",
490 self.max_pinned_columns
491 ));
492 }
493
494 if self.pinned_columns.contains(&source_column_index) {
496 return Ok(()); }
498
499 self.pinned_columns.push(source_column_index);
501
502 self.rebuild_visible_columns();
504
505 Ok(())
506 }
507
508 fn rebuild_visible_columns(&mut self) {
511 let mut new_visible_columns = Vec::new();
512
513 for &pinned_idx in &self.pinned_columns {
515 new_visible_columns.push(pinned_idx);
516 }
517
518 for col_idx in 0..self.source.column_count() {
520 if !self.pinned_columns.contains(&col_idx) {
521 new_visible_columns.push(col_idx);
522 }
523 }
524
525 self.visible_columns = new_visible_columns;
526 }
527
528 pub fn pin_column_by_name(&mut self, column_name: &str) -> Result<()> {
530 if let Some(source_idx) = self.source.get_column_index(column_name) {
531 if let Some(display_idx) = self
533 .visible_columns
534 .iter()
535 .position(|&idx| idx == source_idx)
536 {
537 self.pin_column(display_idx)
538 } else {
539 Err(anyhow::anyhow!("Column '{}' not visible", column_name))
540 }
541 } else {
542 Err(anyhow::anyhow!("Column '{}' not found", column_name))
543 }
544 }
545
546 pub fn unpin_column(&mut self, display_index: usize) -> bool {
548 if let Some(&source_column_index) = self.visible_columns.get(display_index) {
550 if let Some(pos) = self
551 .pinned_columns
552 .iter()
553 .position(|&idx| idx == source_column_index)
554 {
555 self.pinned_columns.remove(pos);
556
557 self.rebuild_visible_columns();
559
560 true
561 } else {
562 false }
564 } else {
565 false }
567 }
568
569 pub fn unpin_column_by_name(&mut self, column_name: &str) -> bool {
571 if let Some(source_idx) = self.source.get_column_index(column_name) {
572 if let Some(display_idx) = self
574 .visible_columns
575 .iter()
576 .position(|&idx| idx == source_idx)
577 {
578 self.unpin_column(display_idx)
579 } else {
580 false }
582 } else {
583 false
584 }
585 }
586
587 pub fn clear_pinned_columns(&mut self) {
589 for col_idx in self.pinned_columns.drain(..) {
591 if !self.visible_columns.contains(&col_idx) {
592 self.visible_columns.push(col_idx);
593 }
594 }
595 }
596
597 #[must_use]
599 pub fn is_column_pinned(&self, display_index: usize) -> bool {
600 if let Some(&source_column_index) = self.visible_columns.get(display_index) {
601 self.pinned_columns.contains(&source_column_index)
602 } else {
603 false
604 }
605 }
606
607 #[must_use]
609 pub fn get_pinned_columns(&self) -> &[usize] {
610 &self.pinned_columns
611 }
612
613 #[must_use]
615 pub fn get_pinned_column_names(&self) -> Vec<String> {
616 let all_columns = self.source.column_names();
617 self.pinned_columns
618 .iter()
619 .filter_map(|&idx| all_columns.get(idx).cloned())
620 .collect()
621 }
622
623 #[must_use]
625 pub fn get_display_columns(&self) -> Vec<usize> {
626 self.visible_columns.clone()
629 }
630
631 #[must_use]
633 pub fn get_display_column_names(&self) -> Vec<String> {
634 let all_columns = self.source.column_names();
635 self.get_display_columns()
636 .iter()
637 .filter_map(|&idx| all_columns.get(idx).cloned())
638 .collect()
639 }
640
641 pub fn set_max_pinned_columns(&mut self, max: usize) {
643 self.max_pinned_columns = max;
644 while self.pinned_columns.len() > max {
646 if let Some(col_idx) = self.pinned_columns.pop() {
647 self.visible_columns.insert(0, col_idx);
648 }
649 }
650 }
651
652 #[must_use]
654 pub fn with_rows(mut self, rows: Vec<usize>) -> Self {
655 self.visible_rows = rows.clone();
656 self.base_rows = rows; self
658 }
659
660 #[must_use]
662 pub fn with_limit(mut self, limit: usize, offset: usize) -> Self {
663 self.limit = Some(limit);
664 self.offset = offset;
665 self
666 }
667
668 pub fn filter<F>(mut self, predicate: F) -> Self
670 where
671 F: Fn(&DataTable, usize) -> bool,
672 {
673 self.visible_rows
674 .retain(|&row_idx| predicate(&self.source, row_idx));
675 self.base_rows = self.visible_rows.clone();
677 self
678 }
679
680 pub fn apply_text_filter(&mut self, pattern: &str, case_sensitive: bool) {
682 info!(
683 "DataView::apply_text_filter - pattern='{}', case_sensitive={}, thread={:?}",
684 pattern,
685 case_sensitive,
686 std::thread::current().id()
687 );
688
689 if pattern.is_empty() {
690 info!("DataView::apply_text_filter - empty pattern, clearing filter");
691 self.clear_filter();
692 return;
693 }
694
695 if self.fuzzy_filter_pattern.is_some() {
697 info!("DataView::apply_text_filter - clearing existing fuzzy filter");
698 self.fuzzy_filter_pattern = None;
699 }
700
701 self.filter_pattern = Some(pattern.to_string());
703
704 let pattern_lower = if case_sensitive {
706 pattern.to_string()
707 } else {
708 pattern.to_lowercase()
709 };
710
711 info!(
712 "DataView::apply_text_filter - searching for '{}' in {} base rows",
713 pattern_lower,
714 self.base_rows.len()
715 );
716
717 let mut matched_count = 0;
718 let mut checked_count = 0;
719
720 self.visible_rows = self
721 .base_rows
722 .iter()
723 .copied()
724 .filter(|&row_idx| {
725 checked_count += 1;
726
727 if let Some(row) = self.source.get_row(row_idx) {
729 if checked_count <= 3 {
731 let preview = row
732 .values
733 .iter()
734 .take(5)
735 .map(std::string::ToString::to_string)
736 .collect::<Vec<_>>()
737 .join(", ");
738 info!(
739 "DataView::apply_text_filter - row {} preview: {}",
740 row_idx, preview
741 );
742 }
743
744 for value in &row.values {
745 let text = value.to_string();
746 let text_to_match = if case_sensitive {
747 text.clone()
748 } else {
749 text.to_lowercase()
750 };
751 if text_to_match.contains(&pattern_lower) {
752 matched_count += 1;
753 if checked_count <= 3 {
754 info!(
755 "DataView::apply_text_filter - MATCH in row {} cell: '{}'",
756 row_idx, text
757 );
758 }
759 return true;
760 }
761 }
762 }
763 false
764 })
765 .collect();
766
767 info!(
768 "DataView::apply_text_filter - checked {} rows, matched {} rows",
769 checked_count, matched_count
770 );
771 info!(
772 "DataView::apply_text_filter - final visible rows: {}",
773 self.visible_rows.len()
774 );
775
776 if let Some(sort_column) = self.sort_state.column {
778 if let Some(source_index) = self.visible_columns.get(sort_column) {
779 let ascending = matches!(self.sort_state.order, SortOrder::Ascending);
780 info!(
781 "DataView::apply_text_filter - reapplying sort on column {} (ascending={})",
782 sort_column, ascending
783 );
784 let _ = self.apply_sort_internal(*source_index, ascending);
785 }
786 }
787 }
788
789 pub fn clear_filter(&mut self) {
791 self.filter_pattern = None;
792 self.fuzzy_filter_pattern = None;
793 self.visible_rows = self.base_rows.clone();
794
795 if let Some(sort_column) = self.sort_state.column {
797 if let Some(source_index) = self.visible_columns.get(sort_column) {
798 let ascending = matches!(self.sort_state.order, SortOrder::Ascending);
799 let _ = self.apply_sort_internal(*source_index, ascending);
800 }
801 }
802 }
803
804 #[must_use]
806 pub fn has_filter(&self) -> bool {
807 self.filter_pattern.is_some() || self.fuzzy_filter_pattern.is_some()
808 }
809
810 #[must_use]
812 pub fn get_filter_pattern(&self) -> Option<&str> {
813 self.filter_pattern.as_deref()
814 }
815
816 #[must_use]
818 pub fn get_fuzzy_filter_pattern(&self) -> Option<&str> {
819 self.fuzzy_filter_pattern.as_deref()
820 }
821
822 pub fn apply_fuzzy_filter(&mut self, pattern: &str, case_insensitive: bool) {
825 info!(
826 "DataView::apply_fuzzy_filter - pattern='{}', case_insensitive={}, thread={:?}",
827 pattern,
828 case_insensitive,
829 std::thread::current().id()
830 );
831
832 if pattern.is_empty() {
833 info!("DataView::apply_fuzzy_filter - empty pattern, clearing filter");
834 self.clear_filter();
835 return;
836 }
837
838 if self.filter_pattern.is_some() {
840 info!("DataView::apply_fuzzy_filter - clearing existing text filter");
841 self.filter_pattern = None;
842 }
843
844 self.fuzzy_filter_pattern = Some(pattern.to_string());
846
847 let use_exact = pattern.starts_with('\'');
849
850 self.visible_rows = self
851 .base_rows
852 .iter()
853 .copied()
854 .filter(|&row_idx| {
855 if let Some(row) = self.source.get_row(row_idx) {
857 let row_text = row
859 .values
860 .iter()
861 .map(std::string::ToString::to_string)
862 .collect::<Vec<_>>()
863 .join(" ");
864
865 if use_exact && pattern.len() > 1 {
866 let exact_pattern = &pattern[1..];
868 if case_insensitive {
869 row_text
870 .to_lowercase()
871 .contains(&exact_pattern.to_lowercase())
872 } else {
873 row_text.contains(exact_pattern)
874 }
875 } else if !use_exact {
876 let matcher = if case_insensitive {
878 SkimMatcherV2::default().ignore_case()
879 } else {
880 SkimMatcherV2::default().respect_case()
881 };
882
883 matcher
885 .fuzzy_match(&row_text, pattern)
886 .is_some_and(|score| score > 0)
887 } else {
888 false
890 }
891 } else {
892 false
893 }
894 })
895 .collect();
896
897 if let Some(sort_column) = self.sort_state.column {
899 if let Some(source_index) = self.visible_columns.get(sort_column) {
900 let ascending = matches!(self.sort_state.order, SortOrder::Ascending);
901 info!(
902 "DataView::apply_fuzzy_filter - reapplying sort on column {} (ascending={})",
903 sort_column, ascending
904 );
905 let _ = self.apply_sort_internal(*source_index, ascending);
906 }
907 }
908 }
909
910 #[must_use]
912 pub fn get_fuzzy_filter_indices(&self) -> Vec<usize> {
913 self.visible_rows.clone()
915 }
916
917 #[must_use]
919 pub fn get_visible_rows(&self) -> Vec<usize> {
920 self.visible_rows.clone()
921 }
922
923 pub fn group_by(&self, group_columns: &[String]) -> Result<BTreeMap<GroupKey, DataView>> {
926 let mut groups = BTreeMap::new();
927
928 let col_indices: Vec<usize> = group_columns
930 .iter()
931 .map(|col_name| {
932 self.source
933 .get_column_index(col_name)
934 .ok_or_else(|| anyhow!("Column '{}' not found for GROUP BY", col_name))
935 })
936 .collect::<Result<Vec<_>>>()?;
937
938 let mut group_rows: BTreeMap<GroupKey, Vec<usize>> = BTreeMap::new();
940
941 for &row_idx in &self.visible_rows {
943 let mut key_values = Vec::new();
945 for &col_idx in &col_indices {
946 let value = self
947 .source
948 .get_value(row_idx, col_idx)
949 .cloned()
950 .unwrap_or(DataValue::Null);
951 key_values.push(value);
952 }
953 let key = GroupKey(key_values);
954
955 group_rows.entry(key).or_default().push(row_idx);
957 }
958
959 for (key, rows) in group_rows {
961 let mut group_view = DataView::new(Arc::clone(&self.source));
962 group_view.visible_rows = rows;
964 group_view.visible_columns = self.visible_columns.clone();
966 group_view.base_rows = group_view.visible_rows.clone();
967 group_view.base_columns = group_view.visible_columns.clone();
968
969 groups.insert(key, group_view);
970 }
971
972 Ok(groups)
973 }
974
975 pub fn sort_by(mut self, column_index: usize, ascending: bool) -> Result<Self> {
978 self.apply_sort(column_index, ascending)?;
979 Ok(self)
980 }
981
982 pub fn apply_sort(&mut self, column_index: usize, ascending: bool) -> Result<()> {
985 let source_column_index = if column_index < self.visible_columns.len() {
987 self.visible_columns[column_index]
988 } else {
989 return Err(anyhow::anyhow!(
990 "Column index {} out of bounds (visible columns: {})",
991 column_index,
992 self.visible_columns.len()
993 ));
994 };
995
996 self.apply_sort_internal(source_column_index, ascending)?;
998
999 self.sort_state.column = Some(column_index);
1001 self.sort_state.order = if ascending {
1002 SortOrder::Ascending
1003 } else {
1004 SortOrder::Descending
1005 };
1006
1007 Ok(())
1008 }
1009
1010 fn apply_sort_internal(&mut self, source_column_index: usize, ascending: bool) -> Result<()> {
1012 if source_column_index >= self.source.column_count() {
1013 return Err(anyhow::anyhow!(
1014 "Source column index {} out of bounds",
1015 source_column_index
1016 ));
1017 }
1018
1019 let source = &self.source;
1020 self.visible_rows.sort_by(|&a, &b| {
1021 let val_a = source.get_value(a, source_column_index);
1022 let val_b = source.get_value(b, source_column_index);
1023
1024 let cmp = compare_optional_datavalues(val_a, val_b);
1025
1026 if ascending {
1027 cmp
1028 } else {
1029 cmp.reverse()
1030 }
1031 });
1032
1033 Ok(())
1037 }
1038
1039 pub fn apply_multi_sort(&mut self, sort_columns: &[(usize, bool)]) -> Result<()> {
1042 if sort_columns.is_empty() {
1043 return Ok(());
1044 }
1045
1046 for (col_idx, _) in sort_columns {
1048 if *col_idx >= self.source.column_count() {
1049 return Err(anyhow::anyhow!(
1050 "Source column index {} out of bounds",
1051 col_idx
1052 ));
1053 }
1054 }
1055
1056 let source = &self.source;
1057 self.visible_rows.sort_by(|&a, &b| {
1058 for (col_idx, ascending) in sort_columns {
1060 let val_a = source.get_value(a, *col_idx);
1061 let val_b = source.get_value(b, *col_idx);
1062
1063 let cmp = compare_optional_datavalues(val_a, val_b);
1064
1065 if cmp != std::cmp::Ordering::Equal {
1067 return if *ascending { cmp } else { cmp.reverse() };
1068 }
1069 }
1071
1072 std::cmp::Ordering::Equal
1074 });
1075
1076 if let Some((primary_col, ascending)) = sort_columns.first() {
1078 if let Some(visible_idx) = self.visible_columns.iter().position(|&x| x == *primary_col)
1080 {
1081 self.sort_state.column = Some(visible_idx);
1082 self.sort_state.order = if *ascending {
1083 SortOrder::Ascending
1084 } else {
1085 SortOrder::Descending
1086 };
1087 }
1088 }
1089
1090 Ok(())
1091 }
1092
1093 pub fn toggle_sort(&mut self, column_index: usize) -> Result<()> {
1096 let source_column_index = if column_index < self.visible_columns.len() {
1098 self.visible_columns[column_index]
1099 } else {
1100 return Err(anyhow::anyhow!(
1101 "Column index {} out of bounds (visible columns: {})",
1102 column_index,
1103 self.visible_columns.len()
1104 ));
1105 };
1106
1107 let next_order = if self.sort_state.column == Some(column_index) {
1109 match self.sort_state.order {
1111 SortOrder::None => SortOrder::Ascending,
1112 SortOrder::Ascending => SortOrder::Descending,
1113 SortOrder::Descending => SortOrder::None,
1114 }
1115 } else {
1116 SortOrder::Ascending
1118 };
1119
1120 match next_order {
1122 SortOrder::Ascending => {
1123 self.apply_sort_internal(source_column_index, true)?;
1124 self.sort_state.column = Some(column_index);
1126 self.sort_state.order = SortOrder::Ascending;
1127 }
1128 SortOrder::Descending => {
1129 self.apply_sort_internal(source_column_index, false)?;
1130 self.sort_state.column = Some(column_index);
1131 self.sort_state.order = SortOrder::Descending;
1132 }
1133 SortOrder::None => {
1134 self.sort_state.column = None;
1135 self.sort_state.order = SortOrder::None;
1136 self.clear_sort();
1137 }
1138 }
1139
1140 Ok(())
1141 }
1142
1143 #[must_use]
1145 pub fn get_sort_state(&self) -> &SortState {
1146 &self.sort_state
1147 }
1148
1149 #[must_use]
1152 pub fn get_visible_column_indices(&self) -> Vec<usize> {
1153 self.visible_columns.clone()
1154 }
1155
1156 pub fn clear_sort(&mut self) {
1158 self.sort_state.column = None;
1160 self.sort_state.order = SortOrder::None;
1161
1162 self.visible_rows = self.base_rows.clone();
1165
1166 if let Some(pattern) = self.filter_pattern.clone() {
1168 let case_insensitive = false; self.apply_text_filter(&pattern, case_insensitive);
1170 }
1171 }
1172
1173 pub fn add_virtual_column(&mut self, virtual_column: VirtualColumn) {
1177 self.virtual_columns.push(virtual_column);
1178 }
1179
1180 pub fn add_row_numbers(&mut self, position: VirtualColumnPosition) {
1182 let row_num_column = VirtualColumn {
1183 name: "#".to_string(),
1184 generator: Arc::new(|row_index| format!("{}", row_index + 1)),
1185 width: Some(4), position,
1187 };
1188 self.add_virtual_column(row_num_column);
1189 }
1190
1191 pub fn remove_virtual_columns(&mut self, name: &str) {
1193 self.virtual_columns.retain(|col| col.name != name);
1194 }
1195
1196 pub fn toggle_row_numbers(&mut self) {
1198 if self.virtual_columns.iter().any(|col| col.name == "#") {
1199 self.remove_virtual_columns("#");
1200 } else {
1201 self.add_row_numbers(VirtualColumnPosition::Left);
1202 }
1203 }
1204
1205 #[must_use]
1207 pub fn has_row_numbers(&self) -> bool {
1208 self.virtual_columns.iter().any(|col| col.name == "#")
1209 }
1210
1211 #[must_use]
1213 pub fn get_all_column_names(&self) -> Vec<String> {
1214 let mut result = Vec::new();
1215 let all_source_names = self.source.column_names();
1216 let real_column_names: Vec<String> = self
1218 .get_display_columns()
1219 .iter()
1220 .map(|&i| {
1221 all_source_names
1222 .get(i)
1223 .cloned()
1224 .unwrap_or_else(|| format!("col_{i}"))
1225 })
1226 .collect();
1227
1228 let mut virtual_left = Vec::new();
1230 let mut virtual_right = Vec::new();
1231 let mut virtual_indexed = Vec::new();
1232
1233 for vcol in &self.virtual_columns {
1234 match vcol.position {
1235 VirtualColumnPosition::Left => virtual_left.push(vcol.name.clone()),
1236 VirtualColumnPosition::Right => virtual_right.push(vcol.name.clone()),
1237 VirtualColumnPosition::Index(idx) => virtual_indexed.push((idx, vcol.name.clone())),
1238 }
1239 }
1240
1241 result.extend(virtual_left);
1243
1244 for (i, real_name) in real_column_names.into_iter().enumerate() {
1246 for (idx, vname) in &virtual_indexed {
1248 if *idx == i {
1249 result.push(vname.clone());
1250 }
1251 }
1252 result.push(real_name);
1253 }
1254
1255 result.extend(virtual_right);
1257
1258 result
1259 }
1260
1261 #[must_use]
1263 pub fn row_count(&self) -> usize {
1264 let count = self.visible_rows.len();
1265
1266 if let Some(limit) = self.limit {
1268 let available = count.saturating_sub(self.offset);
1269 available.min(limit)
1270 } else {
1271 count.saturating_sub(self.offset)
1272 }
1273 }
1274
1275 #[must_use]
1277 pub fn column_count(&self) -> usize {
1278 self.visible_columns.len() + self.virtual_columns.len()
1280 }
1281
1282 #[must_use]
1284 pub fn column_names(&self) -> Vec<String> {
1285 self.get_all_column_names()
1286 }
1287
1288 #[must_use]
1290 pub fn get_row(&self, index: usize) -> Option<DataRow> {
1291 let actual_index = index + self.offset;
1292
1293 if let Some(limit) = self.limit {
1295 if index >= limit {
1296 return None;
1297 }
1298 }
1299
1300 let row_idx = *self.visible_rows.get(actual_index)?;
1302
1303 let mut values = Vec::new();
1305
1306 let mut real_values = Vec::new();
1308 for &col_idx in &self.get_display_columns() {
1309 let value = self
1310 .source
1311 .get_value(row_idx, col_idx)
1312 .cloned()
1313 .unwrap_or(DataValue::Null);
1314 real_values.push(value);
1315 }
1316
1317 let mut virtual_left = Vec::new();
1319 let mut virtual_right = Vec::new();
1320 let mut virtual_indexed = Vec::new();
1321
1322 for vcol in &self.virtual_columns {
1323 let virtual_value = DataValue::String((vcol.generator)(row_idx));
1324 match vcol.position {
1325 VirtualColumnPosition::Left => virtual_left.push(virtual_value),
1326 VirtualColumnPosition::Right => virtual_right.push(virtual_value),
1327 VirtualColumnPosition::Index(idx) => virtual_indexed.push((idx, virtual_value)),
1328 }
1329 }
1330
1331 values.extend(virtual_left);
1333
1334 for (i, real_value) in real_values.into_iter().enumerate() {
1336 for (idx, vvalue) in &virtual_indexed {
1338 if *idx == i {
1339 values.push(vvalue.clone());
1340 }
1341 }
1342 values.push(real_value);
1343 }
1344
1345 values.extend(virtual_right);
1347
1348 Some(DataRow::new(values))
1349 }
1350
1351 #[must_use]
1353 pub fn get_rows(&self) -> Vec<DataRow> {
1354 let count = self.row_count();
1355 (0..count).filter_map(|i| self.get_row(i)).collect()
1356 }
1357
1358 #[must_use]
1360 pub fn source(&self) -> &DataTable {
1361 &self.source
1362 }
1363
1364 #[must_use]
1366 pub fn source_arc(&self) -> Arc<DataTable> {
1367 Arc::clone(&self.source)
1368 }
1369
1370 #[must_use]
1372 pub fn is_column_visible(&self, index: usize) -> bool {
1373 self.pinned_columns.contains(&index) || self.visible_columns.contains(&index)
1374 }
1375
1376 #[must_use]
1378 pub fn visible_column_indices(&self) -> &[usize] {
1379 &self.visible_columns
1380 }
1381
1382 #[must_use]
1384 pub fn display_column_indices(&self) -> Vec<usize> {
1385 self.get_display_columns()
1386 }
1387
1388 #[must_use]
1390 pub fn visible_row_indices(&self) -> &[usize] {
1391 &self.visible_rows
1392 }
1393
1394 pub fn shrink_to_fit(&mut self) {
1396 self.visible_rows.shrink_to_fit();
1397 self.visible_columns.shrink_to_fit();
1398 self.pinned_columns.shrink_to_fit();
1399 self.base_rows.shrink_to_fit();
1400 self.base_columns.shrink_to_fit();
1401 self.matching_columns.shrink_to_fit();
1402 self.virtual_columns.shrink_to_fit();
1403 }
1404
1405 pub fn search_columns(&mut self, pattern: &str) {
1409 self.column_search_pattern = if pattern.is_empty() {
1410 None
1411 } else {
1412 Some(pattern.to_string())
1413 };
1414
1415 if pattern.is_empty() {
1416 self.matching_columns.clear();
1417 self.current_column_match = 0;
1418 return;
1419 }
1420
1421 let pattern_lower = pattern.to_lowercase();
1423 self.matching_columns = self
1424 .visible_columns
1425 .iter()
1426 .enumerate()
1427 .filter_map(|(visible_idx, &source_idx)| {
1428 let col_name = &self.source.columns[source_idx].name;
1429 if col_name.to_lowercase().contains(&pattern_lower) {
1430 debug!(target: "column_search",
1431 "Found match: '{}' at visible_idx={}, source_idx={}",
1432 col_name, visible_idx, source_idx);
1433 Some((visible_idx, col_name.clone()))
1434 } else {
1435 None
1436 }
1437 })
1438 .collect();
1439
1440 debug!(target: "column_search",
1441 "Total matches found: {}, visible_columns.len()={}, pattern='{}'",
1442 self.matching_columns.len(), self.visible_columns.len(), pattern);
1443
1444 self.current_column_match = 0;
1446 }
1447
1448 pub fn clear_column_search(&mut self) {
1450 self.column_search_pattern = None;
1451 self.matching_columns.clear();
1452 self.current_column_match = 0;
1453 }
1454
1455 pub fn next_column_match(&mut self) -> Option<usize> {
1457 if self.matching_columns.is_empty() {
1458 return None;
1459 }
1460
1461 self.current_column_match = (self.current_column_match + 1) % self.matching_columns.len();
1462 Some(self.matching_columns[self.current_column_match].0)
1463 }
1464
1465 pub fn prev_column_match(&mut self) -> Option<usize> {
1467 if self.matching_columns.is_empty() {
1468 return None;
1469 }
1470
1471 if self.current_column_match == 0 {
1472 self.current_column_match = self.matching_columns.len() - 1;
1473 } else {
1474 self.current_column_match -= 1;
1475 }
1476 Some(self.matching_columns[self.current_column_match].0)
1477 }
1478
1479 #[must_use]
1481 pub fn column_search_pattern(&self) -> Option<&str> {
1482 self.column_search_pattern.as_deref()
1483 }
1484
1485 #[must_use]
1487 pub fn get_matching_columns(&self) -> &[(usize, String)] {
1488 &self.matching_columns
1489 }
1490
1491 #[must_use]
1493 pub fn current_column_match_index(&self) -> usize {
1494 self.current_column_match
1495 }
1496
1497 #[must_use]
1499 pub fn get_current_column_match(&self) -> Option<usize> {
1500 if self.matching_columns.is_empty() {
1501 None
1502 } else {
1503 Some(self.matching_columns[self.current_column_match].0)
1504 }
1505 }
1506
1507 #[must_use]
1509 pub fn has_column_search(&self) -> bool {
1510 self.column_search_pattern.is_some()
1511 }
1512
1513 fn get_real_column_names(&self) -> Vec<String> {
1515 let all_source_names = self.source.column_names();
1516 let display_columns = self.get_display_columns();
1517
1518 display_columns
1519 .iter()
1520 .filter_map(|&idx| all_source_names.get(idx).cloned())
1521 .collect()
1522 }
1523
1524 fn extract_real_values_from_row(&self, full_row: &DataRow) -> Vec<DataValue> {
1526 let mut real_values = Vec::new();
1527 let mut value_idx = 0;
1528
1529 let left_virtual_count = self
1531 .virtual_columns
1532 .iter()
1533 .filter(|vc| matches!(vc.position, VirtualColumnPosition::Left))
1534 .count();
1535
1536 value_idx += left_virtual_count;
1538
1539 let real_column_count = self.get_display_columns().len();
1541 for _ in 0..real_column_count {
1542 if value_idx < full_row.values.len() {
1543 real_values.push(full_row.values[value_idx].clone());
1544 value_idx += 1;
1545 }
1546 }
1547
1548 real_values
1549 }
1550
1551 #[must_use]
1554 pub fn to_json(&self) -> Value {
1555 let column_names = self.get_real_column_names();
1557 let mut rows = Vec::new();
1558
1559 for row_idx in 0..self.row_count() {
1561 if let Some(full_row) = self.get_row(row_idx) {
1562 let real_values = self.extract_real_values_from_row(&full_row);
1564
1565 let mut obj = serde_json::Map::new();
1566 for (col_idx, col_name) in column_names.iter().enumerate() {
1567 if let Some(value) = real_values.get(col_idx) {
1568 let json_value = match value {
1569 DataValue::String(s) => json!(s),
1570 DataValue::InternedString(s) => json!(s.as_ref()),
1571 DataValue::Integer(i) => json!(i),
1572 DataValue::Float(f) => json!(f),
1573 DataValue::Boolean(b) => json!(b),
1574 DataValue::DateTime(dt) => json!(dt),
1575 DataValue::Null => json!(null),
1576 };
1577 obj.insert(col_name.clone(), json_value);
1578 }
1579 }
1580 rows.push(json!(obj));
1581 }
1582 }
1583
1584 json!(rows)
1585 }
1586
1587 pub fn to_csv(&self) -> Result<String> {
1589 let mut csv_output = String::new();
1590 let column_names = self.get_real_column_names();
1592
1593 csv_output.push_str(&column_names.join(","));
1595 csv_output.push('\n');
1596
1597 for row_idx in 0..self.row_count() {
1599 if let Some(full_row) = self.get_row(row_idx) {
1600 let real_values = self.extract_real_values_from_row(&full_row);
1602
1603 let row_strings: Vec<String> = real_values
1604 .iter()
1605 .map(|v| {
1606 let s = v.to_string();
1607 if s.contains(',') || s.contains('"') || s.contains('\n') {
1609 format!("\"{}\"", s.replace('"', "\"\""))
1610 } else {
1611 s
1612 }
1613 })
1614 .collect();
1615 csv_output.push_str(&row_strings.join(","));
1616 csv_output.push('\n');
1617 }
1618 }
1619
1620 Ok(csv_output)
1621 }
1622
1623 pub fn to_tsv(&self) -> Result<String> {
1625 let mut tsv_output = String::new();
1626 let column_names = self.get_real_column_names();
1628
1629 tsv_output.push_str(&column_names.join("\t"));
1631 tsv_output.push('\n');
1632
1633 for row_idx in 0..self.row_count() {
1635 if let Some(full_row) = self.get_row(row_idx) {
1636 let real_values = self.extract_real_values_from_row(&full_row);
1638
1639 let row_strings: Vec<String> = real_values
1640 .iter()
1641 .map(std::string::ToString::to_string)
1642 .collect();
1643 tsv_output.push_str(&row_strings.join("\t"));
1644 tsv_output.push('\n');
1645 }
1646 }
1647
1648 Ok(tsv_output)
1649 }
1650
1651 pub fn get_column_values(&self, column_index: usize) -> Vec<String> {
1653 use tracing::trace;
1654
1655 let mut values = Vec::new();
1656 let row_count = self.row_count();
1657
1658 trace!(
1659 "get_column_values: Getting column {} values from {} visible rows",
1660 column_index,
1661 row_count
1662 );
1663
1664 for row_idx in 0..row_count {
1665 if let Some(row) = self.get_row(row_idx) {
1667 if let Some(value) = row.values.get(column_index) {
1670 let str_value = value
1671 .to_string()
1672 .replace('\t', " ")
1673 .replace('\n', " ")
1674 .replace('\r', "");
1675 values.push(str_value);
1676 } else {
1677 values.push("NULL".to_string());
1678 }
1679 }
1680 }
1681
1682 trace!("get_column_values: Retrieved {} values", values.len());
1683 values
1684 }
1685
1686 #[must_use]
1688 pub fn get_cell_value(&self, row_index: usize, column_index: usize) -> Option<String> {
1689 if let Some(row) = self.get_row(row_index) {
1691 row.values
1694 .get(column_index)
1695 .map(std::string::ToString::to_string)
1696 } else {
1697 None
1698 }
1699 }
1700
1701 #[must_use]
1703 pub fn get_row_values(&self, row_index: usize) -> Option<Vec<String>> {
1704 self.get_row(row_index).map(|row| {
1705 row.values
1706 .iter()
1707 .map(std::string::ToString::to_string)
1708 .collect()
1709 })
1710 }
1711
1712 #[must_use]
1715 pub fn get_row_visual_values(&self, row_index: usize) -> Option<Vec<String>> {
1716 if let Some(row) = self.get_row(row_index) {
1717 let values: Vec<String> = row
1720 .values
1721 .iter()
1722 .map(std::string::ToString::to_string)
1723 .collect();
1724 Some(values)
1725 } else {
1726 None
1727 }
1728 }
1729
1730 #[must_use]
1733 pub fn get_column_index_mapping(&self) -> Vec<(usize, String, usize)> {
1734 let mut mappings = Vec::new();
1735
1736 for (visible_idx, &datatable_idx) in self.visible_columns.iter().enumerate() {
1737 if let Some(column) = self.source.columns.get(datatable_idx) {
1738 mappings.push((visible_idx, column.name.clone(), datatable_idx));
1739 }
1740 }
1741
1742 mappings
1743 }
1744
1745 #[must_use]
1747 pub fn get_column_debug_info(&self) -> String {
1748 let mut info = String::new();
1749 info.push_str("Column Mapping (Visible â DataTable):\n");
1750
1751 let total_columns = self.source.columns.len();
1752 let visible_count = self.visible_columns.len();
1753 let hidden_count = total_columns - visible_count;
1754
1755 info.push_str(&format!(
1756 "Total: {total_columns} columns, Visible: {visible_count}, Hidden: {hidden_count}\n\n"
1757 ));
1758
1759 for (visible_idx, &datatable_idx) in self.visible_columns.iter().enumerate() {
1761 if let Some(column) = self.source.columns.get(datatable_idx) {
1762 let pinned_marker = if self.pinned_columns.contains(&datatable_idx) {
1763 " [PINNED]"
1764 } else {
1765 ""
1766 };
1767 info.push_str(&format!(
1768 " V[{:3}] â DT[{:3}] : {}{}\n",
1769 visible_idx, datatable_idx, column.name, pinned_marker
1770 ));
1771 }
1772 }
1773
1774 if hidden_count > 0 {
1776 info.push_str("\nHidden Columns:\n");
1777 for (idx, column) in self.source.columns.iter().enumerate() {
1778 if !self.visible_columns.contains(&idx) {
1779 info.push_str(&format!(" DT[{:3}] : {}\n", idx, column.name));
1780 }
1781 }
1782 }
1783
1784 if !self.pinned_columns.is_empty() {
1786 info.push_str(&format!("\nPinned Columns: {:?}\n", self.pinned_columns));
1787 }
1788
1789 let is_reordered = self.visible_columns.windows(2).any(|w| w[0] > w[1]);
1791
1792 if is_reordered {
1793 info.push_str("\nâ ď¸ Column order has been modified from original DataTable order\n");
1794 }
1795
1796 info
1797 }
1798}
1799
1800impl DataProvider for DataView {
1803 fn get_row(&self, index: usize) -> Option<Vec<String>> {
1804 self.get_row(index).map(|row| {
1805 row.values
1806 .iter()
1807 .map(std::string::ToString::to_string)
1808 .collect()
1809 })
1810 }
1811
1812 fn get_column_names(&self) -> Vec<String> {
1813 self.column_names()
1814 }
1815
1816 fn get_row_count(&self) -> usize {
1817 self.row_count()
1818 }
1819
1820 fn get_column_count(&self) -> usize {
1821 self.column_count()
1822 }
1823
1824 fn get_column_widths(&self) -> Vec<usize> {
1825 let mut widths = vec![0; self.column_count()];
1827
1828 for (i, name) in self.column_names().iter().enumerate() {
1830 widths[i] = name.len();
1831 }
1832
1833 let sample_size = 100.min(self.row_count());
1836 for row_idx in 0..sample_size {
1837 if let Some(row) = self.get_row(row_idx) {
1838 for (col_idx, value) in row.values.iter().enumerate() {
1839 if col_idx < widths.len() {
1840 let display_len = value.to_string().len();
1841 widths[col_idx] = widths[col_idx].max(display_len);
1842 }
1843 }
1844 }
1845 }
1846
1847 let terminal_width = crossterm::terminal::size()
1849 .map(|(w, _)| w as usize)
1850 .unwrap_or(120);
1851
1852 let available_width = terminal_width.saturating_sub(10);
1855 let visible_cols = self.visible_columns.len().min(10);
1856
1857 let dynamic_max = if visible_cols > 0 {
1859 (available_width / visible_cols).min(80).max(20)
1860 } else {
1861 40
1862 };
1863
1864 for width in &mut widths {
1866 *width = (*width).clamp(6, dynamic_max);
1867 }
1868
1869 widths
1870 }
1871}
1872
1873impl std::fmt::Debug for DataView {
1875 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1876 f.debug_struct("DataView")
1877 .field("source_name", &self.source.name)
1878 .field("visible_rows", &self.visible_rows.len())
1879 .field("visible_columns", &self.visible_columns.len())
1880 .field("has_filter", &self.filter_pattern.is_some())
1881 .field("has_column_search", &self.column_search_pattern.is_some())
1882 .finish()
1883 }
1884}
1885
1886#[cfg(test)]
1887mod tests {
1888 use super::*;
1889 use crate::data::datatable::{DataColumn, DataRow, DataTable, DataValue};
1890 use std::sync::Arc;
1891
1892 #[allow(dead_code)]
1895 fn test_hide_empty_columns_index_fix() {
1896 let mut table = DataTable::new("test");
1898
1899 table.add_column(DataColumn::new("name"));
1901 table.add_column(DataColumn::new("empty1")); table.add_column(DataColumn::new("salary"));
1903 table.add_column(DataColumn::new("empty2")); table.add_column(DataColumn::new("department"));
1905
1906 table
1908 .add_row(DataRow::new(vec![
1909 DataValue::String("John".to_string()),
1910 DataValue::Null,
1911 DataValue::Integer(50000),
1912 DataValue::Null,
1913 DataValue::String("Engineering".to_string()),
1914 ]))
1915 .unwrap();
1916
1917 table
1918 .add_row(DataRow::new(vec![
1919 DataValue::String("Jane".to_string()),
1920 DataValue::Null,
1921 DataValue::Integer(60000),
1922 DataValue::Null,
1923 DataValue::String("Marketing".to_string()),
1924 ]))
1925 .unwrap();
1926
1927 let mut dataview = DataView::new(Arc::new(table));
1929
1930 assert_eq!(dataview.column_count(), 5);
1932
1933 let hidden_count = dataview.hide_empty_columns();
1935
1936 assert_eq!(hidden_count, 2);
1938
1939 assert_eq!(dataview.column_count(), 3);
1941
1942 let final_columns = dataview.column_names();
1944
1945 assert_eq!(final_columns[0], "name");
1947 assert_eq!(final_columns[1], "salary");
1948 assert_eq!(final_columns[2], "department");
1949
1950 let hidden_columns = dataview.get_hidden_column_names();
1952 assert!(hidden_columns.contains(&"empty1".to_string()));
1953 assert!(hidden_columns.contains(&"empty2".to_string()));
1954 assert_eq!(hidden_columns.len(), 2);
1955 }
1956}