datui_lib/app/
table_switch.rs1use std::path::PathBuf;
7
8use crate::OpenOptions;
9use crate::table::DataTableState;
10
11#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct Table {
14 pub table: Option<String>,
16 pub label: String,
18 pub detail: String,
20}
21
22#[derive(Debug, Clone, PartialEq, Eq)]
24pub struct Tables {
25 pub file: PathBuf,
27 pub tables: Vec<Table>,
28 pub current: Option<usize>,
30}
31
32impl Tables {
33 pub fn several(&self) -> bool {
35 self.tables.len() > 1
36 }
37}
38
39pub fn of(state: &DataTableState, paths: &[PathBuf], options: &OpenOptions) -> Option<Tables> {
42 let [file] = paths else {
43 return None;
44 };
45 let file = file.clone();
46 if let Some(detail) = state.format_detail()
47 && !detail.tables.is_empty()
48 {
49 return Some(from_detail(file, detail, options.table.as_deref()));
50 }
51 if let Some(read) = state.format_read()
52 && !read.spec.is_delimited()
53 && read.spec.records.variants.len() > 1
54 {
55 return Some(record_types(file, &read.spec));
56 }
57 let splits = options.splits.as_deref()?;
58 let current = splits.split.clone()?;
59 if splits.others.is_empty() {
60 return None;
61 }
62 let mut names: Vec<String> = splits.others.clone();
63 names.push(current.clone());
64 names.sort();
65 names.dedup();
66 let at = names.iter().position(|n| *n == current);
67 Some(Tables {
68 file,
69 tables: names
70 .into_iter()
71 .map(|name| Table {
72 table: Some(name.clone()),
73 label: name,
74 detail: "split".to_string(),
75 })
76 .collect(),
77 current: at,
78 })
79}
80
81pub fn several(state: &DataTableState, paths: &[PathBuf], options: &OpenOptions) -> bool {
84 if paths.len() != 1 {
85 return false;
86 }
87 if let Some(detail) = state.format_detail()
88 && !detail.tables.is_empty()
89 {
90 return detail.tables.len() > 1;
91 }
92 if let Some(read) = state.format_read()
93 && !read.spec.is_delimited()
94 && read.spec.records.variants.len() > 1
95 {
96 return true;
97 }
98 options
99 .splits
100 .as_deref()
101 .is_some_and(|s| s.split.is_some() && s.others.iter().any(|o| Some(o) != s.split.as_ref()))
102}
103
104fn from_detail(
107 file: PathBuf,
108 detail: &crate::formats::text_formats::Detail,
109 asked: Option<&str>,
110) -> Tables {
111 let said = |name: &str| {
112 detail
113 .list
114 .iter()
115 .find(|(key, _)| key == name)
116 .map(|(_, value)| match value {
117 crate::formats::model_files::MetaValue::Text(text) => text
118 .split(", ")
119 .filter(|part| *part != "opened")
120 .collect::<Vec<_>>()
121 .join(", "),
122 _ => String::new(),
123 })
124 .unwrap_or_default()
125 };
126 let opened = detail.table.as_deref().or(asked);
127 let current = opened.and_then(|name| detail.tables.iter().position(|t| t == name));
128 Tables {
129 file,
130 tables: detail
131 .tables
132 .iter()
133 .map(|name| Table {
134 table: Some(name.clone()),
135 label: name.clone(),
136 detail: said(name),
137 })
138 .collect(),
139 current,
140 }
141}
142
143fn record_types(file: PathBuf, spec: &crate::formats::Spec) -> Tables {
146 let current = spec.variant.as_deref();
147 let whole = Table {
148 table: None,
149 label: file
150 .file_name()
151 .map(|n| n.to_string_lossy().into_owned())
152 .unwrap_or_else(|| file.display().to_string()),
153 detail: "every record type".to_string(),
154 };
155 let types = crate::formats::members::variant_tables(spec)
156 .into_iter()
157 .map(|t| Table {
158 detail: crate::formats::text_formats::count(
159 t.columns.len() as u64,
160 "column",
161 "columns",
162 ),
163 table: Some(t.name.clone()),
164 label: t.name,
165 });
166 let tables: Vec<Table> = std::iter::once(whole).chain(types).collect();
167 let at = tables.iter().position(|t| t.table.as_deref() == current);
168 Tables {
169 file,
170 tables,
171 current: at,
172 }
173}