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 #[must_use]
1397 pub fn with_max_rows(mut self, max_rows: usize) -> Self {
1398 self.limit = Some(self.row_count().min(max_rows));
1399 self
1400 }
1401
1402 #[must_use]
1409 pub fn windowed_row_indices(&self) -> &[usize] {
1410 let start = self.offset.min(self.visible_rows.len());
1411 let end = match self.limit {
1412 Some(limit) => start.saturating_add(limit).min(self.visible_rows.len()),
1413 None => self.visible_rows.len(),
1414 };
1415 &self.visible_rows[start..end]
1416 }
1417
1418 pub fn shrink_to_fit(&mut self) {
1420 self.visible_rows.shrink_to_fit();
1421 self.visible_columns.shrink_to_fit();
1422 self.pinned_columns.shrink_to_fit();
1423 self.base_rows.shrink_to_fit();
1424 self.base_columns.shrink_to_fit();
1425 self.matching_columns.shrink_to_fit();
1426 self.virtual_columns.shrink_to_fit();
1427 }
1428
1429 pub fn search_columns(&mut self, pattern: &str) {
1433 self.column_search_pattern = if pattern.is_empty() {
1434 None
1435 } else {
1436 Some(pattern.to_string())
1437 };
1438
1439 if pattern.is_empty() {
1440 self.matching_columns.clear();
1441 self.current_column_match = 0;
1442 return;
1443 }
1444
1445 let pattern_lower = pattern.to_lowercase();
1447 self.matching_columns = self
1448 .visible_columns
1449 .iter()
1450 .enumerate()
1451 .filter_map(|(visible_idx, &source_idx)| {
1452 let col_name = &self.source.columns[source_idx].name;
1453 if col_name.to_lowercase().contains(&pattern_lower) {
1454 debug!(target: "column_search",
1455 "Found match: '{}' at visible_idx={}, source_idx={}",
1456 col_name, visible_idx, source_idx);
1457 Some((visible_idx, col_name.clone()))
1458 } else {
1459 None
1460 }
1461 })
1462 .collect();
1463
1464 debug!(target: "column_search",
1465 "Total matches found: {}, visible_columns.len()={}, pattern='{}'",
1466 self.matching_columns.len(), self.visible_columns.len(), pattern);
1467
1468 self.current_column_match = 0;
1470 }
1471
1472 pub fn clear_column_search(&mut self) {
1474 self.column_search_pattern = None;
1475 self.matching_columns.clear();
1476 self.current_column_match = 0;
1477 }
1478
1479 pub fn next_column_match(&mut self) -> Option<usize> {
1481 if self.matching_columns.is_empty() {
1482 return None;
1483 }
1484
1485 self.current_column_match = (self.current_column_match + 1) % self.matching_columns.len();
1486 Some(self.matching_columns[self.current_column_match].0)
1487 }
1488
1489 pub fn prev_column_match(&mut self) -> Option<usize> {
1491 if self.matching_columns.is_empty() {
1492 return None;
1493 }
1494
1495 if self.current_column_match == 0 {
1496 self.current_column_match = self.matching_columns.len() - 1;
1497 } else {
1498 self.current_column_match -= 1;
1499 }
1500 Some(self.matching_columns[self.current_column_match].0)
1501 }
1502
1503 #[must_use]
1505 pub fn column_search_pattern(&self) -> Option<&str> {
1506 self.column_search_pattern.as_deref()
1507 }
1508
1509 #[must_use]
1511 pub fn get_matching_columns(&self) -> &[(usize, String)] {
1512 &self.matching_columns
1513 }
1514
1515 #[must_use]
1517 pub fn current_column_match_index(&self) -> usize {
1518 self.current_column_match
1519 }
1520
1521 #[must_use]
1523 pub fn get_current_column_match(&self) -> Option<usize> {
1524 if self.matching_columns.is_empty() {
1525 None
1526 } else {
1527 Some(self.matching_columns[self.current_column_match].0)
1528 }
1529 }
1530
1531 #[must_use]
1533 pub fn has_column_search(&self) -> bool {
1534 self.column_search_pattern.is_some()
1535 }
1536
1537 fn get_real_column_names(&self) -> Vec<String> {
1539 let all_source_names = self.source.column_names();
1540 let display_columns = self.get_display_columns();
1541
1542 display_columns
1543 .iter()
1544 .filter_map(|&idx| all_source_names.get(idx).cloned())
1545 .collect()
1546 }
1547
1548 fn extract_real_values_from_row(&self, full_row: &DataRow) -> Vec<DataValue> {
1550 let mut real_values = Vec::new();
1551 let mut value_idx = 0;
1552
1553 let left_virtual_count = self
1555 .virtual_columns
1556 .iter()
1557 .filter(|vc| matches!(vc.position, VirtualColumnPosition::Left))
1558 .count();
1559
1560 value_idx += left_virtual_count;
1562
1563 let real_column_count = self.get_display_columns().len();
1565 for _ in 0..real_column_count {
1566 if value_idx < full_row.values.len() {
1567 real_values.push(full_row.values[value_idx].clone());
1568 value_idx += 1;
1569 }
1570 }
1571
1572 real_values
1573 }
1574
1575 #[must_use]
1578 pub fn to_json(&self) -> Value {
1579 let column_names = self.get_real_column_names();
1581 let mut rows = Vec::new();
1582
1583 for row_idx in 0..self.row_count() {
1585 if let Some(full_row) = self.get_row(row_idx) {
1586 let real_values = self.extract_real_values_from_row(&full_row);
1588
1589 let mut obj = serde_json::Map::new();
1590 for (col_idx, col_name) in column_names.iter().enumerate() {
1591 if let Some(value) = real_values.get(col_idx) {
1592 let json_value = match value {
1593 DataValue::String(s) => json!(s),
1594 DataValue::InternedString(s) => json!(s.as_ref()),
1595 DataValue::Integer(i) => json!(i),
1596 DataValue::Float(f) => json!(f),
1597 DataValue::Boolean(b) => json!(b),
1598 DataValue::DateTime(dt) => json!(dt),
1599 DataValue::Vector(v) => json!(v),
1600 DataValue::Null => json!(null),
1601 };
1602 obj.insert(col_name.clone(), json_value);
1603 }
1604 }
1605 rows.push(json!(obj));
1606 }
1607 }
1608
1609 json!(rows)
1610 }
1611
1612 pub fn to_csv(&self) -> Result<String> {
1614 let mut csv_output = String::new();
1615 let column_names = self.get_real_column_names();
1617
1618 csv_output.push_str(&column_names.join(","));
1620 csv_output.push('\n');
1621
1622 for row_idx in 0..self.row_count() {
1624 if let Some(full_row) = self.get_row(row_idx) {
1625 let real_values = self.extract_real_values_from_row(&full_row);
1627
1628 let row_strings: Vec<String> = real_values
1629 .iter()
1630 .map(|v| {
1631 let s = v.to_string();
1632 if s.contains(',') || s.contains('"') || s.contains('\n') {
1634 format!("\"{}\"", s.replace('"', "\"\""))
1635 } else {
1636 s
1637 }
1638 })
1639 .collect();
1640 csv_output.push_str(&row_strings.join(","));
1641 csv_output.push('\n');
1642 }
1643 }
1644
1645 Ok(csv_output)
1646 }
1647
1648 pub fn to_tsv(&self) -> Result<String> {
1650 let mut tsv_output = String::new();
1651 let column_names = self.get_real_column_names();
1653
1654 tsv_output.push_str(&column_names.join("\t"));
1656 tsv_output.push('\n');
1657
1658 for row_idx in 0..self.row_count() {
1660 if let Some(full_row) = self.get_row(row_idx) {
1661 let real_values = self.extract_real_values_from_row(&full_row);
1663
1664 let row_strings: Vec<String> = real_values
1665 .iter()
1666 .map(std::string::ToString::to_string)
1667 .collect();
1668 tsv_output.push_str(&row_strings.join("\t"));
1669 tsv_output.push('\n');
1670 }
1671 }
1672
1673 Ok(tsv_output)
1674 }
1675
1676 pub fn get_column_values(&self, column_index: usize) -> Vec<String> {
1678 use tracing::trace;
1679
1680 let mut values = Vec::new();
1681 let row_count = self.row_count();
1682
1683 trace!(
1684 "get_column_values: Getting column {} values from {} visible rows",
1685 column_index,
1686 row_count
1687 );
1688
1689 for row_idx in 0..row_count {
1690 if let Some(row) = self.get_row(row_idx) {
1692 if let Some(value) = row.values.get(column_index) {
1695 let str_value = value
1696 .to_string()
1697 .replace('\t', " ")
1698 .replace('\n', " ")
1699 .replace('\r', "");
1700 values.push(str_value);
1701 } else {
1702 values.push("NULL".to_string());
1703 }
1704 }
1705 }
1706
1707 trace!("get_column_values: Retrieved {} values", values.len());
1708 values
1709 }
1710
1711 #[must_use]
1713 pub fn get_cell_value(&self, row_index: usize, column_index: usize) -> Option<String> {
1714 if let Some(row) = self.get_row(row_index) {
1716 row.values
1719 .get(column_index)
1720 .map(std::string::ToString::to_string)
1721 } else {
1722 None
1723 }
1724 }
1725
1726 #[must_use]
1728 pub fn get_row_values(&self, row_index: usize) -> Option<Vec<String>> {
1729 self.get_row(row_index).map(|row| {
1730 row.values
1731 .iter()
1732 .map(std::string::ToString::to_string)
1733 .collect()
1734 })
1735 }
1736
1737 #[must_use]
1740 pub fn get_row_visual_values(&self, row_index: usize) -> Option<Vec<String>> {
1741 if let Some(row) = self.get_row(row_index) {
1742 let values: Vec<String> = row
1745 .values
1746 .iter()
1747 .map(std::string::ToString::to_string)
1748 .collect();
1749 Some(values)
1750 } else {
1751 None
1752 }
1753 }
1754
1755 #[must_use]
1758 pub fn get_column_index_mapping(&self) -> Vec<(usize, String, usize)> {
1759 let mut mappings = Vec::new();
1760
1761 for (visible_idx, &datatable_idx) in self.visible_columns.iter().enumerate() {
1762 if let Some(column) = self.source.columns.get(datatable_idx) {
1763 mappings.push((visible_idx, column.name.clone(), datatable_idx));
1764 }
1765 }
1766
1767 mappings
1768 }
1769
1770 #[must_use]
1772 pub fn get_column_debug_info(&self) -> String {
1773 let mut info = String::new();
1774 info.push_str("Column Mapping (Visible → DataTable):\n");
1775
1776 let total_columns = self.source.columns.len();
1777 let visible_count = self.visible_columns.len();
1778 let hidden_count = total_columns - visible_count;
1779
1780 info.push_str(&format!(
1781 "Total: {total_columns} columns, Visible: {visible_count}, Hidden: {hidden_count}\n\n"
1782 ));
1783
1784 for (visible_idx, &datatable_idx) in self.visible_columns.iter().enumerate() {
1786 if let Some(column) = self.source.columns.get(datatable_idx) {
1787 let pinned_marker = if self.pinned_columns.contains(&datatable_idx) {
1788 " [PINNED]"
1789 } else {
1790 ""
1791 };
1792 info.push_str(&format!(
1793 " V[{:3}] → DT[{:3}] : {}{}\n",
1794 visible_idx, datatable_idx, column.name, pinned_marker
1795 ));
1796 }
1797 }
1798
1799 if hidden_count > 0 {
1801 info.push_str("\nHidden Columns:\n");
1802 for (idx, column) in self.source.columns.iter().enumerate() {
1803 if !self.visible_columns.contains(&idx) {
1804 info.push_str(&format!(" DT[{:3}] : {}\n", idx, column.name));
1805 }
1806 }
1807 }
1808
1809 if !self.pinned_columns.is_empty() {
1811 info.push_str(&format!("\nPinned Columns: {:?}\n", self.pinned_columns));
1812 }
1813
1814 let is_reordered = self.visible_columns.windows(2).any(|w| w[0] > w[1]);
1816
1817 if is_reordered {
1818 info.push_str("\n⚠️ Column order has been modified from original DataTable order\n");
1819 }
1820
1821 info
1822 }
1823}
1824
1825impl DataProvider for DataView {
1828 fn get_row(&self, index: usize) -> Option<Vec<String>> {
1829 self.get_row(index).map(|row| {
1830 row.values
1831 .iter()
1832 .map(std::string::ToString::to_string)
1833 .collect()
1834 })
1835 }
1836
1837 fn get_column_names(&self) -> Vec<String> {
1838 self.column_names()
1839 }
1840
1841 fn get_row_count(&self) -> usize {
1842 self.row_count()
1843 }
1844
1845 fn get_column_count(&self) -> usize {
1846 self.column_count()
1847 }
1848
1849 fn get_column_widths(&self) -> Vec<usize> {
1850 let mut widths = vec![0; self.column_count()];
1852
1853 for (i, name) in self.column_names().iter().enumerate() {
1855 widths[i] = name.len();
1856 }
1857
1858 let sample_size = 100.min(self.row_count());
1861 for row_idx in 0..sample_size {
1862 if let Some(row) = self.get_row(row_idx) {
1863 for (col_idx, value) in row.values.iter().enumerate() {
1864 if col_idx < widths.len() {
1865 let display_len = value.to_string().len();
1866 widths[col_idx] = widths[col_idx].max(display_len);
1867 }
1868 }
1869 }
1870 }
1871
1872 let terminal_width = crossterm::terminal::size()
1874 .map(|(w, _)| w as usize)
1875 .unwrap_or(120);
1876
1877 let available_width = terminal_width.saturating_sub(10);
1880 let visible_cols = self.visible_columns.len().min(10);
1881
1882 let dynamic_max = if visible_cols > 0 {
1884 (available_width / visible_cols).min(80).max(20)
1885 } else {
1886 40
1887 };
1888
1889 for width in &mut widths {
1891 *width = (*width).clamp(6, dynamic_max);
1892 }
1893
1894 widths
1895 }
1896}
1897
1898impl std::fmt::Debug for DataView {
1900 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1901 f.debug_struct("DataView")
1902 .field("source_name", &self.source.name)
1903 .field("visible_rows", &self.visible_rows.len())
1904 .field("visible_columns", &self.visible_columns.len())
1905 .field("has_filter", &self.filter_pattern.is_some())
1906 .field("has_column_search", &self.column_search_pattern.is_some())
1907 .finish()
1908 }
1909}
1910
1911#[cfg(test)]
1912mod tests {
1913 use super::*;
1914 use crate::data::datatable::{DataColumn, DataRow, DataTable, DataValue};
1915 use std::sync::Arc;
1916
1917 #[allow(dead_code)]
1920 fn test_hide_empty_columns_index_fix() {
1921 let mut table = DataTable::new("test");
1923
1924 table.add_column(DataColumn::new("name"));
1926 table.add_column(DataColumn::new("empty1")); table.add_column(DataColumn::new("salary"));
1928 table.add_column(DataColumn::new("empty2")); table.add_column(DataColumn::new("department"));
1930
1931 table
1933 .add_row(DataRow::new(vec![
1934 DataValue::String("John".to_string()),
1935 DataValue::Null,
1936 DataValue::Integer(50000),
1937 DataValue::Null,
1938 DataValue::String("Engineering".to_string()),
1939 ]))
1940 .unwrap();
1941
1942 table
1943 .add_row(DataRow::new(vec![
1944 DataValue::String("Jane".to_string()),
1945 DataValue::Null,
1946 DataValue::Integer(60000),
1947 DataValue::Null,
1948 DataValue::String("Marketing".to_string()),
1949 ]))
1950 .unwrap();
1951
1952 let mut dataview = DataView::new(Arc::new(table));
1954
1955 assert_eq!(dataview.column_count(), 5);
1957
1958 let hidden_count = dataview.hide_empty_columns();
1960
1961 assert_eq!(hidden_count, 2);
1963
1964 assert_eq!(dataview.column_count(), 3);
1966
1967 let final_columns = dataview.column_names();
1969
1970 assert_eq!(final_columns[0], "name");
1972 assert_eq!(final_columns[1], "salary");
1973 assert_eq!(final_columns[2], "department");
1974
1975 let hidden_columns = dataview.get_hidden_column_names();
1977 assert!(hidden_columns.contains(&"empty1".to_string()));
1978 assert!(hidden_columns.contains(&"empty2".to_string()));
1979 assert_eq!(hidden_columns.len(), 2);
1980 }
1981}