use xso::{
error::Error,
text::{Base64, StripWhitespace, TextCodec},
AsXml, FromXml,
};
use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
use crate::ns;
use minidom::Element;
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::VCARD, name = "PHOTO")]
pub struct Photo {
#[xml(child)]
pub type_: Type,
#[xml(child)]
pub binval: Binval,
}
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::VCARD, name = "TYPE")]
pub struct Type {
#[xml(text)]
pub data: String,
}
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::VCARD, name = "BINVAL")]
pub struct Binval {
#[xml(text(codec = Base64.filtered(StripWhitespace)))]
pub data: Vec<u8>,
}
pub struct VCard {
pub photo: Option<Photo>,
}
impl TryFrom<Element> for VCard {
type Error = xso::error::Error;
fn try_from(value: Element) -> Result<Self, Self::Error> {
if !value.is("vCard", ns::VCARD) {
return Err(Error::Other(
"Root element is not <vCard xmlns='vcard-temp'>",
));
}
let photo = value
.get_child("PHOTO", ns::VCARD)
.map(|photo| Photo::try_from(photo.clone()))
.transpose()?;
Ok(VCard { photo })
}
}
impl From<VCard> for Element {
fn from(vcard: VCard) -> Element {
Element::builder("vCard", ns::VCARD)
.append_all(vcard.photo)
.build()
}
}
impl IqGetPayload for VCard {}
impl IqSetPayload for VCard {}
impl IqResultPayload for VCard {}
#[cfg(test)]
mod tests {
use super::*;
use base64::Engine;
use std::str::FromStr;
#[test]
fn test_vcard() {
let bytes = [0u8, 1, 2, 129];
let test_vcard = format!(
r"<vCard xmlns='vcard-temp'>
<BDAY>1476-06-09</BDAY>
<ADR>
<CTRY>Italy</CTRY>
<LOCALITY>Verona</LOCALITY>
<HOME/>
</ADR>
<NICKNAME/>
<N><GIVEN>Juliet</GIVEN><FAMILY>Capulet</FAMILY></N>
<EMAIL>jcapulet@shakespeare.lit</EMAIL>
<PHOTO>
<TYPE>image/jpeg</TYPE>
<BINVAL>{}</BINVAL>
</PHOTO>
</vCard>",
base64::prelude::BASE64_STANDARD.encode(&bytes)
);
let test_vcard = Element::from_str(&test_vcard).expect("Failed to parse XML");
let test_vcard = VCard::try_from(test_vcard).expect("Failed to parse vCard");
let photo = test_vcard.photo.expect("No photo found");
assert_eq!(photo.type_.data, "image/jpeg".to_string());
assert_eq!(photo.binval.data, bytes);
}
#[test]
fn test_vcard_with_linebreaks() {
let test_vcard = r"<vCard xmlns='vcard-temp'>
<BDAY>1476-06-09</BDAY>
<ADR>
<CTRY>Italy</CTRY>
<LOCALITY>Verona</LOCALITY>
<HOME/>
</ADR>
<NICKNAME/>
<N><GIVEN>Juliet</GIVEN><FAMILY>Capulet</FAMILY></N>
<EMAIL>jcapulet@shakespeare.lit</EMAIL>
<PHOTO>
<TYPE>image/jpeg</TYPE>
<BINVAL>Zm9v
Cg==</BINVAL>
</PHOTO>
</vCard>";
let test_vcard = Element::from_str(&test_vcard).expect("Failed to parse XML");
let test_vcard = VCard::try_from(test_vcard).expect("Failed to parse vCard");
let photo = test_vcard.photo.expect("No photo found");
assert_eq!(photo.type_.data, "image/jpeg".to_string());
assert_eq!(photo.binval.data, b"foo\n");
}
}