blob: 0ec48ac9f5e199038390e0892f3eeeb4412458db [file] [log] [blame]
use std::collections::{BTreeMap, BTreeSet};
use std::fmt::Debug;
use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct Select<T>
where
T: Selectable,
{
common: T::CommonType,
selects: BTreeMap<String, T::SelectsType>,
}
pub trait Selectable
where
Self: SelectableValue,
{
type ItemType: SelectableValue;
type CommonType: SelectableValue + Default;
type SelectsType: SelectableValue;
fn is_empty(this: &Select<Self>) -> bool;
fn insert(this: &mut Select<Self>, value: Self::ItemType, configuration: Option<String>);
fn items(this: &Select<Self>) -> Vec<(Option<String>, Self::ItemType)>;
fn values(this: &Select<Self>) -> Vec<Self::ItemType>;
fn merge(lhs: Select<Self>, rhs: Select<Self>) -> Select<Self>;
}
// Replace with `trait_alias` once stabilized.
// https://github.com/rust-lang/rust/issues/41517
pub trait SelectableValue
where
Self: Debug + Clone + PartialEq + Eq + Serialize + DeserializeOwned,
{
}
impl<T> SelectableValue for T where T: Debug + Clone + PartialEq + Eq + Serialize + DeserializeOwned {}
// Replace with `trait_alias` once stabilized.
// https://github.com/rust-lang/rust/issues/41517
pub trait SelectableOrderedValue
where
Self: SelectableValue + PartialOrd + Ord,
{
}
impl<T> SelectableOrderedValue for T where T: SelectableValue + PartialOrd + Ord {}
pub trait SelectableScalar
where
Self: SelectableValue,
{
}
impl SelectableScalar for String {}
impl SelectableScalar for bool {}
impl SelectableScalar for i64 {}
// General
impl<T> Select<T>
where
T: Selectable,
{
pub fn new() -> Self {
Self {
common: T::CommonType::default(),
selects: BTreeMap::new(),
}
}
pub fn from_value(value: T::CommonType) -> Self {
Self {
common: value,
selects: BTreeMap::new(),
}
}
pub fn is_empty(&self) -> bool {
T::is_empty(self)
}
pub fn configurations(&self) -> BTreeSet<String> {
self.selects.keys().cloned().collect()
}
pub fn items(&self) -> Vec<(Option<String>, T::ItemType)> {
T::items(self)
}
pub fn values(&self) -> Vec<T::ItemType> {
T::values(self)
}
pub fn insert(&mut self, value: T::ItemType, configuration: Option<String>) {
T::insert(self, value, configuration);
}
pub fn into_parts(self) -> (T::CommonType, BTreeMap<String, T::SelectsType>) {
(self.common, self.selects)
}
pub fn merge(lhs: Self, rhs: Self) -> Self {
T::merge(lhs, rhs)
}
}
impl<T> Default for Select<T>
where
T: Selectable,
{
fn default() -> Self {
Self::new()
}
}
impl<'de, T> Deserialize<'de> for Select<T>
where
T: Selectable,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum Either<T>
where
T: Selectable,
{
Select {
common: T::CommonType,
selects: BTreeMap<String, T::SelectsType>,
},
Value(T::CommonType),
}
let either = Either::<T>::deserialize(deserializer)?;
match either {
Either::Select { common, selects } => Ok(Self { common, selects }),
Either::Value(common) => Ok(Self {
common,
selects: BTreeMap::new(),
}),
}
}
}
// Scalar
impl<T> Selectable for T
where
T: SelectableScalar,
{
type ItemType = T;
type CommonType = Option<Self::ItemType>;
type SelectsType = Self::ItemType;
fn is_empty(this: &Select<Self>) -> bool {
this.common.is_none() && this.selects.is_empty()
}
fn items(this: &Select<Self>) -> Vec<(Option<String>, Self::ItemType)> {
let mut result = Vec::new();
if let Some(value) = this.common.as_ref() {
result.push((None, value.clone()));
}
result.extend(
this.selects
.iter()
.map(|(configuration, value)| (Some(configuration.clone()), value.clone())),
);
result
}
fn values(this: &Select<Self>) -> Vec<Self::ItemType> {
let mut result = Vec::new();
if let Some(value) = this.common.as_ref() {
result.push(value.clone());
}
result.extend(this.selects.values().cloned());
result
}
fn insert(this: &mut Select<Self>, value: Self::ItemType, configuration: Option<String>) {
match configuration {
None => {
this.selects
.retain(|_, existing_value| existing_value != &value);
this.common = Some(value);
}
Some(configuration) => {
if Some(&value) != this.common.as_ref() {
this.selects.insert(configuration, value);
}
}
}
}
fn merge(lhs: Select<Self>, rhs: Select<Self>) -> Select<Self> {
let mut result: Select<Self> = Select::new();
if let Some(value) = lhs.common {
result.insert(value, None);
}
if let Some(value) = rhs.common {
result.insert(value, None);
}
for (configuration, value) in lhs.selects.into_iter() {
result.insert(value, Some(configuration));
}
for (configuration, value) in rhs.selects.into_iter() {
result.insert(value, Some(configuration));
}
result
}
}
// Vec<T>
impl<T> Selectable for Vec<T>
where
T: SelectableValue,
{
type ItemType = T;
type CommonType = Vec<T>;
type SelectsType = Vec<T>;
fn is_empty(this: &Select<Self>) -> bool {
this.common.is_empty() && this.selects.is_empty()
}
fn items(this: &Select<Self>) -> Vec<(Option<String>, Self::ItemType)> {
let mut result = Vec::new();
result.extend(this.common.iter().map(|value| (None, value.clone())));
result.extend(this.selects.iter().flat_map(|(configuration, values)| {
values
.iter()
.map(|value| (Some(configuration.clone()), value.clone()))
}));
result
}
fn values(this: &Select<Self>) -> Vec<Self::ItemType> {
let mut result = Vec::new();
result.extend(this.common.iter().cloned());
result.extend(
this.selects
.values()
.flat_map(|values| values.iter().cloned()),
);
result
}
fn insert(this: &mut Select<Self>, value: Self::ItemType, configuration: Option<String>) {
match configuration {
None => this.common.push(value),
Some(configuration) => this.selects.entry(configuration).or_default().push(value),
}
}
fn merge(lhs: Select<Self>, rhs: Select<Self>) -> Select<Self> {
let mut result: Select<Self> = Select::new();
for value in lhs.common.into_iter() {
result.insert(value, None);
}
for value in rhs.common.into_iter() {
result.insert(value, None);
}
for (configuration, values) in lhs.selects.into_iter() {
for value in values.into_iter() {
result.insert(value, Some(configuration.clone()));
}
}
for (configuration, values) in rhs.selects.into_iter() {
for value in values.into_iter() {
result.insert(value, Some(configuration.clone()));
}
}
result
}
}
// BTreeSet<T>
impl<T> Selectable for BTreeSet<T>
where
T: SelectableOrderedValue,
{
type ItemType = T;
type CommonType = BTreeSet<T>;
type SelectsType = BTreeSet<T>;
fn is_empty(this: &Select<Self>) -> bool {
this.common.is_empty() && this.selects.is_empty()
}
fn items(this: &Select<Self>) -> Vec<(Option<String>, Self::ItemType)> {
let mut result = Vec::new();
result.extend(this.common.iter().map(|value| (None, value.clone())));
result.extend(this.selects.iter().flat_map(|(configuration, values)| {
values
.iter()
.map(|value| (Some(configuration.clone()), value.clone()))
}));
result
}
fn values(this: &Select<Self>) -> Vec<Self::ItemType> {
let mut result = Vec::new();
result.extend(this.common.iter().cloned());
result.extend(
this.selects
.values()
.flat_map(|values| values.iter().cloned()),
);
result
}
fn insert(this: &mut Select<Self>, value: Self::ItemType, configuration: Option<String>) {
match configuration {
None => {
this.selects.retain(|_, set| {
set.remove(&value);
!set.is_empty()
});
this.common.insert(value);
}
Some(configuration) => {
if !this.common.contains(&value) {
this.selects.entry(configuration).or_default().insert(value);
}
}
}
}
fn merge(lhs: Select<Self>, rhs: Select<Self>) -> Select<Self> {
let mut result: Select<Self> = Select::new();
for value in lhs.common.into_iter() {
result.insert(value, None);
}
for value in rhs.common.into_iter() {
result.insert(value, None);
}
for (configuration, values) in lhs.selects.into_iter() {
for value in values {
result.insert(value, Some(configuration.clone()));
}
}
for (configuration, values) in rhs.selects.into_iter() {
for value in values {
result.insert(value, Some(configuration.clone()));
}
}
result
}
}
impl<T> Select<BTreeSet<T>>
where
T: SelectableOrderedValue,
{
pub fn map<U, F>(self, func: F) -> Select<BTreeSet<U>>
where
U: SelectableOrderedValue,
F: Copy + FnMut(T) -> U,
{
Select {
common: self.common.into_iter().map(func).collect(),
selects: self
.selects
.into_iter()
.map(|(configuration, values)| {
(configuration, values.into_iter().map(func).collect())
})
.collect(),
}
}
}
// BTreeMap<String, T>
impl<T> Selectable for BTreeMap<String, T>
where
T: SelectableValue,
{
type ItemType = (String, T);
type CommonType = BTreeMap<String, T>;
type SelectsType = BTreeMap<String, T>;
fn is_empty(this: &Select<Self>) -> bool {
this.common.is_empty() && this.selects.is_empty()
}
fn items(this: &Select<Self>) -> Vec<(Option<String>, Self::ItemType)> {
let mut result = Vec::new();
result.extend(
this.common
.iter()
.map(|(key, value)| (None, (key.clone(), value.clone()))),
);
result.extend(this.selects.iter().flat_map(|(configuration, values)| {
values
.iter()
.map(|(key, value)| (Some(configuration.clone()), (key.clone(), value.clone())))
}));
result
}
fn values(this: &Select<Self>) -> Vec<Self::ItemType> {
let mut result = Vec::new();
result.extend(
this.common
.iter()
.map(|(key, value)| (key.clone(), value.clone())),
);
result.extend(this.selects.values().flat_map(|values| {
values
.iter()
.map(|(key, value)| (key.clone(), value.clone()))
}));
result
}
fn insert(
this: &mut Select<Self>,
(key, value): Self::ItemType,
configuration: Option<String>,
) {
match configuration {
None => {
this.selects.retain(|_, map| {
map.remove(&key);
!map.is_empty()
});
this.common.insert(key, value);
}
Some(configuration) => {
if !this.common.contains_key(&key) {
this.selects
.entry(configuration)
.or_default()
.insert(key, value);
}
}
}
}
fn merge(lhs: Select<Self>, rhs: Select<Self>) -> Select<Self> {
let mut result: Select<Self> = Select::new();
for (key, value) in lhs.common.into_iter() {
result.insert((key, value), None);
}
for (key, value) in rhs.common.into_iter() {
result.insert((key, value), None);
}
for (configuration, entries) in lhs.selects.into_iter() {
for (key, value) in entries {
result.insert((key, value), Some(configuration.clone()));
}
}
for (configuration, entries) in rhs.selects.into_iter() {
for (key, value) in entries {
result.insert((key, value), Some(configuration.clone()));
}
}
result
}
}