JMAP for Calendars implementation (part 3)
This commit is contained in:
191
crates/types/src/dead_property.rs
Normal file
191
crates/types/src/dead_property.rs
Normal file
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
|
||||
#[cfg_attr(feature = "test_mode", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "test_mode", serde(tag = "type", content = "data"))]
|
||||
#[rkyv(derive(Debug))]
|
||||
pub enum DeadPropertyTag {
|
||||
ElementStart(DeadElementTag),
|
||||
ElementEnd,
|
||||
Text(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
|
||||
#[cfg_attr(feature = "test_mode", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[rkyv(derive(Debug))]
|
||||
pub struct DeadElementTag {
|
||||
pub name: String,
|
||||
pub attrs: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
|
||||
#[cfg_attr(feature = "test_mode", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "test_mode", serde(transparent))]
|
||||
#[rkyv(derive(Debug))]
|
||||
#[repr(transparent)]
|
||||
pub struct DeadProperty(pub Vec<DeadPropertyTag>);
|
||||
|
||||
impl From<&ArchivedDeadProperty> for DeadProperty {
|
||||
fn from(value: &ArchivedDeadProperty) -> Self {
|
||||
DeadProperty(value.0.iter().map(|tag| tag.into()).collect::<Vec<_>>())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&ArchivedDeadPropertyTag> for DeadPropertyTag {
|
||||
fn from(tag: &ArchivedDeadPropertyTag) -> Self {
|
||||
match tag {
|
||||
ArchivedDeadPropertyTag::ElementStart(tag) => DeadPropertyTag::ElementStart(tag.into()),
|
||||
ArchivedDeadPropertyTag::ElementEnd => DeadPropertyTag::ElementEnd,
|
||||
ArchivedDeadPropertyTag::Text(tag) => DeadPropertyTag::Text(tag.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&ArchivedDeadElementTag> for DeadElementTag {
|
||||
fn from(tag: &ArchivedDeadElementTag) -> Self {
|
||||
DeadElementTag {
|
||||
name: tag.name.to_string(),
|
||||
attrs: tag.attrs.as_ref().map(|s| s.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ArchivedDeadProperty {
|
||||
pub fn find_tag(&self, needle: &str) -> Option<DeadProperty> {
|
||||
let mut depth: u32 = 0;
|
||||
let mut tags = Vec::new();
|
||||
let mut found_tag = false;
|
||||
|
||||
for tag in self.0.iter() {
|
||||
match tag {
|
||||
ArchivedDeadPropertyTag::ElementStart(start) => {
|
||||
if depth == 0 && start.name == needle {
|
||||
found_tag = true;
|
||||
} else if found_tag {
|
||||
tags.push(tag.into());
|
||||
}
|
||||
|
||||
depth += 1;
|
||||
}
|
||||
ArchivedDeadPropertyTag::ElementEnd => {
|
||||
if found_tag {
|
||||
if depth == 1 {
|
||||
break;
|
||||
} else {
|
||||
tags.push(tag.into());
|
||||
}
|
||||
}
|
||||
depth = depth.saturating_sub(1);
|
||||
}
|
||||
ArchivedDeadPropertyTag::Text(_) => {
|
||||
if found_tag {
|
||||
tags.push(tag.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if found_tag {
|
||||
Some(DeadProperty(tags))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DeadProperty {
|
||||
pub fn remove_element(&mut self, element: &DeadElementTag) {
|
||||
let mut depth = 0;
|
||||
let mut remove = false;
|
||||
self.0.retain(|item| match item {
|
||||
DeadPropertyTag::ElementStart(tag) => {
|
||||
if depth == 0 && !remove && tag.name == element.name {
|
||||
remove = true;
|
||||
}
|
||||
depth += 1;
|
||||
|
||||
!remove
|
||||
}
|
||||
DeadPropertyTag::ElementEnd => {
|
||||
depth -= 1;
|
||||
if remove && depth == 0 {
|
||||
remove = false;
|
||||
false
|
||||
} else {
|
||||
!remove
|
||||
}
|
||||
}
|
||||
_ => !remove,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn add_element(&mut self, element: DeadElementTag, values: Vec<DeadPropertyTag>) {
|
||||
self.0.push(DeadPropertyTag::ElementStart(element));
|
||||
self.0.extend(values);
|
||||
self.0.push(DeadPropertyTag::ElementEnd);
|
||||
}
|
||||
|
||||
pub fn size(&self) -> usize {
|
||||
let mut size = 0;
|
||||
for item in &self.0 {
|
||||
match item {
|
||||
DeadPropertyTag::ElementStart(tag) => {
|
||||
size += tag.size();
|
||||
}
|
||||
DeadPropertyTag::ElementEnd => {
|
||||
size += 1;
|
||||
}
|
||||
DeadPropertyTag::Text(text) => {
|
||||
size += text.len();
|
||||
}
|
||||
}
|
||||
}
|
||||
size
|
||||
}
|
||||
}
|
||||
|
||||
impl ArchivedDeadProperty {
|
||||
pub fn size(&self) -> usize {
|
||||
let mut size = 0;
|
||||
for item in self.0.iter() {
|
||||
match item {
|
||||
ArchivedDeadPropertyTag::ElementStart(tag) => {
|
||||
size += tag.size();
|
||||
}
|
||||
ArchivedDeadPropertyTag::ElementEnd => {
|
||||
size += 1;
|
||||
}
|
||||
ArchivedDeadPropertyTag::Text(text) => {
|
||||
size += text.len();
|
||||
}
|
||||
}
|
||||
}
|
||||
size
|
||||
}
|
||||
}
|
||||
|
||||
impl DeadElementTag {
|
||||
pub fn new(name: String, attrs: Option<String>) -> Self {
|
||||
DeadElementTag { name, attrs }
|
||||
}
|
||||
|
||||
pub fn size(&self) -> usize {
|
||||
self.name.len() + self.attrs.as_ref().map_or(0, |attrs| attrs.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl ArchivedDeadElementTag {
|
||||
pub fn size(&self) -> usize {
|
||||
self.name.len() + self.attrs.as_ref().map_or(0, |attrs| attrs.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DeadProperty {
|
||||
fn default() -> Self {
|
||||
DeadProperty(Vec::with_capacity(4))
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,9 @@ pub enum ContactField {
|
||||
pub enum CalendarField {
|
||||
Uid,
|
||||
Created,
|
||||
Updated,
|
||||
Start,
|
||||
Text,
|
||||
Archive,
|
||||
}
|
||||
|
||||
@@ -100,7 +103,10 @@ impl From<CalendarField> for u8 {
|
||||
fn from(value: CalendarField) -> Self {
|
||||
match value {
|
||||
CalendarField::Uid => 0,
|
||||
CalendarField::Text => 1,
|
||||
CalendarField::Created => 2,
|
||||
CalendarField::Updated => 3,
|
||||
CalendarField::Start => 4,
|
||||
CalendarField::Archive => ARCHIVE_FIELD,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ pub mod acl;
|
||||
pub mod blob;
|
||||
pub mod blob_hash;
|
||||
pub mod collection;
|
||||
pub mod dead_property;
|
||||
pub mod field;
|
||||
pub mod id;
|
||||
pub mod keyword;
|
||||
@@ -17,3 +18,22 @@ pub mod type_state;
|
||||
|
||||
pub type DocumentId = u32;
|
||||
pub type ChangeId = u64;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "test_mode", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct TimeRange {
|
||||
pub start: i64,
|
||||
pub end: i64,
|
||||
}
|
||||
|
||||
impl TimeRange {
|
||||
pub fn is_in_range(&self, match_overlap: bool, start: i64, end: i64) -> bool {
|
||||
if !match_overlap {
|
||||
// RFC4791#9.9: (start < DTEND AND end > DTSTART)
|
||||
self.start < end && self.end > start
|
||||
} else {
|
||||
// RFC4791#9.9: ((start < DUE) OR (start <= DTSTART)) AND ((end > DTSTART) OR (end >= DUE))
|
||||
((start < self.end) || (start <= self.start)) && (end > self.start || end >= self.end)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user