pub mod adr;
pub mod binary;
pub mod client_pid_map;
pub mod datetime;
pub mod gender;
pub mod geo;
pub mod language;
pub mod n;
pub mod org;
pub mod text;
pub mod uri;
pub mod utc_offset;
use core::{error, fmt, ops, str};
use alloc::{
borrow::Cow,
string::{String, ToString},
vec::Vec,
};
use crate::value::{
adr::VcardAdr,
binary::VcardBinary,
client_pid_map::VcardClientPidMap,
datetime::{VcardDateAndOrTime, VcardTimestamp},
gender::VcardGender,
geo::VcardGeo,
language::VcardLanguageTag,
n::VcardN,
org::VcardOrg,
text::{VcardText, VcardTextList},
uri::VcardUri,
utc_offset::VcardUtcOffset,
};
#[derive(Debug)]
pub struct ParseVcardValueKindError(
String,
);
impl fmt::Display for ParseVcardValueKindError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Cannot parse vCard value type `{}`", self.0)
}
}
impl error::Error for ParseVcardValueKindError {}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum VcardValueKind {
Adr,
Binary,
ClientPidMap,
DateAndOrTime,
Gender,
Geo,
LanguageTag,
N,
Org,
Text,
TextList,
Timestamp,
Uri,
UtcOffset,
}
impl str::FromStr for VcardValueKind {
type Err = ParseVcardValueKindError;
fn from_str(kind: &str) -> Result<Self, Self::Err> {
match kind {
kind if kind.eq_ignore_ascii_case("ADR") => Ok(Self::Adr),
kind if kind.eq_ignore_ascii_case("B") => Ok(Self::Binary),
kind if kind.eq_ignore_ascii_case("BINARY") => Ok(Self::Binary),
kind if kind.eq_ignore_ascii_case("CLIENTPIDMAP") => Ok(Self::ClientPidMap),
kind if kind.eq_ignore_ascii_case("DATE") => Ok(Self::DateAndOrTime),
kind if kind.eq_ignore_ascii_case("DATE-AND-OR-TIME") => Ok(Self::DateAndOrTime),
kind if kind.eq_ignore_ascii_case("DATE-TIME") => Ok(Self::DateAndOrTime),
kind if kind.eq_ignore_ascii_case("GENDER") => Ok(Self::Gender),
kind if kind.eq_ignore_ascii_case("GEO") => Ok(Self::Geo),
kind if kind.eq_ignore_ascii_case("LANGUAGE-TAG") => Ok(Self::LanguageTag),
kind if kind.eq_ignore_ascii_case("N") => Ok(Self::N),
kind if kind.eq_ignore_ascii_case("ORG") => Ok(Self::Org),
kind if kind.eq_ignore_ascii_case("TEXT") => Ok(Self::Text),
kind if kind.eq_ignore_ascii_case("TEXT-LIST") => Ok(Self::TextList),
kind if kind.eq_ignore_ascii_case("TIME") => Ok(Self::DateAndOrTime),
kind if kind.eq_ignore_ascii_case("TIMESTAMP") => Ok(Self::Timestamp),
kind if kind.eq_ignore_ascii_case("URI") => Ok(Self::Uri),
kind if kind.eq_ignore_ascii_case("URL") => Ok(Self::Uri),
kind if kind.eq_ignore_ascii_case("UTC-OFFSET") => Ok(Self::UtcOffset),
_ => Err(ParseVcardValueKindError(kind.to_string())),
}
}
}
impl ops::Deref for VcardValueKind {
type Target = str;
fn deref(&self) -> &Self::Target {
match self {
Self::Adr => "ADR",
Self::Binary => "BINARY",
Self::ClientPidMap => "CLIENTPIDMAP",
Self::DateAndOrTime => "DATE-AND-OR-TIME",
Self::Gender => "GENDER",
Self::Geo => "GEO",
Self::LanguageTag => "LANGUAGE-TAG",
Self::N => "N",
Self::Org => "ORG",
Self::Text => "TEXT",
Self::TextList => "TEXT-LIST",
Self::Timestamp => "TIMESTAMP",
Self::Uri => "URI",
Self::UtcOffset => "UTC-OFFSET",
}
}
}
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum VcardValue<'a> {
Adr(VcardAdr<'a>),
Binary(VcardBinary<'a>),
ClientPidMap(VcardClientPidMap<'a>),
DateAndOrTime(VcardDateAndOrTime<'a>),
Gender(VcardGender<'a>),
Geo(VcardGeo<'a>),
LanguageTag(VcardLanguageTag<'a>),
N(VcardN<'a>),
Org(VcardOrg<'a>),
Text(VcardText<'a>),
TextList(VcardTextList<'a>),
Timestamp(VcardTimestamp<'a>),
Uri(VcardUri<'a>),
UtcOffset(VcardUtcOffset<'a>),
Unknown(VcardUnknownValue<'a>),
}
impl VcardValue<'_> {
pub fn kind(&self) -> Option<VcardValueKind> {
match self {
Self::Adr(_) => Some(VcardValueKind::Adr),
Self::Binary(_) => Some(VcardValueKind::Binary),
Self::ClientPidMap(_) => Some(VcardValueKind::ClientPidMap),
Self::DateAndOrTime(_) => Some(VcardValueKind::DateAndOrTime),
Self::Gender(_) => Some(VcardValueKind::Gender),
Self::Geo(_) => Some(VcardValueKind::Geo),
Self::LanguageTag(_) => Some(VcardValueKind::LanguageTag),
Self::N(_) => Some(VcardValueKind::N),
Self::Org(_) => Some(VcardValueKind::Org),
Self::Text(_) => Some(VcardValueKind::Text),
Self::TextList(_) => Some(VcardValueKind::TextList),
Self::Timestamp(_) => Some(VcardValueKind::Timestamp),
Self::Uri(_) => Some(VcardValueKind::Uri),
Self::UtcOffset(_) => Some(VcardValueKind::UtcOffset),
Self::Unknown(_) => None,
}
}
pub fn empty(kind: VcardValueKind) -> VcardValue<'static> {
match kind {
VcardValueKind::Adr => VcardValue::Adr(VcardAdr::default()),
VcardValueKind::Binary => VcardValue::Binary(VcardBinary::Uri(Cow::Borrowed(""))),
VcardValueKind::ClientPidMap => VcardValue::ClientPidMap(VcardClientPidMap::default()),
VcardValueKind::DateAndOrTime => {
VcardValue::DateAndOrTime(VcardDateAndOrTime::default())
}
VcardValueKind::Gender => VcardValue::Gender(VcardGender::default()),
VcardValueKind::Geo => VcardValue::Geo(VcardGeo::default()),
VcardValueKind::LanguageTag => VcardValue::LanguageTag(VcardLanguageTag::default()),
VcardValueKind::N => VcardValue::N(VcardN::default()),
VcardValueKind::Org => VcardValue::Org(VcardOrg::default()),
VcardValueKind::Text => VcardValue::Text(VcardText::default()),
VcardValueKind::TextList => VcardValue::TextList(VcardTextList::default()),
VcardValueKind::Timestamp => VcardValue::Timestamp(VcardTimestamp::default()),
VcardValueKind::Uri => VcardValue::Uri(VcardUri::default()),
VcardValueKind::UtcOffset => VcardValue::UtcOffset(VcardUtcOffset::default()),
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct VcardUnknownValue<'a> {
pub components: Vec<Vec<Cow<'a, str>>>,
}
#[cfg(test)]
mod tests {
use core::str::FromStr;
use crate::value::{VcardUnknownValue, VcardValue, VcardValueKind, text::VcardText};
#[test]
fn empty_is_the_inverse_of_kind() {
use crate::value::VcardValueKind::*;
for kind in [
Adr,
Binary,
ClientPidMap,
DateAndOrTime,
Gender,
Geo,
LanguageTag,
N,
Org,
Text,
TextList,
Timestamp,
Uri,
UtcOffset,
] {
assert_eq!(VcardValue::empty(kind).kind(), Some(kind));
}
}
#[test]
fn reports_the_kind_of_a_value_and_none_for_unknown() {
assert_eq!(
VcardValue::Text(VcardText::default()).kind(),
Some(VcardValueKind::Text),
);
assert_eq!(
VcardValue::Unknown(VcardUnknownValue::default()).kind(),
None,
);
}
#[test]
fn maps_value_param_strings_liberally_and_case_insensitively() {
assert_eq!("URI".parse().ok(), Some(VcardValueKind::Uri));
assert_eq!("date".parse().ok(), Some(VcardValueKind::DateAndOrTime));
assert!(VcardValueKind::from_str("bogus").is_err());
}
}