use alloc::{
borrow::Cow,
string::{String, ToString},
vec::Vec,
};
use crate::{
param::VcardParam,
tree::{
codec::unescape::unescape_param, line::VcardLine, merge::VcardMergeAction,
param::node::VcardParamNode, value::node::VcardValueNode,
},
};
impl VcardValueNode<'_> {
pub(super) fn raw_bytes(&self) -> Vec<u8> {
let mut out = Vec::new();
self.write_bytes(&mut out);
out
}
pub(super) fn same_value_as(&self, other: &Self) -> bool {
if self.escaper != other.escaper {
return self.raw_bytes() == other.raw_bytes();
}
let count = self.component_count().max(other.component_count());
(0..count).all(|i| {
component_eq(
&self.decode_component_list(i),
&other.decode_component_list(i),
)
})
}
pub(super) fn same_item_bytes_as(&self, other: &Self, item: &str) -> bool {
let raw = |node: &Self| -> Option<Vec<u8>> {
let at = node
.decode_list()
.iter()
.position(|held| held.as_ref() == item)?;
node.raw_list().into_iter().nth(at)
};
match (raw(self), raw(other)) {
(Some(ours), Some(theirs)) => ours == theirs,
_ => false,
}
}
}
impl VcardParamNode<'_> {
pub(super) fn raw_bytes(&self) -> Vec<u8> {
let mut out = Vec::new();
self.write_bytes(&mut out);
out
}
pub(super) fn same_param_as(&self, other: &Self) -> bool {
if self.escaper != other.escaper {
return self.raw_bytes() == other.raw_bytes();
}
self.values.len() == other.values.len()
&& self.values.iter().zip(&other.values).all(|(ours, theirs)| {
unescape_param(ours.get(), self.escaper)
== unescape_param(theirs.get(), other.escaper)
})
}
}
impl VcardParam<'_> {
pub(super) fn key(&self) -> String {
if let Self::Unknown { name, .. } = self {
return name.to_ascii_uppercase();
}
self.kind().expect("a known parameter kind").to_string()
}
pub(super) fn is_unordered(&self) -> bool {
matches!(self, Self::Type(_) | Self::Pid(_))
}
pub(super) fn same_value_as(&self, other: &Self) -> bool {
match (self, other) {
(Self::Type(ours), Self::Type(theirs)) | (Self::Pid(ours), Self::Pid(theirs)) => {
sorted(ours) == sorted(theirs)
}
(ours, theirs) => ours == theirs,
}
}
}
impl<'a> VcardLine<'a> {
pub(super) fn param_node(&self, key: &str, at: usize) -> Option<&VcardParamNode<'a>> {
self.params
.iter()
.filter(|node| node.name.get().eq_ignore_ascii_case(key))
.nth(at)
}
pub(super) fn param_node_mut(
&mut self,
key: &str,
at: usize,
) -> Option<&mut VcardParamNode<'a>> {
self.params
.iter_mut()
.filter(|node| node.name.get().eq_ignore_ascii_case(key))
.nth(at)
}
pub(super) fn raw_param_item(&self, param: &str, index: usize, item: &str) -> Option<&str> {
let node = self.param_node(param, index)?;
node.values
.iter()
.find(|value| unescape_param(value.get(), node.escaper) == item)
.map(|leaf| leaf.get())
}
pub(super) fn same_param_bytes_as(
&self,
other: &Self,
param: &VcardParam<'_>,
index: usize,
) -> bool {
let key = param.key();
let (Some(ours), Some(theirs)) =
(self.param_node(&key, index), other.param_node(&key, index))
else {
return false;
};
if !param.is_unordered() {
return ours.raw_bytes() == theirs.raw_bytes();
}
let items = |node: &VcardParamNode<'_>| {
let mut items: Vec<String> = node
.values
.iter()
.map(|leaf| leaf.get().to_string())
.collect();
items.sort_unstable();
items
};
ours.name.get().eq_ignore_ascii_case(theirs.name.get()) && items(ours) == items(theirs)
}
}
impl VcardMergeAction<'_> {
pub(super) fn same_change_as(&self, other: &Self) -> bool {
use VcardMergeAction::{ParamAdded, ParamChanged, ParamRemoved};
match (self, other) {
(
ParamAdded {
at: left_at,
index: left_index,
param: left,
},
ParamAdded {
at: right_at,
index: right_index,
param: right,
},
)
| (
ParamRemoved {
at: left_at,
index: left_index,
param: left,
},
ParamRemoved {
at: right_at,
index: right_index,
param: right,
},
) => left_at == right_at && left_index == right_index && left.same_value_as(right),
(
ParamChanged {
at: left_at,
index: left_index,
old: left_old,
new: left_new,
},
ParamChanged {
at: right_at,
index: right_index,
old: right_old,
new: right_new,
},
) => {
left_at == right_at
&& left_index == right_index
&& left_old.same_value_as(right_old)
&& left_new.same_value_as(right_new)
}
(left, right) => left == right,
}
}
}
pub(super) fn component_eq(old: &[Cow<'_, str>], new: &[Cow<'_, str>]) -> bool {
let eq = old.len() == new.len()
&& old
.iter()
.zip(new)
.all(|(old, new)| old.as_ref() == new.as_ref());
eq || (old.iter().all(|value| value.is_empty()) && new.iter().all(|value| value.is_empty()))
}
fn sorted<'v>(values: &'v [Cow<'_, str>]) -> Vec<&'v str> {
let mut items: Vec<&str> = values.iter().map(Cow::as_ref).collect();
items.sort_unstable();
items
}