use std::fmt::Write;
pub struct ListView {
cols: i32,
rows: i32,
items: Vec<ListItem>,
primary_key: String,
secondary_keys: Vec<String>,
selected_line: i32,
start_index: i32,
sort_key: Option<String>,
counter: i32,
ordering: Option<Ordering>,
}
impl ListView {
pub fn new(
cols: i32,
rows: i32,
items: &[ListItem],
primary_key: String,
secondary_keys: Vec<String>,
sort_key: Option<String>,
ordering: Option<Ordering>,
) -> Self {
let mut created_listview = Self {
rows,
cols,
counter: 0,
items: items.to_vec(),
primary_key,
secondary_keys,
selected_line: 1,
start_index: 0,
sort_key,
ordering,
};
created_listview.sort();
created_listview
}
pub fn previous(&mut self) {
if self.counter > 0 {
self.counter -= 1;
if self.selected_line > 1 {
self.selected_line -= 1;
} else {
self.start_index -= 1;
}
}
}
pub fn next(&mut self) {
if self.counter < self.items.len() as i32 - 1 {
self.counter += 1;
if self.selected_line == self.rows - 1 {
self.start_index += 1;
} else {
self.selected_line += 1;
}
}
}
pub fn to_last(&mut self) {
self.counter = self.items.len() as i32 - 1;
self.selected_line = self.counter + 1;
if self.selected_line > self.rows - 1 {
self.start_index = self.counter - (self.rows - 2);
self.selected_line = self.rows - 1;
}
}
pub fn to_first(&mut self) {
self.counter = 0;
self.selected_line = 1;
self.start_index = 0;
}
pub fn display(&mut self) -> String {
let mut secondary_keys_len = std::collections::HashMap::new();
for key in &self.secondary_keys {
secondary_keys_len.insert(key.to_string(), key.len() + 2);
}
let mut displayed_items = &*self.items;
for item in displayed_items {
for key_value in &item.data {
if secondary_keys_len.contains_key(key_value.0) {
let tmp = secondary_keys_len[key_value.0];
if key_value.1.len() + 2 > tmp {
*secondary_keys_len.get_mut(key_value.0).unwrap() = key_value.1.len() + 2;
}
}
}
}
let mut secondary_cols = String::new();
for key in &self.secondary_keys {
if let Some(sort_key) = &self.sort_key {
if key == sort_key {
if let Some(ordering) = &self.ordering {
secondary_cols += &if ordering == &Ordering::Default {
format!(
"[[EFFECT_BOLD]]{}[[EFFECT_BOLD]]{}",
key,
" ".repeat(secondary_keys_len[key] - key.len())
)
} else {
format!(
"[[EFFECT_ITALIC]]{}[[EFFECT_ITALIC]]{}",
key,
" ".repeat(secondary_keys_len[key] - key.len())
)
};
} else {
write!(
&mut secondary_cols,
"{}{}",
key,
" ".repeat(secondary_keys_len[key] - key.len())
)
.unwrap();
}
} else {
write!(
&mut secondary_cols,
"{}{}",
key,
" ".repeat(secondary_keys_len[key] - key.len())
)
.unwrap();
}
} else {
write!(
&mut secondary_cols,
"{}{}",
key,
" ".repeat(secondary_keys_len[key] - key.len())
)
.unwrap();
}
}
let mut ordering_spaces_header = 0;
if let Some(sort_key) = &self.sort_key {
if &self.primary_key != sort_key && self.secondary_keys.contains(sort_key) {
if let Some(ordering) = &self.ordering {
ordering_spaces_header = match ordering {
Ordering::Default => 30,
Ordering::Inversed => 32,
};
}
}
}
let mut table_header_primary_col = String::from(&self.primary_key);
if let Some(sort_key) = &self.sort_key {
if &self.primary_key == sort_key {
if let Some(ordering) = &self.ordering {
table_header_primary_col = if ordering == &Ordering::Default {
format!("[[EFFECT_BOLD]]{}[[EFFECT_BOLD]]", self.primary_key)
} else {
format!("[[EFFECT_ITALIC]]{}[[EFFECT_ITALIC]]", self.primary_key)
};
}
}
}
let mut output_string = format!(
"{}{}{}\n",
table_header_primary_col,
" ".repeat(self.cols as usize - self.primary_key.len() - secondary_cols.len() + ordering_spaces_header),
secondary_cols
);
if displayed_items.len() > (self.rows - 1) as usize {
displayed_items =
&self.items[self.start_index as usize..(self.start_index + self.rows - 1) as usize];
}
let mut i = 1;
for item in displayed_items {
let name = item
.name
.chars()
.into_iter()
.take(self.cols as usize - secondary_cols.len() + ordering_spaces_header)
.collect::<String>();
if i == self.selected_line {
write!(
&mut output_string,
"[[EFFECT_REVERSE]]{}{}",
name,
" ".repeat(
self.cols as usize - name.chars().count() - secondary_cols.len() + ordering_spaces_header
)
)
.unwrap();
} else {
write!(
&mut output_string,
"{}{}",
name,
" ".repeat(
self.cols as usize - name.chars().count() - secondary_cols.len() + ordering_spaces_header
)
)
.unwrap();
}
for col in &self.secondary_keys {
let len = secondary_keys_len[col];
if item.data.contains_key(col) {
write!(
&mut output_string,
"{}{}",
item.data[col],
" ".repeat(len - item.data[col].len())
)
.unwrap();
} else {
output_string.push_str(&" ".repeat(len));
}
}
if i == self.selected_line {
output_string += "[[EFFECT_REVERSE]]";
}
output_string += "\n";
i += 1;
}
output_string
}
pub fn resize(&mut self, rows: i32, cols: i32) {
self.rows = rows;
self.cols = cols;
if self.selected_line > self.rows - 1 {
self.selected_line = self.rows - 1;
self.start_index = self.counter - (self.rows - 2);
}
}
pub fn update_items(&mut self, items: &[ListItem]) {
if items.len() < self.counter as usize + 1 {
self.start_index -= self.counter + 1 - items.len() as i32;
self.counter -= self.counter + 1 - items.len() as i32;
}
self.items = items.to_vec();
self.sort();
}
pub fn select(&self) -> &ListItem {
&self.items[self.counter as usize]
}
pub fn sort_by(&mut self, key: Option<String>, ordering: Option<Ordering>) {
self.sort_key = key;
self.ordering = ordering;
self.sort();
}
fn sort(&mut self) {
if let (Some(sort_key), Some(ordering)) = (&self.sort_key, &self.ordering) {
if sort_key == &self.primary_key {
self.items.sort_by(|a, b| {
human_sort::compare(&b.name.to_lowercase(), &a.name.to_lowercase())
});
} else {
self.items.sort_by(|a, b| {
human_sort::compare(
&b.data
.get(sort_key)
.unwrap_or(&String::new())
.to_lowercase(),
&a.data
.get(sort_key)
.unwrap_or(&String::new())
.to_lowercase(),
)
});
}
if *ordering == Ordering::Inversed {
self.items.reverse();
}
}
}
}
#[derive(Clone)]
pub struct ListItem {
pub name: String,
pub data: std::collections::HashMap<String, String>,
}
impl ListItem {
pub fn new(name: &str, data: &std::collections::HashMap<String, String>) -> Self {
Self {
name: String::from(name),
data: data.clone(),
}
}
}
#[derive(PartialEq, Eq)]
#[non_exhaustive]
pub enum Ordering {
Default,
Inversed,
}