1use core::fmt;
11
12use alloc::vec::Vec;
13
14use crate::tree::leaf::VcardLeaf;
15
16#[derive(Clone, Debug)]
20pub struct VcardParamNode<'a> {
21 pub name: VcardLeaf<'a>,
23 pub values: Vec<VcardLeaf<'a>>,
25}
26
27impl<'a> VcardParamNode<'a> {
28 pub fn parse(param: &'a str) -> Self {
30 match param.split_once('=') {
31 Some((name, values)) => Self {
32 name: VcardLeaf::from(name),
33 values: split_param_values(values)
34 .into_iter()
35 .map(VcardLeaf::from)
36 .collect(),
37 },
38 None => Self {
39 name: VcardLeaf::from(param),
40 values: Vec::new(),
41 },
42 }
43 }
44
45 pub(crate) fn into_static(self) -> VcardParamNode<'static> {
47 VcardParamNode {
48 name: self.name.into_static(),
49 values: self
50 .values
51 .into_iter()
52 .map(VcardLeaf::into_static)
53 .collect(),
54 }
55 }
56
57 pub(crate) fn write_bytes(&self, out: &mut Vec<u8>) {
60 out.extend_from_slice(self.name.get().as_bytes());
61
62 if let Some((first, rest)) = self.values.split_first() {
63 out.push(b'=');
64 out.extend_from_slice(first.get().as_bytes());
65 for value in rest {
66 out.push(b',');
67 out.extend_from_slice(value.get().as_bytes());
68 }
69 }
70 }
71}
72
73impl fmt::Display for VcardParamNode<'_> {
74 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75 f.write_str(self.name.get())?;
76
77 if let Some((first, rest)) = self.values.split_first() {
78 write!(f, "={}", first.get())?;
79
80 for value in rest {
81 write!(f, ",{}", value.get())?;
82 }
83 }
84
85 Ok(())
86 }
87}
88
89fn split_param_values(values: &str) -> Vec<&str> {
91 let bytes = values.as_bytes();
92 let mut pieces = Vec::new();
93 let mut start = 0;
94 let mut quoted = false;
95
96 for (i, &byte) in bytes.iter().enumerate() {
97 match byte {
98 b'"' => quoted = !quoted,
99 b',' if !quoted => {
100 pieces.push(&values[start..i]);
101 start = i + 1;
102 }
103 _ => {}
104 }
105 }
106
107 pieces.push(&values[start..]);
108 pieces
109}
110
111#[cfg(test)]
112mod tests {
113 use alloc::string::ToString;
114
115 use crate::tree::param::node::VcardParamNode;
116
117 #[test]
118 fn parses_quoted_values_then_round_trips() {
119 let node = VcardParamNode::parse(r#"TYPE=work,"a,b""#);
120 assert_eq!(node.values.len(), 2);
121 assert_eq!(node.to_string(), r#"TYPE=work,"a,b""#);
122 }
123}