1use crate::{
2 datetime::TimeStamp,
3 message::{Component, Separators},
4};
5use display::ComponentBuilderDisplay;
6use std::{collections::HashMap, fmt::Display};
7
8#[derive(Debug, Clone, PartialEq, Eq)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10pub enum ComponentBuilder {
11 Value(String),
12 Subcomponents(HashMap<usize, String>),
13}
14
15impl Default for ComponentBuilder {
16 fn default() -> Self {
17 ComponentBuilder::Value(String::new())
18 }
19}
20
21impl ComponentBuilder {
22 pub fn with_value(value: String) -> Self {
23 ComponentBuilder::Value(value)
24 }
25
26 pub fn with_subcomponents(subcomponents: HashMap<usize, String>) -> Self {
27 ComponentBuilder::Subcomponents(subcomponents)
28 }
29
30 pub fn value(&self) -> Option<&String> {
31 match self {
32 ComponentBuilder::Value(value) => Some(value),
33 _ => None,
34 }
35 }
36
37 pub fn subcomponents(&self) -> Option<&HashMap<usize, String>> {
38 match self {
39 ComponentBuilder::Subcomponents(subcomponents) => Some(subcomponents),
40 _ => None,
41 }
42 }
43
44 pub fn value_mut(&mut self) -> Option<&mut String> {
45 match self {
46 ComponentBuilder::Value(value) => Some(value),
47 _ => None,
48 }
49 }
50
51 pub fn subcomponents_mut(&mut self) -> Option<&mut HashMap<usize, String>> {
52 match self {
53 ComponentBuilder::Subcomponents(subcomponents) => Some(subcomponents),
54 _ => None,
55 }
56 }
57
58 pub fn has_subcomponents(&self) -> bool {
59 matches!(self, ComponentBuilder::Subcomponents(_))
60 }
61
62 pub fn into_value(self) -> Option<String> {
63 match self {
64 ComponentBuilder::Value(value) => Some(value),
65 _ => None,
66 }
67 }
68
69 pub fn into_subcomponents(self) -> Option<HashMap<usize, String>> {
70 match self {
71 ComponentBuilder::Subcomponents(subcomponents) => Some(subcomponents),
72 _ => None,
73 }
74 }
75
76 pub fn set_value<S: ToString>(&mut self, value: S) {
77 *self = ComponentBuilder::Value(value.to_string());
78 }
79
80 pub fn set_timestamp<T: Into<TimeStamp>>(&mut self, timestamp: T) {
81 *self = ComponentBuilder::Value(timestamp.into().to_string());
82 }
83
84 pub fn set_subcomponents(&mut self, subcomponents: HashMap<usize, String>) {
85 *self = ComponentBuilder::Subcomponents(subcomponents);
86 }
87
88 pub fn set_subcomponent<S: ToString>(&mut self, index: usize, value: S) {
89 debug_assert!(index > 0, "Subcomponent index must be greater than 0");
90 match self {
91 ComponentBuilder::Subcomponents(subcomponents) => {
92 subcomponents.insert(index, value.to_string());
93 }
94 _ => {
95 let mut subcomponents = HashMap::new();
96 subcomponents.insert(index, value.to_string());
97 *self = ComponentBuilder::Subcomponents(subcomponents);
98 }
99 }
100 }
101
102 pub fn with_subcomponent<S: ToString>(mut self, index: usize, value: S) -> Self {
103 self.set_subcomponent(index, value);
104 self
105 }
106
107 pub fn set_subcomponent_timestamp<T: Into<TimeStamp>>(&mut self, index: usize, timestamp: T) {
108 self.set_subcomponent(index, timestamp.into().to_string());
109 }
110
111 pub fn with_subcomponent_timestamp<T: Into<TimeStamp>>(
112 mut self,
113 index: usize,
114 timestamp: T,
115 ) -> Self {
116 self.set_subcomponent_timestamp(index, timestamp);
117 self
118 }
119
120 pub fn subcomponent(&self, index: usize) -> Option<&String> {
121 debug_assert!(index > 0, "Subcomponent index must be greater than 0");
122 match self {
123 ComponentBuilder::Subcomponents(subcomponents) => subcomponents.get(&index),
124 _ => None,
125 }
126 }
127
128 pub fn subcomponent_mut(&mut self, index: usize) -> Option<&mut String> {
129 debug_assert!(index > 0, "Subcomponent index must be greater than 0");
130 match self {
131 ComponentBuilder::Subcomponents(subcomponents) => subcomponents.get_mut(&index),
132 _ => None,
133 }
134 }
135
136 pub fn remove_subcomponent(&mut self, index: usize) -> Option<String> {
137 debug_assert!(index > 0, "Subcomponent index must be greater than 0");
138 match self {
139 ComponentBuilder::Subcomponents(subcomponents) => subcomponents.remove(&index),
140 _ => None,
141 }
142 }
143
144 pub fn clear(&mut self) {
145 *self = ComponentBuilder::Value(String::new());
146 }
147
148 pub fn is_empty(&self) -> bool {
149 match self {
150 ComponentBuilder::Value(value) => value.is_empty(),
151 ComponentBuilder::Subcomponents(subcomponents) => subcomponents.is_empty(),
152 }
153 }
154
155 pub fn display<'a>(&'a self, separators: &'a Separators) -> ComponentBuilderDisplay<'a> {
156 ComponentBuilderDisplay {
157 component: self,
158 separators,
159 }
160 }
161}
162
163mod display {
164 use super::*;
165
166 pub struct ComponentBuilderDisplay<'a> {
167 pub(super) component: &'a ComponentBuilder,
168 pub(super) separators: &'a Separators,
169 }
170
171 impl Display for ComponentBuilderDisplay<'_> {
172 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
173 match self.component {
174 ComponentBuilder::Value(value) => self.separators.encode(value).fmt(f),
175 ComponentBuilder::Subcomponents(subcomponents) => {
176 if subcomponents.is_empty() {
177 return Ok(());
178 }
179 let max_index = subcomponents.keys().max().unwrap();
180 for i in 1..=*max_index {
181 if let Some(value) = subcomponents.get(&i) {
182 self.separators.encode(value).fmt(f)?;
183 }
184 if i < *max_index {
185 write!(f, "{}", self.separators.subcomponent)?;
186 }
187 }
188 Ok(())
189 }
190 }
191 }
192 }
193}
194
195impl<S: ToString> From<S> for ComponentBuilder {
196 fn from(value: S) -> Self {
197 ComponentBuilder::Value(value.to_string())
198 }
199}
200
201impl<'m> From<&'m Component<'m>> for ComponentBuilder {
202 fn from(component: &'m Component<'m>) -> Self {
203 if component.subcomponents.len() <= 1 {
204 ComponentBuilder::Value(component.source.to_string())
205 } else {
206 let subcomponents = component
207 .subcomponents
208 .iter()
209 .enumerate()
210 .map(|(i, subcomponent)| (i + 1, subcomponent.value.to_string()))
211 .collect();
212 ComponentBuilder::Subcomponents(subcomponents)
213 }
214 }
215}
216
217#[cfg(test)]
218mod tests {
219 use super::*;
220 use crate::message::Subcomponent;
221 use pretty_assertions_sorted::assert_eq;
222
223 #[test]
224 fn can_display_component_builder() {
225 let separators = Separators::default();
226 let component = ComponentBuilder::with_value("foo".to_string());
227 let display = component.display(&separators).to_string();
228 assert_eq!(display, "foo");
229
230 let mut subcomponents = HashMap::new();
231 subcomponents.insert(1, "bar".to_string());
232 subcomponents.insert(3, "baz".to_string());
233 let component = ComponentBuilder::with_subcomponents(subcomponents);
234 let display = component.display(&separators).to_string();
235 assert_eq!(display, "bar&&baz");
236 }
237
238 #[test]
239 fn can_convert_component_to_component_builder() {
240 let component = Component {
241 source: "foo&bar",
242 subcomponents: vec![
243 Subcomponent {
244 value: "foo",
245 range: 0..1, },
247 Subcomponent {
248 value: "bar",
249 range: 0..1, },
251 ],
252 range: 0..1, };
254
255 let component_builder: ComponentBuilder = (&component).into();
256 assert_eq!(
257 component_builder,
258 ComponentBuilder::with_subcomponents(
259 vec![(1, "foo".to_string()), (2, "bar".to_string())]
260 .into_iter()
261 .collect()
262 )
263 );
264 }
265
266 #[test]
267 fn can_convert_with_singular_value() {
268 let component = crate::parser::parse_component("foo").expect("Can parse component");
269 let component_builder = ComponentBuilder::from(&component);
270 assert_eq!(
271 component_builder,
272 ComponentBuilder::with_value("foo".to_string())
273 );
274 }
275}