use alloc::{
borrow::Cow,
string::{String, ToString},
vec::Vec,
};
use serde_json::{Map, Value};
use crate::{jcard::export::merge_param, param::VcardParam};
pub(super) struct ConvertedParams<'a> {
contexts: Map<String, Value>,
features: Map<String, Value>,
pref: Option<u64>,
prop_id: Option<String>,
label: Option<&'a VcardParam<'a>>,
media_type: Option<&'a VcardParam<'a>>,
sort_as: Option<&'a VcardParam<'a>>,
geo: Option<&'a VcardParam<'a>>,
tz: Option<&'a VcardParam<'a>>,
service_type: Option<&'a VcardParam<'a>>,
username: Option<&'a VcardParam<'a>>,
rest: Vec<&'a VcardParam<'a>>,
}
impl<'a> ConvertedParams<'a> {
pub(super) fn split(params: &'a [VcardParam<'a>], phone: bool) -> Self {
let mut split = Self {
contexts: Map::new(),
features: Map::new(),
pref: None,
prop_id: None,
label: None,
media_type: None,
sort_as: None,
geo: None,
tz: None,
service_type: None,
username: None,
rest: Vec::new(),
};
for param in params {
match param {
VcardParam::Type(values) => {
for value in values.iter().filter(|value| !value.is_empty()) {
split.r#type(&value.to_ascii_lowercase(), phone);
}
}
VcardParam::Pref(value) => split.pref = value.trim().parse().ok(),
VcardParam::PropId(value) => split.prop_id = Some(value.to_string()),
VcardParam::Label(_) => split.label = Some(param),
VcardParam::MediaType(_) => split.media_type = Some(param),
VcardParam::SortAs(_) => split.sort_as = Some(param),
VcardParam::Geo(_) => split.geo = Some(param),
VcardParam::Tz(_) => split.tz = Some(param),
VcardParam::ServiceType(_) => split.service_type = Some(param),
VcardParam::Username(_) => split.username = Some(param),
VcardParam::Value(_) => {}
param => split.rest.push(param),
}
}
split
}
fn r#type(&mut self, value: &str, phone: bool) {
match value {
"work" => {
self.contexts.insert("work".into(), Value::Bool(true));
}
"home" => {
self.contexts.insert("private".into(), Value::Bool(true));
}
"pref" => {
self.pref.get_or_insert(1);
}
"cell" if phone => {
self.features.insert("mobile".into(), Value::Bool(true));
}
value if phone => {
self.features.insert(value.into(), Value::Bool(true));
}
value => {
self.contexts.insert(value.into(), Value::Bool(true));
}
}
}
pub(super) fn take_prop_id(&mut self) -> Option<String> {
self.prop_id.take()
}
pub(super) fn take_label(&mut self) -> Option<&'a str> {
match self.label.take() {
Some(VcardParam::Label(value)) => Some(value.as_ref()),
_ => None,
}
}
pub(super) fn take_media_type(&mut self) -> Option<&'a str> {
match self.media_type.take() {
Some(VcardParam::MediaType(value)) => Some(value.as_ref()),
_ => None,
}
}
pub(super) fn take_sort_as(&mut self) -> Option<&'a [Cow<'a, str>]> {
match self.sort_as.take() {
Some(VcardParam::SortAs(values)) => Some(values.as_slice()),
_ => None,
}
}
pub(super) fn take_geo(&mut self) -> Option<&'a str> {
match self.geo.take() {
Some(VcardParam::Geo(value)) => Some(value.as_ref()),
_ => None,
}
}
pub(super) fn take_tz(&mut self) -> Option<&'a str> {
match self.tz.take() {
Some(VcardParam::Tz(value)) => Some(value.as_ref()),
_ => None,
}
}
pub(super) fn take_service_type(&mut self) -> Option<&'a str> {
match self.service_type.take() {
Some(VcardParam::ServiceType(value)) => Some(value.as_ref()),
_ => None,
}
}
pub(super) fn take_username(&mut self) -> Option<&'a str> {
match self.username.take() {
Some(VcardParam::Username(value)) => Some(value.as_ref()),
_ => None,
}
}
pub(super) fn finish(self, object: &mut Map<String, Value>) {
if !self.contexts.is_empty() {
object.insert("contexts".into(), Value::Object(self.contexts));
}
if !self.features.is_empty() {
object.insert("features".into(), Value::Object(self.features));
}
if let Some(pref) = self.pref {
object.insert("pref".into(), Value::from(pref));
}
let unconsumed = [
self.label,
self.media_type,
self.sort_as,
self.geo,
self.tz,
self.service_type,
self.username,
];
let mut escaped = Map::new();
for param in self
.rest
.into_iter()
.chain(unconsumed.into_iter().flatten())
{
let (key, value) = param.to_jcard();
merge_param(&mut escaped, key, value);
}
if !escaped.is_empty() {
object.insert("vCardParams".into(), Value::Object(escaped));
}
}
}