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> {
225 let mut empty_columns = Vec::new();
226
227 let column_names = self.source.column_names();
229
230 for &col_idx in &self.visible_columns {
232 let column_name = column_names
233 .get(col_idx)
234 .map_or("unknown", std::string::String::as_str);
235 let mut is_empty = true;
236 let mut sample_values = Vec::new();
237
238 let rows_to_check = self.visible_rows.len().min(1000);
241
242 for &row_idx in self.visible_rows.iter().take(rows_to_check) {
243 if let Some(value) = self.source.get_value(row_idx, col_idx) {
244 if sample_values.len() < 3 {
246 sample_values.push(format!("{value:?}"));
247 }
248
249 match value {
250 DataValue::Null => continue,
251 DataValue::String(s) if s.is_empty() => continue,
252 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, _ => {
258 is_empty = false;
259 break;
260 }
261 }
262 }
263 }
264
265 if is_empty {
266 tracing::debug!(
267 "Column '{}' (idx {}) detected as empty. Sample values: {:?}",
268 column_name,
269 col_idx,
270 sample_values
271 );
272 empty_columns.push(col_idx);
273 } else {
274 tracing::debug!(
275 "Column '{}' (idx {}) has non-empty values. Sample values: {:?}",
276 column_name,
277 col_idx,
278 sample_values
279 );
280 }
281 }
282
283 tracing::info!(
284 "Detected {} empty columns out of {} visible columns",
285 empty_columns.len(),
286 self.visible_columns.len()
287 );
288 empty_columns
289 }
290
291 pub fn hide_empty_columns(&mut self) -> usize {
294 let empty_columns = self.detect_empty_columns();
295 let count = empty_columns.len();
296
297 let column_names = self.source.column_names();
301 for col_idx in empty_columns {
302 if let Some(column_name) = column_names.get(col_idx) {
303 tracing::debug!(
304 "Hiding empty column '{}' (source index {})",
305 column_name,
306 col_idx
307 );
308 self.hide_column_by_name(column_name);
309 }
310 }
311
312 count
313 }
314
315 pub fn unhide_all_columns(&mut self) {
318 self.visible_columns = self.base_columns.clone();
319 }
320
321 pub fn hide_all_columns(&mut self) {
323 self.visible_columns.clear();
324 }
325
326 #[must_use]
328 pub fn has_visible_columns(&self) -> bool {
329 !self.visible_columns.is_empty()
330 }
331
332 pub fn move_column_left(&mut self, display_column_index: usize) -> bool {
335 if display_column_index >= self.visible_columns.len() {
336 return false;
337 }
338
339 let pinned_count = self.pinned_columns.len();
340
341 if display_column_index < pinned_count {
343 if display_column_index == 0 {
345 if pinned_count > 1 {
347 let col = self.pinned_columns.remove(0);
348 self.pinned_columns.push(col);
349 self.rebuild_visible_columns();
350 }
351 } else {
352 self.pinned_columns
354 .swap(display_column_index - 1, display_column_index);
355 self.rebuild_visible_columns();
356 }
357 return true;
358 }
359
360 if display_column_index == pinned_count {
362 let col = self.visible_columns.remove(display_column_index);
364 self.visible_columns.push(col);
365 } else {
366 self.visible_columns
368 .swap(display_column_index - 1, display_column_index);
369 }
370 true
371 }
372
373 pub fn move_column_right(&mut self, display_column_index: usize) -> bool {
376 if display_column_index >= self.visible_columns.len() {
377 return false;
378 }
379
380 let pinned_count = self.pinned_columns.len();
381
382 if display_column_index < pinned_count {
384 if display_column_index == pinned_count - 1 {
386 if pinned_count > 1 {
388 let col = self.pinned_columns.pop().unwrap();
389 self.pinned_columns.insert(0, col);
390 self.rebuild_visible_columns();
391 }
392 } else {
393 self.pinned_columns
395 .swap(display_column_index, display_column_index + 1);
396 self.rebuild_visible_columns();
397 }
398 return true;
399 }
400
401 if display_column_index == self.visible_columns.len() - 1 {
403 let col = self.visible_columns.pop().unwrap();
405 self.visible_columns.insert(pinned_count, col);
406 } else {
407 self.visible_columns
409 .swap(display_column_index, display_column_index + 1);
410 }
411 true
412 }
413
414 pub fn move_column_left_by_name(&mut self, column_name: &str) -> bool {
416 if let Some(source_idx) = self.source.get_column_index(column_name) {
417 if let Some(visible_idx) = self
418 .visible_columns
419 .iter()
420 .position(|&idx| idx == source_idx)
421 {
422 return self.move_column_left(visible_idx);
423 }
424 }
425 false
426 }
427
428 pub fn move_column_right_by_name(&mut self, column_name: &str) -> bool {
430 if let Some(source_idx) = self.source.get_column_index(column_name) {
431 if let Some(visible_idx) = self
432 .visible_columns
433 .iter()
434 .position(|&idx| idx == source_idx)
435 {
436 return self.move_column_right(visible_idx);
437 }
438 }
439 false
440 }
441
442 #[must_use]
444 pub fn get_hidden_column_names(&self) -> Vec<String> {
445 let all_columns = self.source.column_names();
446 let visible_columns = self.column_names();
447
448 all_columns
449 .into_iter()
450 .filter(|col| !visible_columns.contains(col))
451 .collect()
452 }
453
454 #[must_use]
456 pub fn has_hidden_columns(&self) -> bool {
457 self.visible_columns.len() < self.source.column_count()
458 }
459
460 pub fn pin_column(&mut self, display_index: usize) -> Result<()> {
464 let source_column_index = if let Some(&idx) = self.visible_columns.get(display_index) {
466 idx
467 } else {
468 return Err(anyhow::anyhow!(
469 "Display index {} out of bounds",
470 display_index
471 ));
472 };
473
474 if self.pinned_columns.len() >= self.max_pinned_columns {
476 return Err(anyhow::anyhow!(
477 "Maximum {} pinned columns allowed",
478 self.max_pinned_columns
479 ));
480 }
481
482 if self.pinned_columns.contains(&source_column_index) {
484 return Ok(()); }
486
487 self.pinned_columns.push(source_column_index);
489
490 self.rebuild_visible_columns();
492
493 Ok(())
494 }
495
496 fn rebuild_visible_columns(&mut self) {
499 let mut new_visible_columns = Vec::new();
500
501 for &pinned_idx in &self.pinned_columns {
503 new_visible_columns.push(pinned_idx);
504 }
505
506 for col_idx in 0..self.source.column_count() {
508 if !self.pinned_columns.contains(&col_idx) {
509 new_visible_columns.push(col_idx);
510 }
511 }
512
513 self.visible_columns = new_visible_columns;
514 }
515
516 pub fn pin_column_by_name(&mut self, column_name: &str) -> Result<()> {
518 if let Some(source_idx) = self.source.get_column_index(column_name) {
519 if let Some(display_idx) = self
521 .visible_columns
522 .iter()
523 .position(|&idx| idx == source_idx)
524 {
525 self.pin_column(display_idx)
526 } else {
527 Err(anyhow::anyhow!("Column '{}' not visible", column_name))
528 }
529 } else {
530 Err(anyhow::anyhow!("Column '{}' not found", column_name))
531 }
532 }
533
534 pub fn unpin_column(&mut self, display_index: usize) -> bool {
536 if let Some(&source_column_index) = self.visible_columns.get(display_index) {
538 if let Some(pos) = self
539 .pinned_columns
540 .iter()
541 .position(|&idx| idx == source_column_index)
542 {
543 self.pinned_columns.remove(pos);
544
545 self.rebuild_visible_columns();
547
548 true
549 } else {
550 false }
552 } else {
553 false }
555 }
556
557 pub fn unpin_column_by_name(&mut self, column_name: &str) -> bool {
559 if let Some(source_idx) = self.source.get_column_index(column_name) {
560 if let Some(display_idx) = self
562 .visible_columns
563 .iter()
564 .position(|&idx| idx == source_idx)
565 {
566 self.unpin_column(display_idx)
567 } else {
568 false }
570 } else {
571 false
572 }
573 }
574
575 pub fn clear_pinned_columns(&mut self) {
577 for col_idx in self.pinned_columns.drain(..) {
579 if !self.visible_columns.contains(&col_idx) {
580 self.visible_columns.push(col_idx);
581 }
582 }
583 }
584
585 #[must_use]
587 pub fn is_column_pinned(&self, display_index: usize) -> bool {
588 if let Some(&source_column_index) = self.visible_columns.get(display_index) {
589 self.pinned_columns.contains(&source_column_index)
590 } else {
591 false
592 }
593 }
594
595 #[must_use]
597 pub fn get_pinned_columns(&self) -> &[usize] {
598 &self.pinned_columns
599 }
600
601 #[must_use]
603 pub fn get_pinned_column_names(&self) -> Vec<String> {
604 let all_columns = self.source.column_names();
605 self.pinned_columns
606 .iter()
607 .filter_map(|&idx| all_columns.get(idx).cloned())
608 .collect()
609 }
610
611 #[must_use]
613 pub fn get_display_columns(&self) -> Vec<usize> {
614 self.visible_columns.clone()
617 }
618
619 #[must_use]
621 pub fn get_display_column_names(&self) -> Vec<String> {
622 let all_columns = self.source.column_names();
623 self.get_display_columns()
624 .iter()
625 .filter_map(|&idx| all_columns.get(idx).cloned())
626 .collect()
627 }
628
629 pub fn set_max_pinned_columns(&mut self, max: usize) {
631 self.max_pinned_columns = max;
632 while self.pinned_columns.len() > max {
634 if let Some(col_idx) = self.pinned_columns.pop() {
635 self.visible_columns.insert(0, col_idx);
636 }
637 }
638 }
639
640 #[must_use]
642 pub fn with_rows(mut self, rows: Vec<usize>) -> Self {
643 self.visible_rows = rows.clone();
644 self.base_rows = rows; self
646 }
647
648 #[must_use]
650 pub fn with_limit(mut self, limit: usize, offset: usize) -> Self {
651 self.limit = Some(limit);
652 self.offset = offset;
653 self
654 }
655
656 pub fn filter<F>(mut self, predicate: F) -> Self
658 where
659 F: Fn(&DataTable, usize) -> bool,
660 {
661 self.visible_rows
662 .retain(|&row_idx| predicate(&self.source, row_idx));
663 self.base_rows = self.visible_rows.clone();
665 self
666 }
667
668 pub fn apply_text_filter(&mut self, pattern: &str, case_sensitive: bool) {
670 info!(
671 "DataView::apply_text_filter - pattern='{}', case_sensitive={}, thread={:?}",
672 pattern,
673 case_sensitive,
674 std::thread::current().id()
675 );
676
677 if pattern.is_empty() {
678 info!("DataView::apply_text_filter - empty pattern, clearing filter");
679 self.clear_filter();
680 return;
681 }
682
683 if self.fuzzy_filter_pattern.is_some() {
685 info!("DataView::apply_text_filter - clearing existing fuzzy filter");
686 self.fuzzy_filter_pattern = None;
687 }
688
689 self.filter_pattern = Some(pattern.to_string());
691
692 let pattern_lower = if case_sensitive {
694 pattern.to_string()
695 } else {
696 pattern.to_lowercase()
697 };
698
699 info!(
700 "DataView::apply_text_filter - searching for '{}' in {} base rows",
701 pattern_lower,
702 self.base_rows.len()
703 );
704
705 let mut matched_count = 0;
706 let mut checked_count = 0;
707
708 self.visible_rows = self
709 .base_rows
710 .iter()
711 .copied()
712 .filter(|&row_idx| {
713 checked_count += 1;
714
715 if let Some(row) = self.source.get_row(row_idx) {
717 if checked_count <= 3 {
719 let preview = row
720 .values
721 .iter()
722 .take(5)
723 .map(std::string::ToString::to_string)
724 .collect::<Vec<_>>()
725 .join(", ");
726 info!(
727 "DataView::apply_text_filter - row {} preview: {}",
728 row_idx, preview
729 );
730 }
731
732 for value in &row.values {
733 let text = value.to_string();
734 let text_to_match = if case_sensitive {
735 text.clone()
736 } else {
737 text.to_lowercase()
738 };
739 if text_to_match.contains(&pattern_lower) {
740 matched_count += 1;
741 if checked_count <= 3 {
742 info!(
743 "DataView::apply_text_filter - MATCH in row {} cell: '{}'",
744 row_idx, text
745 );
746 }
747 return true;
748 }
749 }
750 }
751 false
752 })
753 .collect();
754
755 info!(
756 "DataView::apply_text_filter - checked {} rows, matched {} rows",
757 checked_count, matched_count
758 );
759 info!(
760 "DataView::apply_text_filter - final visible rows: {}",
761 self.visible_rows.len()
762 );
763
764 if let Some(sort_column) = self.sort_state.column {
766 if let Some(source_index) = self.visible_columns.get(sort_column) {
767 let ascending = matches!(self.sort_state.order, SortOrder::Ascending);
768 info!(
769 "DataView::apply_text_filter - reapplying sort on column {} (ascending={})",
770 sort_column, ascending
771 );
772 let _ = self.apply_sort_internal(*source_index, ascending);
773 }
774 }
775 }
776
777 pub fn clear_filter(&mut self) {
779 self.filter_pattern = None;
780 self.fuzzy_filter_pattern = None;
781 self.visible_rows = self.base_rows.clone();
782
783 if let Some(sort_column) = self.sort_state.column {
785 if let Some(source_index) = self.visible_columns.get(sort_column) {
786 let ascending = matches!(self.sort_state.order, SortOrder::Ascending);
787 let _ = self.apply_sort_internal(*source_index, ascending);
788 }
789 }
790 }
791
792 #[must_use]
794 pub fn has_filter(&self) -> bool {
795 self.filter_pattern.is_some() || self.fuzzy_filter_pattern.is_some()
796 }
797
798 #[must_use]
800 pub fn get_filter_pattern(&self) -> Option<&str> {
801 self.filter_pattern.as_deref()
802 }
803
804 #[must_use]
806 pub fn get_fuzzy_filter_pattern(&self) -> Option<&str> {
807 self.fuzzy_filter_pattern.as_deref()
808 }
809
810 pub fn apply_fuzzy_filter(&mut self, pattern: &str, case_insensitive: bool) {
813 info!(
814 "DataView::apply_fuzzy_filter - pattern='{}', case_insensitive={}, thread={:?}",
815 pattern,
816 case_insensitive,
817 std::thread::current().id()
818 );
819
820 if pattern.is_empty() {
821 info!("DataView::apply_fuzzy_filter - empty pattern, clearing filter");
822 self.clear_filter();
823 return;
824 }
825
826 if self.filter_pattern.is_some() {
828 info!("DataView::apply_fuzzy_filter - clearing existing text filter");
829 self.filter_pattern = None;
830 }
831
832 self.fuzzy_filter_pattern = Some(pattern.to_string());
834
835 let use_exact = pattern.starts_with('\'');
837
838 self.visible_rows = self
839 .base_rows
840 .iter()
841 .copied()
842 .filter(|&row_idx| {
843 if let Some(row) = self.source.get_row(row_idx) {
845 let row_text = row
847 .values
848 .iter()
849 .map(std::string::ToString::to_string)
850 .collect::<Vec<_>>()
851 .join(" ");
852
853 if use_exact && pattern.len() > 1 {
854 let exact_pattern = &pattern[1..];
856 if case_insensitive {
857 row_text
858 .to_lowercase()
859 .contains(&exact_pattern.to_lowercase())
860 } else {
861 row_text.contains(exact_pattern)
862 }
863 } else if !use_exact {
864 let matcher = if case_insensitive {
866 SkimMatcherV2::default().ignore_case()
867 } else {
868 SkimMatcherV2::default().respect_case()
869 };
870
871 matcher
873 .fuzzy_match(&row_text, pattern)
874 .is_some_and(|score| score > 0)
875 } else {
876 false
878 }
879 } else {
880 false
881 }
882 })
883 .collect();
884
885 if let Some(sort_column) = self.sort_state.column {
887 if let Some(source_index) = self.visible_columns.get(sort_column) {
888 let ascending = matches!(self.sort_state.order, SortOrder::Ascending);
889 info!(
890 "DataView::apply_fuzzy_filter - reapplying sort on column {} (ascending={})",
891 sort_column, ascending
892 );
893 let _ = self.apply_sort_internal(*source_index, ascending);
894 }
895 }
896 }
897
898 #[must_use]
900 pub fn get_fuzzy_filter_indices(&self) -> Vec<usize> {
901 self.visible_rows.clone()
903 }
904
905 #[must_use]
907 pub fn get_visible_rows(&self) -> Vec<usize> {
908 self.visible_rows.clone()
909 }
910
911 pub fn group_by(&self, group_columns: &[String]) -> Result<BTreeMap<GroupKey, DataView>> {
914 let mut groups = BTreeMap::new();
915
916 let col_indices: Vec<usize> = group_columns
918 .iter()
919 .map(|col_name| {
920 self.source
921 .get_column_index(col_name)
922 .ok_or_else(|| anyhow!("Column '{}' not found for GROUP BY", col_name))
923 })
924 .collect::<Result<Vec<_>>>()?;
925
926 let mut group_rows: BTreeMap<GroupKey, Vec<usize>> = BTreeMap::new();
928
929 for &row_idx in &self.visible_rows {
931 let mut key_values = Vec::new();
933 for &col_idx in &col_indices {
934 let value = self
935 .source
936 .get_value(row_idx, col_idx)
937 .cloned()
938 .unwrap_or(DataValue::Null);
939 key_values.push(value);
940 }
941 let key = GroupKey(key_values);
942
943 group_rows.entry(key).or_default().push(row_idx);
945 }
946
947 for (key, rows) in group_rows {
949 let mut group_view = DataView::new(Arc::clone(&self.source));
950 group_view.visible_rows = rows;
952 group_view.visible_columns = self.visible_columns.clone();
954 group_view.base_rows = group_view.visible_rows.clone();
955 group_view.base_columns = group_view.visible_columns.clone();
956
957 groups.insert(key, group_view);
958 }
959
960 Ok(groups)
961 }
962
963 pub fn sort_by(mut self, column_index: usize, ascending: bool) -> Result<Self> {
966 self.apply_sort(column_index, ascending)?;
967 Ok(self)
968 }
969
970 pub fn apply_sort(&mut self, column_index: usize, ascending: bool) -> Result<()> {
973 let source_column_index = if column_index < self.visible_columns.len() {
975 self.visible_columns[column_index]
976 } else {
977 return Err(anyhow::anyhow!(
978 "Column index {} out of bounds (visible columns: {})",
979 column_index,
980 self.visible_columns.len()
981 ));
982 };
983
984 self.apply_sort_internal(source_column_index, ascending)?;
986
987 self.sort_state.column = Some(column_index);
989 self.sort_state.order = if ascending {
990 SortOrder::Ascending
991 } else {
992 SortOrder::Descending
993 };
994
995 Ok(())
996 }
997
998 fn apply_sort_internal(&mut self, source_column_index: usize, ascending: bool) -> Result<()> {
1000 if source_column_index >= self.source.column_count() {
1001 return Err(anyhow::anyhow!(
1002 "Source column index {} out of bounds",
1003 source_column_index
1004 ));
1005 }
1006
1007 let source = &self.source;
1008 self.visible_rows.sort_by(|&a, &b| {
1009 let val_a = source.get_value(a, source_column_index);
1010 let val_b = source.get_value(b, source_column_index);
1011
1012 let cmp = compare_optional_datavalues(val_a, val_b);
1013
1014 if ascending {
1015 cmp
1016 } else {
1017 cmp.reverse()
1018 }
1019 });
1020
1021 Ok(())
1025 }
1026
1027 pub fn apply_multi_sort(&mut self, sort_columns: &[(usize, bool)]) -> Result<()> {
1030 if sort_columns.is_empty() {
1031 return Ok(());
1032 }
1033
1034 for (col_idx, _) in sort_columns {
1036 if *col_idx >= self.source.column_count() {
1037 return Err(anyhow::anyhow!(
1038 "Source column index {} out of bounds",
1039 col_idx
1040 ));
1041 }
1042 }
1043
1044 let source = &self.source;
1045 self.visible_rows.sort_by(|&a, &b| {
1046 for (col_idx, ascending) in sort_columns {
1048 let val_a = source.get_value(a, *col_idx);
1049 let val_b = source.get_value(b, *col_idx);
1050
1051 let cmp = compare_optional_datavalues(val_a, val_b);
1052
1053 if cmp != std::cmp::Ordering::Equal {
1055 return if *ascending { cmp } else { cmp.reverse() };
1056 }
1057 }
1059
1060 std::cmp::Ordering::Equal
1062 });
1063
1064 if let Some((primary_col, ascending)) = sort_columns.first() {
1066 if let Some(visible_idx) = self.visible_columns.iter().position(|&x| x == *primary_col)
1068 {
1069 self.sort_state.column = Some(visible_idx);
1070 self.sort_state.order = if *ascending {
1071 SortOrder::Ascending
1072 } else {
1073 SortOrder::Descending
1074 };
1075 }
1076 }
1077
1078 Ok(())
1079 }
1080
1081 pub fn toggle_sort(&mut self, column_index: usize) -> Result<()> {
1084 let source_column_index = if column_index < self.visible_columns.len() {
1086 self.visible_columns[column_index]
1087 } else {
1088 return Err(anyhow::anyhow!(
1089 "Column index {} out of bounds (visible columns: {})",
1090 column_index,
1091 self.visible_columns.len()
1092 ));
1093 };
1094
1095 let next_order = if self.sort_state.column == Some(column_index) {
1097 match self.sort_state.order {
1099 SortOrder::None => SortOrder::Ascending,
1100 SortOrder::Ascending => SortOrder::Descending,
1101 SortOrder::Descending => SortOrder::None,
1102 }
1103 } else {
1104 SortOrder::Ascending
1106 };
1107
1108 match next_order {
1110 SortOrder::Ascending => {
1111 self.apply_sort_internal(source_column_index, true)?;
1112 self.sort_state.column = Some(column_index);
1114 self.sort_state.order = SortOrder::Ascending;
1115 }
1116 SortOrder::Descending => {
1117 self.apply_sort_internal(source_column_index, false)?;
1118 self.sort_state.column = Some(column_index);
1119 self.sort_state.order = SortOrder::Descending;
1120 }
1121 SortOrder::None => {
1122 self.sort_state.column = None;
1123 self.sort_state.order = SortOrder::None;
1124 self.clear_sort();
1125 }
1126 }
1127
1128 Ok(())
1129 }
1130
1131 #[must_use]
1133 pub fn get_sort_state(&self) -> &SortState {
1134 &self.sort_state
1135 }
1136
1137 #[must_use]
1140 pub fn get_visible_column_indices(&self) -> Vec<usize> {
1141 self.visible_columns.clone()
1142 }
1143
1144 pub fn clear_sort(&mut self) {
1146 self.sort_state.column = None;
1148 self.sort_state.order = SortOrder::None;
1149
1150 self.visible_rows = self.base_rows.clone();
1153
1154 if let Some(pattern) = self.filter_pattern.clone() {
1156 let case_insensitive = false; self.apply_text_filter(&pattern, case_insensitive);
1158 }
1159 }
1160
1161 pub fn add_virtual_column(&mut self, virtual_column: VirtualColumn) {
1165 self.virtual_columns.push(virtual_column);
1166 }
1167
1168 pub fn add_row_numbers(&mut self, position: VirtualColumnPosition) {
1170 let row_num_column = VirtualColumn {
1171 name: "#".to_string(),
1172 generator: Arc::new(|row_index| format!("{}", row_index + 1)),
1173 width: Some(4), position,
1175 };
1176 self.add_virtual_column(row_num_column);
1177 }
1178
1179 pub fn remove_virtual_columns(&mut self, name: &str) {
1181 self.virtual_columns.retain(|col| col.name != name);
1182 }
1183
1184 pub fn toggle_row_numbers(&mut self) {
1186 if self.virtual_columns.iter().any(|col| col.name == "#") {
1187 self.remove_virtual_columns("#");
1188 } else {
1189 self.add_row_numbers(VirtualColumnPosition::Left);
1190 }
1191 }
1192
1193 #[must_use]
1195 pub fn has_row_numbers(&self) -> bool {
1196 self.virtual_columns.iter().any(|col| col.name == "#")
1197 }
1198
1199 #[must_use]
1201 pub fn get_all_column_names(&self) -> Vec<String> {
1202 let mut result = Vec::new();
1203 let all_source_names = self.source.column_names();
1204 let real_column_names: Vec<String> = self
1206 .get_display_columns()
1207 .iter()
1208 .map(|&i| {
1209 all_source_names
1210 .get(i)
1211 .cloned()
1212 .unwrap_or_else(|| format!("col_{i}"))
1213 })
1214 .collect();
1215
1216 let mut virtual_left = Vec::new();
1218 let mut virtual_right = Vec::new();
1219 let mut virtual_indexed = Vec::new();
1220
1221 for vcol in &self.virtual_columns {
1222 match vcol.position {
1223 VirtualColumnPosition::Left => virtual_left.push(vcol.name.clone()),
1224 VirtualColumnPosition::Right => virtual_right.push(vcol.name.clone()),
1225 VirtualColumnPosition::Index(idx) => virtual_indexed.push((idx, vcol.name.clone())),
1226 }
1227 }
1228
1229 result.extend(virtual_left);
1231
1232 for (i, real_name) in real_column_names.into_iter().enumerate() {
1234 for (idx, vname) in &virtual_indexed {
1236 if *idx == i {
1237 result.push(vname.clone());
1238 }
1239 }
1240 result.push(real_name);
1241 }
1242
1243 result.extend(virtual_right);
1245
1246 result
1247 }
1248
1249 #[must_use]
1251 pub fn row_count(&self) -> usize {
1252 let count = self.visible_rows.len();
1253
1254 if let Some(limit) = self.limit {
1256 let available = count.saturating_sub(self.offset);
1257 available.min(limit)
1258 } else {
1259 count.saturating_sub(self.offset)
1260 }
1261 }
1262
1263 #[must_use]
1265 pub fn column_count(&self) -> usize {
1266 self.visible_columns.len() + self.virtual_columns.len()
1268 }
1269
1270 #[must_use]
1272 pub fn column_names(&self) -> Vec<String> {
1273 self.get_all_column_names()
1274 }
1275
1276 #[must_use]
1278 pub fn get_row(&self, index: usize) -> Option<DataRow> {
1279 let actual_index = index + self.offset;
1280
1281 if let Some(limit) = self.limit {
1283 if index >= limit {
1284 return None;
1285 }
1286 }
1287
1288 let row_idx = *self.visible_rows.get(actual_index)?;
1290
1291 let mut values = Vec::new();
1293
1294 let mut real_values = Vec::new();
1296 for &col_idx in &self.get_display_columns() {
1297 let value = self
1298 .source
1299 .get_value(row_idx, col_idx)
1300 .cloned()
1301 .unwrap_or(DataValue::Null);
1302 real_values.push(value);
1303 }
1304
1305 let mut virtual_left = Vec::new();
1307 let mut virtual_right = Vec::new();
1308 let mut virtual_indexed = Vec::new();
1309
1310 for vcol in &self.virtual_columns {
1311 let virtual_value = DataValue::String((vcol.generator)(row_idx));
1312 match vcol.position {
1313 VirtualColumnPosition::Left => virtual_left.push(virtual_value),
1314 VirtualColumnPosition::Right => virtual_right.push(virtual_value),
1315 VirtualColumnPosition::Index(idx) => virtual_indexed.push((idx, virtual_value)),
1316 }
1317 }
1318
1319 values.extend(virtual_left);
1321
1322 for (i, real_value) in real_values.into_iter().enumerate() {
1324 for (idx, vvalue) in &virtual_indexed {
1326 if *idx == i {
1327 values.push(vvalue.clone());
1328 }
1329 }
1330 values.push(real_value);
1331 }
1332
1333 values.extend(virtual_right);
1335
1336 Some(DataRow::new(values))
1337 }
1338
1339 #[must_use]
1341 pub fn get_rows(&self) -> Vec<DataRow> {
1342 let count = self.row_count();
1343 (0..count).filter_map(|i| self.get_row(i)).collect()
1344 }
1345
1346 #[must_use]
1348 pub fn source(&self) -> &DataTable {
1349 &self.source
1350 }
1351
1352 #[must_use]
1354 pub fn source_arc(&self) -> Arc<DataTable> {
1355 Arc::clone(&self.source)
1356 }
1357
1358 #[must_use]
1360 pub fn is_column_visible(&self, index: usize) -> bool {
1361 self.pinned_columns.contains(&index) || self.visible_columns.contains(&index)
1362 }
1363
1364 #[must_use]
1366 pub fn visible_column_indices(&self) -> &[usize] {
1367 &self.visible_columns
1368 }
1369
1370 #[must_use]
1372 pub fn display_column_indices(&self) -> Vec<usize> {
1373 self.get_display_columns()
1374 }
1375
1376 #[must_use]
1378 pub fn visible_row_indices(&self) -> &[usize] {
1379 &self.visible_rows
1380 }
1381
1382 pub fn shrink_to_fit(&mut self) {
1384 self.visible_rows.shrink_to_fit();
1385 self.visible_columns.shrink_to_fit();
1386 self.pinned_columns.shrink_to_fit();
1387 self.base_rows.shrink_to_fit();
1388 self.base_columns.shrink_to_fit();
1389 self.matching_columns.shrink_to_fit();
1390 self.virtual_columns.shrink_to_fit();
1391 }
1392
1393 pub fn search_columns(&mut self, pattern: &str) {
1397 self.column_search_pattern = if pattern.is_empty() {
1398 None
1399 } else {
1400 Some(pattern.to_string())
1401 };
1402
1403 if pattern.is_empty() {
1404 self.matching_columns.clear();
1405 self.current_column_match = 0;
1406 return;
1407 }
1408
1409 let pattern_lower = pattern.to_lowercase();
1411 self.matching_columns = self
1412 .visible_columns
1413 .iter()
1414 .enumerate()
1415 .filter_map(|(visible_idx, &source_idx)| {
1416 let col_name = &self.source.columns[source_idx].name;
1417 if col_name.to_lowercase().contains(&pattern_lower) {
1418 debug!(target: "column_search",
1419 "Found match: '{}' at visible_idx={}, source_idx={}",
1420 col_name, visible_idx, source_idx);
1421 Some((visible_idx, col_name.clone()))
1422 } else {
1423 None
1424 }
1425 })
1426 .collect();
1427
1428 debug!(target: "column_search",
1429 "Total matches found: {}, visible_columns.len()={}, pattern='{}'",
1430 self.matching_columns.len(), self.visible_columns.len(), pattern);
1431
1432 self.current_column_match = 0;
1434 }
1435
1436 pub fn clear_column_search(&mut self) {
1438 self.column_search_pattern = None;
1439 self.matching_columns.clear();
1440 self.current_column_match = 0;
1441 }
1442
1443 pub fn next_column_match(&mut self) -> Option<usize> {
1445 if self.matching_columns.is_empty() {
1446 return None;
1447 }
1448
1449 self.current_column_match = (self.current_column_match + 1) % self.matching_columns.len();
1450 Some(self.matching_columns[self.current_column_match].0)
1451 }
1452
1453 pub fn prev_column_match(&mut self) -> Option<usize> {
1455 if self.matching_columns.is_empty() {
1456 return None;
1457 }
1458
1459 if self.current_column_match == 0 {
1460 self.current_column_match = self.matching_columns.len() - 1;
1461 } else {
1462 self.current_column_match -= 1;
1463 }
1464 Some(self.matching_columns[self.current_column_match].0)
1465 }
1466
1467 #[must_use]
1469 pub fn column_search_pattern(&self) -> Option<&str> {
1470 self.column_search_pattern.as_deref()
1471 }
1472
1473 #[must_use]
1475 pub fn get_matching_columns(&self) -> &[(usize, String)] {
1476 &self.matching_columns
1477 }
1478
1479 #[must_use]
1481 pub fn current_column_match_index(&self) -> usize {
1482 self.current_column_match
1483 }
1484
1485 #[must_use]
1487 pub fn get_current_column_match(&self) -> Option<usize> {
1488 if self.matching_columns.is_empty() {
1489 None
1490 } else {
1491 Some(self.matching_columns[self.current_column_match].0)
1492 }
1493 }
1494
1495 #[must_use]
1497 pub fn has_column_search(&self) -> bool {
1498 self.column_search_pattern.is_some()
1499 }
1500
1501 fn get_real_column_names(&self) -> Vec<String> {
1503 let all_source_names = self.source.column_names();
1504 let display_columns = self.get_display_columns();
1505
1506 display_columns
1507 .iter()
1508 .filter_map(|&idx| all_source_names.get(idx).cloned())
1509 .collect()
1510 }
1511
1512 fn extract_real_values_from_row(&self, full_row: &DataRow) -> Vec<DataValue> {
1514 let mut real_values = Vec::new();
1515 let mut value_idx = 0;
1516
1517 let left_virtual_count = self
1519 .virtual_columns
1520 .iter()
1521 .filter(|vc| matches!(vc.position, VirtualColumnPosition::Left))
1522 .count();
1523
1524 value_idx += left_virtual_count;
1526
1527 let real_column_count = self.get_display_columns().len();
1529 for _ in 0..real_column_count {
1530 if value_idx < full_row.values.len() {
1531 real_values.push(full_row.values[value_idx].clone());
1532 value_idx += 1;
1533 }
1534 }
1535
1536 real_values
1537 }
1538
1539 #[must_use]
1542 pub fn to_json(&self) -> Value {
1543 let column_names = self.get_real_column_names();
1545 let mut rows = Vec::new();
1546
1547 for row_idx in 0..self.row_count() {
1549 if let Some(full_row) = self.get_row(row_idx) {
1550 let real_values = self.extract_real_values_from_row(&full_row);
1552
1553 let mut obj = serde_json::Map::new();
1554 for (col_idx, col_name) in column_names.iter().enumerate() {
1555 if let Some(value) = real_values.get(col_idx) {
1556 let json_value = match value {
1557 DataValue::String(s) => json!(s),
1558 DataValue::InternedString(s) => json!(s.as_ref()),
1559 DataValue::Integer(i) => json!(i),
1560 DataValue::Float(f) => json!(f),
1561 DataValue::Boolean(b) => json!(b),
1562 DataValue::DateTime(dt) => json!(dt),
1563 DataValue::Null => json!(null),
1564 };
1565 obj.insert(col_name.clone(), json_value);
1566 }
1567 }
1568 rows.push(json!(obj));
1569 }
1570 }
1571
1572 json!(rows)
1573 }
1574
1575 pub fn to_csv(&self) -> Result<String> {
1577 let mut csv_output = String::new();
1578 let column_names = self.get_real_column_names();
1580
1581 csv_output.push_str(&column_names.join(","));
1583 csv_output.push('\n');
1584
1585 for row_idx in 0..self.row_count() {
1587 if let Some(full_row) = self.get_row(row_idx) {
1588 let real_values = self.extract_real_values_from_row(&full_row);
1590
1591 let row_strings: Vec<String> = real_values
1592 .iter()
1593 .map(|v| {
1594 let s = v.to_string();
1595 if s.contains(',') || s.contains('"') || s.contains('\n') {
1597 format!("\"{}\"", s.replace('"', "\"\""))
1598 } else {
1599 s
1600 }
1601 })
1602 .collect();
1603 csv_output.push_str(&row_strings.join(","));
1604 csv_output.push('\n');
1605 }
1606 }
1607
1608 Ok(csv_output)
1609 }
1610
1611 pub fn to_tsv(&self) -> Result<String> {
1613 let mut tsv_output = String::new();
1614 let column_names = self.get_real_column_names();
1616
1617 tsv_output.push_str(&column_names.join("\t"));
1619 tsv_output.push('\n');
1620
1621 for row_idx in 0..self.row_count() {
1623 if let Some(full_row) = self.get_row(row_idx) {
1624 let real_values = self.extract_real_values_from_row(&full_row);
1626
1627 let row_strings: Vec<String> = real_values
1628 .iter()
1629 .map(std::string::ToString::to_string)
1630 .collect();
1631 tsv_output.push_str(&row_strings.join("\t"));
1632 tsv_output.push('\n');
1633 }
1634 }
1635
1636 Ok(tsv_output)
1637 }
1638
1639 pub fn get_column_values(&self, column_index: usize) -> Vec<String> {
1641 use tracing::trace;
1642
1643 let mut values = Vec::new();
1644 let row_count = self.row_count();
1645
1646 trace!(
1647 "get_column_values: Getting column {} values from {} visible rows",
1648 column_index,
1649 row_count
1650 );
1651
1652 for row_idx in 0..row_count {
1653 if let Some(row) = self.get_row(row_idx) {
1655 if let Some(value) = row.values.get(column_index) {
1658 let str_value = value
1659 .to_string()
1660 .replace('\t', " ")
1661 .replace('\n', " ")
1662 .replace('\r', "");
1663 values.push(str_value);
1664 } else {
1665 values.push("NULL".to_string());
1666 }
1667 }
1668 }
1669
1670 trace!("get_column_values: Retrieved {} values", values.len());
1671 values
1672 }
1673
1674 #[must_use]
1676 pub fn get_cell_value(&self, row_index: usize, column_index: usize) -> Option<String> {
1677 if let Some(row) = self.get_row(row_index) {
1679 row.values
1682 .get(column_index)
1683 .map(std::string::ToString::to_string)
1684 } else {
1685 None
1686 }
1687 }
1688
1689 #[must_use]
1691 pub fn get_row_values(&self, row_index: usize) -> Option<Vec<String>> {
1692 self.get_row(row_index).map(|row| {
1693 row.values
1694 .iter()
1695 .map(std::string::ToString::to_string)
1696 .collect()
1697 })
1698 }
1699
1700 #[must_use]
1703 pub fn get_row_visual_values(&self, row_index: usize) -> Option<Vec<String>> {
1704 if let Some(row) = self.get_row(row_index) {
1705 let values: Vec<String> = row
1708 .values
1709 .iter()
1710 .map(std::string::ToString::to_string)
1711 .collect();
1712 Some(values)
1713 } else {
1714 None
1715 }
1716 }
1717
1718 #[must_use]
1721 pub fn get_column_index_mapping(&self) -> Vec<(usize, String, usize)> {
1722 let mut mappings = Vec::new();
1723
1724 for (visible_idx, &datatable_idx) in self.visible_columns.iter().enumerate() {
1725 if let Some(column) = self.source.columns.get(datatable_idx) {
1726 mappings.push((visible_idx, column.name.clone(), datatable_idx));
1727 }
1728 }
1729
1730 mappings
1731 }
1732
1733 #[must_use]
1735 pub fn get_column_debug_info(&self) -> String {
1736 let mut info = String::new();
1737 info.push_str("Column Mapping (Visible â DataTable):\n");
1738
1739 let total_columns = self.source.columns.len();
1740 let visible_count = self.visible_columns.len();
1741 let hidden_count = total_columns - visible_count;
1742
1743 info.push_str(&format!(
1744 "Total: {total_columns} columns, Visible: {visible_count}, Hidden: {hidden_count}\n\n"
1745 ));
1746
1747 for (visible_idx, &datatable_idx) in self.visible_columns.iter().enumerate() {
1749 if let Some(column) = self.source.columns.get(datatable_idx) {
1750 let pinned_marker = if self.pinned_columns.contains(&datatable_idx) {
1751 " [PINNED]"
1752 } else {
1753 ""
1754 };
1755 info.push_str(&format!(
1756 " V[{:3}] â DT[{:3}] : {}{}\n",
1757 visible_idx, datatable_idx, column.name, pinned_marker
1758 ));
1759 }
1760 }
1761
1762 if hidden_count > 0 {
1764 info.push_str("\nHidden Columns:\n");
1765 for (idx, column) in self.source.columns.iter().enumerate() {
1766 if !self.visible_columns.contains(&idx) {
1767 info.push_str(&format!(" DT[{:3}] : {}\n", idx, column.name));
1768 }
1769 }
1770 }
1771
1772 if !self.pinned_columns.is_empty() {
1774 info.push_str(&format!("\nPinned Columns: {:?}\n", self.pinned_columns));
1775 }
1776
1777 let is_reordered = self.visible_columns.windows(2).any(|w| w[0] > w[1]);
1779
1780 if is_reordered {
1781 info.push_str("\nâ ď¸ Column order has been modified from original DataTable order\n");
1782 }
1783
1784 info
1785 }
1786}
1787
1788impl DataProvider for DataView {
1791 fn get_row(&self, index: usize) -> Option<Vec<String>> {
1792 self.get_row(index).map(|row| {
1793 row.values
1794 .iter()
1795 .map(std::string::ToString::to_string)
1796 .collect()
1797 })
1798 }
1799
1800 fn get_column_names(&self) -> Vec<String> {
1801 self.column_names()
1802 }
1803
1804 fn get_row_count(&self) -> usize {
1805 self.row_count()
1806 }
1807
1808 fn get_column_count(&self) -> usize {
1809 self.column_count()
1810 }
1811
1812 fn get_column_widths(&self) -> Vec<usize> {
1813 let mut widths = vec![0; self.column_count()];
1815
1816 for (i, name) in self.column_names().iter().enumerate() {
1818 widths[i] = name.len();
1819 }
1820
1821 let sample_size = 100.min(self.row_count());
1824 for row_idx in 0..sample_size {
1825 if let Some(row) = self.get_row(row_idx) {
1826 for (col_idx, value) in row.values.iter().enumerate() {
1827 if col_idx < widths.len() {
1828 let display_len = value.to_string().len();
1829 widths[col_idx] = widths[col_idx].max(display_len);
1830 }
1831 }
1832 }
1833 }
1834
1835 let terminal_width = crossterm::terminal::size()
1837 .map(|(w, _)| w as usize)
1838 .unwrap_or(120);
1839
1840 let available_width = terminal_width.saturating_sub(10);
1843 let visible_cols = self.visible_columns.len().min(10);
1844
1845 let dynamic_max = if visible_cols > 0 {
1847 (available_width / visible_cols).min(80).max(20)
1848 } else {
1849 40
1850 };
1851
1852 for width in &mut widths {
1854 *width = (*width).clamp(6, dynamic_max);
1855 }
1856
1857 widths
1858 }
1859}
1860
1861impl std::fmt::Debug for DataView {
1863 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1864 f.debug_struct("DataView")
1865 .field("source_name", &self.source.name)
1866 .field("visible_rows", &self.visible_rows.len())
1867 .field("visible_columns", &self.visible_columns.len())
1868 .field("has_filter", &self.filter_pattern.is_some())
1869 .field("has_column_search", &self.column_search_pattern.is_some())
1870 .finish()
1871 }
1872}
1873
1874#[cfg(test)]
1875mod tests {
1876 use super::*;
1877 use crate::data::datatable::{DataColumn, DataRow, DataTable, DataValue};
1878 use std::sync::Arc;
1879
1880 #[allow(dead_code)]
1883 fn test_hide_empty_columns_index_fix() {
1884 let mut table = DataTable::new("test");
1886
1887 table.add_column(DataColumn::new("name"));
1889 table.add_column(DataColumn::new("empty1")); table.add_column(DataColumn::new("salary"));
1891 table.add_column(DataColumn::new("empty2")); table.add_column(DataColumn::new("department"));
1893
1894 table
1896 .add_row(DataRow::new(vec![
1897 DataValue::String("John".to_string()),
1898 DataValue::Null,
1899 DataValue::Integer(50000),
1900 DataValue::Null,
1901 DataValue::String("Engineering".to_string()),
1902 ]))
1903 .unwrap();
1904
1905 table
1906 .add_row(DataRow::new(vec![
1907 DataValue::String("Jane".to_string()),
1908 DataValue::Null,
1909 DataValue::Integer(60000),
1910 DataValue::Null,
1911 DataValue::String("Marketing".to_string()),
1912 ]))
1913 .unwrap();
1914
1915 let mut dataview = DataView::new(Arc::new(table));
1917
1918 assert_eq!(dataview.column_count(), 5);
1920
1921 let hidden_count = dataview.hide_empty_columns();
1923
1924 assert_eq!(hidden_count, 2);
1926
1927 assert_eq!(dataview.column_count(), 3);
1929
1930 let final_columns = dataview.column_names();
1932
1933 assert_eq!(final_columns[0], "name");
1935 assert_eq!(final_columns[1], "salary");
1936 assert_eq!(final_columns[2], "department");
1937
1938 let hidden_columns = dataview.get_hidden_column_names();
1940 assert!(hidden_columns.contains(&"empty1".to_string()));
1941 assert!(hidden_columns.contains(&"empty2".to_string()));
1942 assert_eq!(hidden_columns.len(), 2);
1943 }
1944}