use std::collections::BTreeSet;
use super::{BoxColumn, ColumnToggle};
use crate::topcoat_compat::href;
const HIDE: &str = "hide";
const SHOW: &str = "show";
const MAX_NAMES: usize = 64;
const MAX_NAME_LEN: usize = 256;
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub(crate) struct ColumnChoice {
hidden: BTreeSet<String>,
shown: BTreeSet<String>,
}
impl ColumnChoice {
pub(crate) fn shows(&self, name: &str, toggle: ColumnToggle) -> bool {
match toggle {
ColumnToggle::Fixed => true,
ColumnToggle::ShownByDefault => !self.hidden.contains(name),
ColumnToggle::HiddenByDefault => self.shown.contains(name),
}
}
pub(crate) fn checked<'a, M: 'a>(
columns: impl IntoIterator<Item = &'a BoxColumn<M>>,
checked: &[&str],
) -> Self {
let mut choice = Self::default();
for column in columns {
let name = column.name();
let wanted = checked.contains(&name);
match column.toggle() {
ColumnToggle::ShownByDefault if !wanted => {
choice.hidden.insert(name.to_string());
}
ColumnToggle::HiddenByDefault if wanted => {
choice.shown.insert(name.to_string());
}
_ => {}
}
}
choice
}
pub(crate) fn is_default(&self) -> bool {
self.hidden.is_empty() && self.shown.is_empty()
}
pub(crate) fn encode(&self) -> String {
let pairs: Vec<(String, &str)> = self
.hidden
.iter()
.map(|name| (HIDE.to_string(), name.as_str()))
.chain(
self.shown
.iter()
.map(|name| (SHOW.to_string(), name.as_str())),
)
.collect();
href::encode_query(&pairs)
}
pub(crate) fn decode(stored: &str) -> Self {
let mut choice = Self::default();
let pairs = form_urlencoded::parse(stored.as_bytes())
.filter(|(_, name)| !name.is_empty() && name.len() <= MAX_NAME_LEN)
.take(MAX_NAMES);
for (param, name) in pairs {
let set = match param.as_ref() {
HIDE => &mut choice.hidden,
SHOW => &mut choice.shown,
_ => continue,
};
set.insert(name.into_owned());
}
choice
}
}
#[cfg(test)]
mod tests;