use either::Either;
use serde::{Deserialize, Serialize};
use std::{borrow::Borrow, collections::HashSet, hash::Hash};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub(crate) enum ScalarOrArray<T> {
Scalar(T),
Array(Vec<T>),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(transparent)]
pub(super) struct ArrayOrValue<T>(
#[serde(bound(
serialize = "T: Serialize",
deserialize = "T: Deserialize<'de> + Eq + Hash"
))]
#[serde(with = "either::serde_untagged")]
pub(super) Either<T, HashSet<T>>,
);
impl<T> ArrayOrValue<T>
where
T: Eq + std::hash::Hash + Clone + 'static,
{
pub(super) fn single(value: T) -> Self {
Self(Either::Left(value))
}
pub(super) fn add(self, value: impl Into<T>) -> Self {
let value: T = value.into();
match self.0 {
Either::Left(existing) => {
if existing == value {
return Self(Either::Left(existing));
}
let mut set = HashSet::new();
set.insert(existing);
set.insert(value);
Self(Either::Right(set))
}
Either::Right(mut set) => {
if set.is_empty() {
return Self(Either::Left(value));
}
set.insert(value);
Self(Either::Right(set))
}
}
}
pub(super) fn contains<Q>(&self, value: &Q) -> bool
where
T: Borrow<Q> + PartialEq<Q>,
Q: Eq + Hash,
Q: ?Sized,
{
match &self.0 {
Either::Left(v) => v == value,
Either::Right(set) => set.contains(value),
}
}
pub(super) fn iter(&self) -> Box<dyn Iterator<Item = T> + '_> {
match &self.0 {
Either::Left(value) => Box::new(std::iter::once(value).cloned()),
Either::Right(set) => Box::new(set.iter().cloned()),
}
}
pub(super) fn len(&self) -> usize {
match &self.0 {
Either::Left(_) => 1,
Either::Right(set) => set.len(),
}
}
}
impl<T> PartialEq for ArrayOrValue<T>
where
T: Eq + Hash + Clone,
{
fn eq(&self, other: &Self) -> bool {
match (&self.0, &other.0) {
(Either::Left(a), Either::Left(b)) => a == b,
(Either::Right(a), Either::Right(b)) => a == b,
_ => false,
}
}
}
impl<T> FromIterator<T> for ArrayOrValue<T>
where
T: Eq + std::hash::Hash + Clone,
{
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
let mut set = HashSet::new();
for item in iter {
set.insert(item);
}
if set.len() == 1 {
Self(Either::Left(set.into_iter().next().unwrap()))
} else {
Self(Either::Right(set))
}
}
}
impl<T> Default for ArrayOrValue<T> {
fn default() -> Self {
Self(Either::Right(HashSet::default()))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn single() {
let array_or_value = ArrayOrValue::single("value");
assert_eq!(array_or_value.0, Either::Left("value"));
}
#[test]
fn multiple() {
let array_or_value = ArrayOrValue::single("value")
.add("another_value")
.add("value");
if let Either::Right(set) = array_or_value.0 {
assert_eq!(set.len(), 2);
assert!(set.contains(&"value"));
assert!(set.contains(&"another_value"));
} else {
panic!("Expected Either::Right, got {:?}", array_or_value.0);
}
}
mod a_single {
use super::*;
#[test]
fn contains_a_matching_literal() {
let array_or_value = ArrayOrValue::single("value");
assert!(!array_or_value.contains(&"non_existent_value"));
}
#[test]
fn does_not_contains_a_different_literal() {
let array_or_value = ArrayOrValue::single("value");
assert!(!array_or_value.contains(&"non_existent_value"));
}
}
mod an_empty_set {
use super::*;
#[test]
fn becomes_single_when_adding() {
let empty = ArrayOrValue::default();
let single = ArrayOrValue::single("value");
let added = empty.add("value");
assert_eq!(added, single);
}
}
}