use super::{MarkType, Schema};
use derivative::Derivative;
use displaydoc::Display;
use serde::{Deserialize, Serialize, Serializer};
use std::fmt::{self, Debug};
use std::{borrow::Cow, convert::TryFrom, hash::Hash};
#[derive(Derivative, Deserialize)]
#[derivative(
Clone(bound = ""),
PartialEq(bound = ""),
Eq(bound = ""),
Default(bound = "")
)]
#[serde(bound = "", try_from = "Vec<S::Mark>")]
pub struct MarkSet<S: Schema> {
content: Vec<S::Mark>,
}
impl<S: Schema> MarkSet<S> {
pub fn new() -> Self {
MarkSet {
content: Vec::new(),
}
}
pub fn from_vec(content: Vec<S::Mark>) -> Self {
MarkSet { content }
}
pub fn contains(&self, mark: &S::Mark) -> bool {
self.content.contains(mark)
}
pub fn is_in_set(&self, mark: &S::Mark) -> bool {
self.content.iter().any(|m| m.r#type() == mark.r#type())
}
pub fn len(&self) -> usize {
self.content.len()
}
pub fn is_empty(&self) -> bool {
self.content.is_empty()
}
pub fn iter(&self) -> std::slice::Iter<'_, S::Mark> {
self.content.iter()
}
pub fn add(&mut self, mark: &S::Mark) {
let mut copy = Vec::new();
let mut placed = false;
let mut modified = false;
for other in &self.content {
if mark == other {
return;
}
if mark.r#type().excludes(other.r#type()) {
modified = true;
continue;
}
if other.r#type().excludes(mark.r#type()) {
return;
}
if !placed && other.r#type().rank() > mark.r#type().rank() {
copy.push(mark.clone());
placed = true;
modified = true;
}
copy.push(other.clone());
}
if !placed {
copy.push(mark.clone());
modified = true;
}
if modified {
self.content = copy;
}
}
pub fn remove(&mut self, mark: &S::Mark) {
if let Some(index) = self.content.iter().position(|m| m == mark) {
self.content.remove(index);
}
}
}
impl<'a, S: Schema> IntoIterator for &'a MarkSet<S> {
type Item = &'a S::Mark;
type IntoIter = std::slice::Iter<'a, S::Mark>;
fn into_iter(self) -> Self::IntoIter {
self.content.iter()
}
}
impl<S: Schema> Serialize for MarkSet<S> {
fn serialize<Sr>(&self, serializer: Sr) -> Result<Sr::Ok, Sr::Error>
where
Sr: Serializer,
{
self.content.serialize(serializer)
}
}
#[derive(Display)]
pub enum MarkSetError {
Duplicates,
}
impl<S: Schema> TryFrom<Vec<S::Mark>> for MarkSet<S> {
type Error = MarkSetError;
fn try_from(value: Vec<S::Mark>) -> Result<Self, Self::Error> {
let mut result = MarkSet::new();
for mark in value {
if result.contains(&mark) {
return Err(MarkSetError::Duplicates);
}
result.add(&mark);
}
Ok(result)
}
}
impl<S: Schema> fmt::Debug for MarkSet<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.content.fmt(f)
}
}
pub trait Mark<S: Schema<Mark = Self>>:
Serialize + for<'de> Deserialize<'de> + Debug + Clone + PartialEq + Eq + Hash
{
fn r#type(&self) -> S::MarkType;
fn attrs_json(&self) -> serde_json::Value {
serde_json::Value::Null
}
fn is_in_set(&self, set: &MarkSet<S>) -> bool {
set.content.iter().any(|m| m == self)
}
fn add_to_set<'a>(&self, set: Cow<'a, MarkSet<S>>) -> Cow<'a, MarkSet<S>> {
let mut copy = Vec::new();
let mut placed = false;
let mut modified = false;
for other in set.iter() {
if self == other {
return set;
}
if self.r#type().excludes(other.r#type()) {
modified = true;
continue;
}
if other.r#type().excludes(self.r#type()) {
return set;
}
if !placed && other.r#type().rank() > self.r#type().rank() {
copy.push(self.clone());
placed = true;
modified = true;
}
copy.push(other.clone());
}
if !placed {
copy.push(self.clone());
modified = true;
}
if modified {
Cow::Owned(MarkSet { content: copy })
} else {
set
}
}
fn remove_from_set<'a>(&self, set: Cow<'a, MarkSet<S>>) -> Cow<'a, MarkSet<S>> {
if let Some(index) = set.content.iter().position(|m| m == self) {
let mut owned_set = set.into_owned();
owned_set.content.remove(index);
Cow::Owned(owned_set)
} else {
set
}
}
fn into_set(self) -> MarkSet<S> {
MarkSet {
content: vec![self],
}
}
}