use crate::{
param::VcardParamKind,
prop::VcardPropKind,
tree::{
param::COMMON_PARAMS,
prop::{
adr, agent, anniversary, bday, caladruri, caluri, cardinality::VcardPropCardinality,
categories, class, client_pid_map, created, email, fburl, r#fn, gender, geo,
gramgender, impp, jsprop, key, kind, label, lang, language, logo, mailer, member, n,
name, nickname, note, org, photo, prodid, profile, pronouns, related, rev, role,
socialprofile, sort_string, sound, source, tel, title, tz, uid, url, xml,
},
},
value::VcardValueKind,
version::VcardVersion,
};
pub trait VcardPropSpec {
const KIND: VcardPropKind;
fn allowed_versions() -> &'static [VcardVersion] {
&[VcardVersion::V2_1, VcardVersion::V3_0, VcardVersion::V4_0]
}
fn cardinality(_version: VcardVersion) -> VcardPropCardinality {
VcardPropCardinality::Any
}
fn allowed_values(_version: VcardVersion) -> &'static [VcardValueKind] {
&[VcardValueKind::Text]
}
fn allowed_params(_version: VcardVersion) -> &'static [VcardParamKind] {
COMMON_PARAMS
}
fn value(version: VcardVersion, declared: Option<VcardValueKind>) -> VcardValueKind {
declared
.or_else(|| Self::allowed_values(version).first().copied())
.unwrap_or(VcardValueKind::Text)
}
}
pub(crate) struct VcardPropSpecFns {
pub allowed_versions: fn() -> &'static [VcardVersion],
pub cardinality: fn(VcardVersion) -> VcardPropCardinality,
pub allowed_values: fn(VcardVersion) -> &'static [VcardValueKind],
pub allowed_params: fn(VcardVersion) -> &'static [VcardParamKind],
pub value: fn(VcardVersion, Option<VcardValueKind>) -> VcardValueKind,
}
fn spec_fns<L: VcardPropSpec>() -> VcardPropSpecFns {
VcardPropSpecFns {
allowed_versions: L::allowed_versions,
cardinality: L::cardinality,
allowed_values: L::allowed_values,
allowed_params: L::allowed_params,
value: L::value,
}
}
pub(crate) fn prop_spec(prop: VcardPropKind) -> VcardPropSpecFns {
use VcardPropKind::*;
match prop {
Adr => spec_fns::<adr::ADR>(),
Agent => spec_fns::<agent::AGENT>(),
Anniversary => spec_fns::<anniversary::ANNIVERSARY>(),
Bday => spec_fns::<bday::BDAY>(),
CalAdrUri => spec_fns::<caladruri::CALADRURI>(),
CalUri => spec_fns::<caluri::CALURI>(),
Categories => spec_fns::<categories::CATEGORIES>(),
Class => spec_fns::<class::CLASS>(),
ClientPidMap => spec_fns::<client_pid_map::CLIENTPIDMAP>(),
Created => spec_fns::<created::CREATED>(),
Email => spec_fns::<email::EMAIL>(),
FbUrl => spec_fns::<fburl::FBURL>(),
Fn => spec_fns::<r#fn::FN>(),
Gender => spec_fns::<gender::GENDER>(),
Geo => spec_fns::<geo::GEO>(),
GramGender => spec_fns::<gramgender::GRAMGENDER>(),
Impp => spec_fns::<impp::IMPP>(),
JsProp => spec_fns::<jsprop::JSPROP>(),
Key => spec_fns::<key::KEY>(),
Kind => spec_fns::<kind::KIND>(),
Label => spec_fns::<label::LABEL>(),
Lang => spec_fns::<lang::LANG>(),
Language => spec_fns::<language::LANGUAGE>(),
Logo => spec_fns::<logo::LOGO>(),
Mailer => spec_fns::<mailer::MAILER>(),
Member => spec_fns::<member::MEMBER>(),
N => spec_fns::<n::N>(),
Name => spec_fns::<name::NAME>(),
Nickname => spec_fns::<nickname::NICKNAME>(),
Note => spec_fns::<note::NOTE>(),
Org => spec_fns::<org::ORG>(),
Photo => spec_fns::<photo::PHOTO>(),
ProdId => spec_fns::<prodid::PRODID>(),
Profile => spec_fns::<profile::PROFILE>(),
Pronouns => spec_fns::<pronouns::PRONOUNS>(),
Related => spec_fns::<related::RELATED>(),
Rev => spec_fns::<rev::REV>(),
Role => spec_fns::<role::ROLE>(),
SocialProfile => spec_fns::<socialprofile::SOCIALPROFILE>(),
SortString => spec_fns::<sort_string::SORT_STRING>(),
Sound => spec_fns::<sound::SOUND>(),
Source => spec_fns::<source::SOURCE>(),
Tel => spec_fns::<tel::TEL>(),
Title => spec_fns::<title::TITLE>(),
Tz => spec_fns::<tz::TZ>(),
Uid => spec_fns::<uid::UID>(),
Url => spec_fns::<url::URL>(),
Xml => spec_fns::<xml::XML>(),
}
}
#[cfg(test)]
mod tests {
use crate::{
prop::VcardPropKind, tree::prop::spec::prop_spec, value::VcardValueKind,
version::VcardVersion,
};
#[test]
fn every_property_spec_answers_for_every_version() {
for kind in VcardPropKind::ALL {
let spec = prop_spec(kind);
for version in [VcardVersion::V2_1, VcardVersion::V3_0, VcardVersion::V4_0] {
let _ = (spec.allowed_versions)();
let _ = (spec.cardinality)(version);
let _ = (spec.allowed_values)(version);
let _ = (spec.allowed_params)(version);
let _ = (spec.value)(version, None);
let _ = (spec.value)(version, Some(VcardValueKind::Uri));
}
}
}
}