use super::{VCard, VCardEntry, VCardParameterName, VCardProperty, VCardValue, VCardVersion};
use crate::{
common::{
CalendarScale, Data, IanaString, IanaType, PartialDateTime,
writer::{write_bytes, write_jscomps},
},
vcard::{
Jscomp, VCardLevel, VCardParameter, VCardParameterValue, VCardPhonetic, VCardType,
VCardValueType,
},
};
use std::borrow::Cow;
impl VCard {
pub fn uid(&self) -> Option<&str> {
self.property(&VCardProperty::Uid)
.and_then(|e| e.values.first())
.and_then(|v| v.as_text())
}
pub fn property(&self, prop: &VCardProperty) -> Option<&VCardEntry> {
self.entries.iter().find(|entry| &entry.name == prop)
}
pub fn properties<'x, 'y: 'x>(
&'x self,
prop: &'y VCardProperty,
) -> impl Iterator<Item = &'x VCardEntry> + 'x {
self.entries.iter().filter(move |entry| &entry.name == prop)
}
pub fn version(&self) -> Option<VCardVersion> {
self.entries
.iter()
.find(|e| e.name == VCardProperty::Version)
.and_then(|e| {
e.values
.first()
.and_then(|v| v.as_text())
.and_then(VCardVersion::try_parse)
})
}
pub fn size(&self) -> usize {
self.entries.iter().map(|e| e.size()).sum()
}
}
impl VCardEntry {
#[inline]
pub fn parameters(
&self,
prop: &VCardParameterName,
) -> impl Iterator<Item = &VCardParameterValue> {
self.params.iter().filter_map(move |param| {
if ¶m.name == prop {
Some(¶m.value)
} else {
None
}
})
}
pub fn language(&self) -> Option<&str> {
self.parameters(&VCardParameterName::Language)
.find_map(|v| v.as_text())
}
pub fn alt_id(&self) -> Option<&str> {
self.parameters(&VCardParameterName::Altid)
.find_map(|v| v.as_text())
}
pub fn prop_id(&self) -> Option<&str> {
self.parameters(&VCardParameterName::PropId)
.find_map(|v| v.as_text())
}
pub fn phonetic_system(&self) -> Option<IanaType<&VCardPhonetic, &str>> {
self.parameters(&VCardParameterName::Phonetic)
.find_map(|v| v.as_phonetic())
}
pub fn phonetic_script(&self) -> Option<&str> {
self.parameters(&VCardParameterName::Script)
.find_map(|v| v.as_text())
}
pub fn size(&self) -> usize {
self.group.as_ref().map_or(0, |g| g.len())
+ self.name.as_str().len()
+ self.params.iter().map(|p| p.size()).sum::<usize>()
+ self.values.iter().map(|v| v.size()).sum::<usize>()
}
}
impl VCardValue {
pub fn as_text(&self) -> Option<&str> {
match self {
VCardValue::Text(v) => v.as_str().into(),
VCardValue::Sex(v) => v.as_str().into(),
VCardValue::GramGender(v) => v.as_str().into(),
VCardValue::Kind(v) => v.as_str().into(),
VCardValue::Component(v) => v.first().map(|s| s.as_str()),
VCardValue::Integer(_)
| VCardValue::Float(_)
| VCardValue::Boolean(_)
| VCardValue::PartialDateTime(_)
| VCardValue::Binary(_) => None,
}
}
pub fn into_text(self) -> Option<Cow<'static, str>> {
match self {
VCardValue::Text(s) => Some(Cow::Owned(s)),
VCardValue::Sex(v) => Some(Cow::Borrowed(v.as_str())),
VCardValue::GramGender(v) => Some(Cow::Borrowed(v.as_str())),
VCardValue::Kind(v) => Some(Cow::Borrowed(v.as_str())),
VCardValue::Component(v) => Some(Cow::Owned(v.join(","))),
VCardValue::Integer(_)
| VCardValue::Float(_)
| VCardValue::Boolean(_)
| VCardValue::PartialDateTime(_)
| VCardValue::Binary(_) => None,
}
}
pub fn into_uri(self) -> Option<String> {
match self {
VCardValue::Binary(v) => Some(v.to_unwrapped_string()),
VCardValue::Text(v) => Some(v),
_ => None,
}
}
pub fn as_integer(&self) -> Option<i64> {
match self {
VCardValue::Integer(i) => Some(*i),
_ => None,
}
}
pub fn as_float(&self) -> Option<f64> {
match self {
VCardValue::Float(f) => Some(*f),
_ => None,
}
}
pub fn as_boolean(&self) -> Option<bool> {
match self {
VCardValue::Boolean(b) => Some(*b),
_ => None,
}
}
pub fn as_partial_date_time(&self) -> Option<&PartialDateTime> {
match self {
VCardValue::PartialDateTime(dt) => Some(dt),
_ => None,
}
}
pub fn as_binary(&self) -> Option<&Data> {
match self {
VCardValue::Binary(d) => Some(d),
_ => None,
}
}
pub fn size(&self) -> usize {
match self {
VCardValue::Text(v) => v.len(),
VCardValue::Sex(v) => v.as_str().len(),
VCardValue::GramGender(v) => v.as_str().len(),
VCardValue::Kind(v) => v.as_str().len(),
VCardValue::Component(v) => v.iter().map(|s| s.len()).sum::<usize>(),
VCardValue::Integer(_) => std::mem::size_of::<i64>(),
VCardValue::Float(_) => std::mem::size_of::<f64>(),
VCardValue::Boolean(_) => std::mem::size_of::<bool>(),
VCardValue::PartialDateTime(_) => std::mem::size_of::<PartialDateTime>(),
VCardValue::Binary(d) => {
d.content_type.as_ref().map_or(0, |ct| ct.len()) + d.data.len()
}
}
}
}
impl Data {
pub fn to_unwrapped_string(&self) -> String {
use std::fmt::Write;
let mut out = String::with_capacity(
self.data.len().div_ceil(4) + self.content_type.as_ref().map_or(0, |ct| ct.len() + 5),
);
let _ = write!(&mut out, "data:");
if let Some(ct) = &self.content_type {
let _ = write!(&mut out, "{ct};");
}
let _ = write!(&mut out, "base64,");
let _ = write_bytes(&mut out, None, &self.data);
out
}
}
impl VCardParameterValue {
pub fn into_text(self) -> Cow<'static, str> {
match self {
VCardParameterValue::Text(s) => Cow::Owned(s),
VCardParameterValue::ValueType(v) => Cow::Borrowed(v.as_str()),
VCardParameterValue::Type(v) => Cow::Borrowed(v.as_str()),
VCardParameterValue::Calscale(v) => Cow::Borrowed(v.as_str()),
VCardParameterValue::Level(v) => Cow::Borrowed(v.as_str()),
VCardParameterValue::Phonetic(v) => Cow::Borrowed(v.as_str()),
VCardParameterValue::Jscomps(v) => {
let mut jscomps = String::new();
let _ = write_jscomps(&mut jscomps, &mut 0, &v);
Cow::Owned(jscomps)
}
VCardParameterValue::Integer(i) => Cow::Owned(i.to_string()),
VCardParameterValue::Timestamp(t) => Cow::Owned(t.to_string()),
VCardParameterValue::Bool(b) => Cow::Borrowed(if b { "true" } else { "false" }),
VCardParameterValue::Null => Cow::Borrowed(""),
}
}
pub fn as_text(&self) -> Option<&str> {
match self {
VCardParameterValue::Text(v) => Some(v.as_str()),
VCardParameterValue::ValueType(v) => Some(v.as_str()),
VCardParameterValue::Type(v) => Some(v.as_str()),
VCardParameterValue::Calscale(v) => Some(v.as_str()),
VCardParameterValue::Level(v) => Some(v.as_str()),
VCardParameterValue::Phonetic(v) => Some(v.as_str()),
VCardParameterValue::Jscomps(_)
| VCardParameterValue::Integer(_)
| VCardParameterValue::Timestamp(_)
| VCardParameterValue::Bool(_)
| VCardParameterValue::Null => None,
}
}
pub fn into_phonetic(self) -> Option<IanaType<VCardPhonetic, String>> {
match self {
VCardParameterValue::Phonetic(v) => Some(IanaType::Iana(v)),
VCardParameterValue::Text(v) => Some(IanaType::Other(v)),
_ => None,
}
}
pub fn as_phonetic(&self) -> Option<IanaType<&VCardPhonetic, &str>> {
match self {
VCardParameterValue::Phonetic(v) => Some(IanaType::Iana(v)),
VCardParameterValue::Text(v) => Some(IanaType::Other(v.as_str())),
_ => None,
}
}
pub fn into_calscale(self) -> Option<IanaType<CalendarScale, String>> {
match self {
VCardParameterValue::Calscale(v) => Some(IanaType::Iana(v)),
VCardParameterValue::Text(v) => Some(IanaType::Other(v)),
_ => None,
}
}
pub fn as_calscale(&self) -> Option<IanaType<&CalendarScale, &str>> {
match self {
VCardParameterValue::Calscale(v) => Some(IanaType::Iana(v)),
VCardParameterValue::Text(v) => Some(IanaType::Other(v.as_str())),
_ => None,
}
}
pub fn into_level(self) -> Option<IanaType<VCardLevel, String>> {
match self {
VCardParameterValue::Level(v) => Some(IanaType::Iana(v)),
VCardParameterValue::Text(v) => Some(IanaType::Other(v)),
_ => None,
}
}
pub fn as_level(&self) -> Option<IanaType<&VCardLevel, &str>> {
match self {
VCardParameterValue::Level(v) => Some(IanaType::Iana(v)),
VCardParameterValue::Text(v) => Some(IanaType::Other(v.as_str())),
_ => None,
}
}
pub fn into_type(self) -> Option<IanaType<VCardType, String>> {
match self {
VCardParameterValue::Type(v) => Some(IanaType::Iana(v)),
VCardParameterValue::Text(v) => Some(IanaType::Other(v)),
_ => None,
}
}
pub fn as_type(&self) -> Option<IanaType<&VCardType, &str>> {
match self {
VCardParameterValue::Type(v) => Some(IanaType::Iana(v)),
VCardParameterValue::Text(v) => Some(IanaType::Other(v.as_str())),
_ => None,
}
}
pub fn into_value_type(self) -> Option<IanaType<VCardValueType, String>> {
match self {
VCardParameterValue::ValueType(v) => Some(IanaType::Iana(v)),
VCardParameterValue::Text(v) => Some(IanaType::Other(v)),
_ => None,
}
}
pub fn as_value_type(&self) -> Option<IanaType<&VCardValueType, &str>> {
match self {
VCardParameterValue::ValueType(v) => Some(IanaType::Iana(v)),
VCardParameterValue::Text(v) => Some(IanaType::Other(v.as_str())),
_ => None,
}
}
pub fn as_bool(&self) -> Option<IanaType<bool, &str>> {
match self {
VCardParameterValue::Bool(b) => Some(IanaType::Iana(*b)),
VCardParameterValue::Text(v) => Some(IanaType::Other(v.as_str())),
_ => None,
}
}
pub fn as_integer(&self) -> Option<IanaType<u32, &str>> {
match self {
VCardParameterValue::Integer(i) => Some(IanaType::Iana(*i)),
VCardParameterValue::Text(v) => Some(IanaType::Other(v.as_str())),
_ => None,
}
}
pub fn into_timestamp(self) -> Option<IanaType<i64, String>> {
match self {
VCardParameterValue::Timestamp(t) => Some(IanaType::Iana(t)),
VCardParameterValue::Text(v) => Some(IanaType::Other(v)),
_ => None,
}
}
pub fn as_timestamp(&self) -> Option<IanaType<i64, &str>> {
match self {
VCardParameterValue::Timestamp(t) => Some(IanaType::Iana(*t)),
VCardParameterValue::Text(v) => Some(IanaType::Other(v.as_str())),
_ => None,
}
}
pub fn into_jscomps(self) -> Option<Vec<Jscomp>> {
match self {
VCardParameterValue::Jscomps(v) => Some(v),
_ => None,
}
}
pub fn size(&self) -> usize {
match self {
VCardParameterValue::Text(v) => v.len(),
VCardParameterValue::ValueType(v) => v.as_str().len(),
VCardParameterValue::Type(v) => v.as_str().len(),
VCardParameterValue::Calscale(v) => v.as_str().len(),
VCardParameterValue::Level(v) => v.as_str().len(),
VCardParameterValue::Phonetic(v) => v.as_str().len(),
VCardParameterValue::Jscomps(v) => v.iter().map(|j| j.size()).sum::<usize>(),
VCardParameterValue::Integer(_) => std::mem::size_of::<u32>(),
VCardParameterValue::Timestamp(_) => std::mem::size_of::<i64>(),
VCardParameterValue::Bool(_) => std::mem::size_of::<bool>(),
VCardParameterValue::Null => 0,
}
}
}
impl VCardParameter {
pub fn size(&self) -> usize {
self.name.as_str().len() + self.value.size()
}
}
impl Jscomp {
pub fn size(&self) -> usize {
match self {
Jscomp::Entry { .. } => std::mem::size_of::<Jscomp>(),
Jscomp::Separator(value) => value.len(),
}
}
}