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