1use xso::{
17    text::{Base64, StripWhitespace, TextCodec},
18    AsXml, FromXml,
19};
20
21use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
22use crate::ns;
23use minidom::Element;
24
25#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
27#[xml(namespace = ns::VCARD, name = "PHOTO")]
28pub struct Photo {
29    #[xml(child)]
31    pub type_: Type,
32
33    #[xml(child)]
35    pub binval: Binval,
36}
37
38#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
40#[xml(namespace = ns::VCARD, name = "TYPE")]
41pub struct Type {
42    #[xml(text)]
44    pub data: String,
45}
46
47#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
49#[xml(namespace = ns::VCARD, name = "BINVAL")]
50pub struct Binval {
51    #[xml(text(codec = Base64.filtered(StripWhitespace)))]
53    pub data: Vec<u8>,
54}
55
56#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
58#[xml(namespace = ns::VCARD, name = "vCard")]
59pub struct VCard {
60    #[xml(child(default))]
62    pub photo: Option<Photo>,
63
64    #[xml(element(n = ..))]
66    pub payloads: Vec<Element>,
67}
68
69impl IqSetPayload for VCard {}
70impl IqResultPayload for VCard {}
71
72#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
74#[xml(namespace = ns::VCARD, name = "vCard")]
75pub struct VCardQuery;
76
77impl IqGetPayload for VCardQuery {}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82    use core::str::FromStr;
83
84    #[cfg(target_pointer_width = "32")]
85    #[test]
86    fn test_size() {
87        assert_size!(Photo, 24);
88        assert_size!(Type, 12);
89        assert_size!(Binval, 12);
90        assert_size!(VCard, 36);
91        assert_size!(VCardQuery, 0);
92    }
93
94    #[cfg(target_pointer_width = "64")]
95    #[test]
96    fn test_size() {
97        assert_size!(Photo, 48);
98        assert_size!(Type, 24);
99        assert_size!(Binval, 24);
100        assert_size!(VCard, 72);
101        assert_size!(VCardQuery, 0);
102    }
103
104    #[test]
105    fn test_vcard() {
106        let bytes = [0u8, 1, 2, 129];
108
109        let test_vcard = format!(
111            r"<vCard xmlns='vcard-temp'>
112    <BDAY>1476-06-09</BDAY>
113    <ADR>
114      <CTRY>Italy</CTRY>
115      <LOCALITY>Verona</LOCALITY>
116      <HOME/>
117    </ADR>
118    <NICKNAME/>
119    <N><GIVEN>Juliet</GIVEN><FAMILY>Capulet</FAMILY></N>
120    <EMAIL>jcapulet@shakespeare.lit</EMAIL>
121    <PHOTO>
122      <TYPE>image/jpeg</TYPE>
123      <BINVAL>{}</BINVAL>
124    </PHOTO>
125  </vCard>",
126            base64::Engine::encode(&base64::prelude::BASE64_STANDARD, &bytes)
127        );
128
129        let test_vcard = Element::from_str(&test_vcard).expect("Failed to parse XML");
130        let test_vcard = VCard::try_from(test_vcard).expect("Failed to parse vCard");
131
132        let photo = test_vcard.photo.expect("No photo found");
133
134        assert_eq!(photo.type_.data, "image/jpeg".to_string());
135        assert_eq!(photo.binval.data, bytes);
136    }
137
138    #[test]
139    fn test_vcard_with_linebreaks() {
140        let test_vcard = r"<vCard xmlns='vcard-temp'>
143    <BDAY>1476-06-09</BDAY>
144    <ADR>
145      <CTRY>Italy</CTRY>
146      <LOCALITY>Verona</LOCALITY>
147      <HOME/>
148    </ADR>
149    <NICKNAME/>
150    <N><GIVEN>Juliet</GIVEN><FAMILY>Capulet</FAMILY></N>
151    <EMAIL>jcapulet@shakespeare.lit</EMAIL>
152    <PHOTO>
153      <TYPE>image/jpeg</TYPE>
154      <BINVAL>Zm9v
155Cg==</BINVAL>
156    </PHOTO>
157  </vCard>";
158
159        let test_vcard = Element::from_str(&test_vcard).expect("Failed to parse XML");
160        let test_vcard = VCard::try_from(test_vcard).expect("Failed to parse vCard");
161
162        let photo = test_vcard.photo.expect("No photo found");
163
164        assert_eq!(photo.type_.data, "image/jpeg".to_string());
165        assert_eq!(photo.binval.data, b"foo\n");
166    }
167}