1use crate::{Attribute, Span, XmlNode};
2use std::borrow::Cow;
3
4#[derive(Debug, Clone, PartialEq, Eq, Default)]
5pub struct Adf<'a> {
6 pub prospects: Vec<Prospect<'a>>,
7 pub extensions: Vec<XmlNode<'a>>,
8 pub span: Span,
9}
10
11#[derive(Debug, Clone, PartialEq, Eq, Default)]
12pub struct Prospect<'a> {
13 pub status: Option<Cow<'a, str>>,
14 pub ids: Vec<Id<'a>>,
15 pub request_date: Option<TextElement<'a>>,
16 pub vehicles: Vec<Vehicle<'a>>,
17 pub customer: Option<Customer<'a>>,
18 pub vendor: Option<Vendor<'a>>,
19 pub provider: Option<Provider<'a>>,
20 pub extensions: Vec<XmlNode<'a>>,
21 pub attributes: Vec<Attribute<'a>>,
22 pub span: Span,
23}
24
25#[derive(Debug, Clone, PartialEq, Eq, Default)]
26pub struct Vehicle<'a> {
27 pub interest: Option<Cow<'a, str>>,
28 pub status: Option<Cow<'a, str>>,
29 pub ids: Vec<Id<'a>>,
30 pub year: Option<TextElement<'a>>,
31 pub make: Option<TextElement<'a>>,
32 pub model: Option<TextElement<'a>>,
33 pub vin: Option<TextElement<'a>>,
34 pub stock: Option<TextElement<'a>>,
35 pub trim: Option<TextElement<'a>>,
36 pub doors: Option<TextElement<'a>>,
37 pub body_style: Option<TextElement<'a>>,
38 pub transmission: Option<TextElement<'a>>,
39 pub odometer: Option<TextElement<'a>>,
40 pub condition: Option<TextElement<'a>>,
41 pub color_combinations: Vec<ColorCombination<'a>>,
42 pub image_tags: Vec<TextElement<'a>>,
43 pub prices: Vec<Price<'a>>,
44 pub price_comments: Option<TextElement<'a>>,
45 pub options: Vec<VehicleOption<'a>>,
46 pub finance: Option<Finance<'a>>,
47 pub comments: Option<TextElement<'a>>,
48 pub extensions: Vec<XmlNode<'a>>,
49 pub attributes: Vec<Attribute<'a>>,
50 pub span: Span,
51}
52
53#[derive(Debug, Clone, PartialEq, Eq, Default)]
54pub struct ColorCombination<'a> {
55 pub interior_color: Option<TextElement<'a>>,
56 pub exterior_color: Option<TextElement<'a>>,
57 pub preference: Option<TextElement<'a>>,
58 pub extensions: Vec<XmlNode<'a>>,
59 pub attributes: Vec<Attribute<'a>>,
60 pub span: Span,
61}
62
63#[derive(Debug, Clone, PartialEq, Eq, Default)]
64pub struct VehicleOption<'a> {
65 pub option_name: Option<TextElement<'a>>,
66 pub manufacturer_code: Option<TextElement<'a>>,
67 pub stock: Option<TextElement<'a>>,
68 pub weighting: Option<TextElement<'a>>,
69 pub prices: Vec<Price<'a>>,
70 pub extensions: Vec<XmlNode<'a>>,
71 pub attributes: Vec<Attribute<'a>>,
72 pub span: Span,
73}
74
75#[derive(Debug, Clone, PartialEq, Eq, Default)]
76pub struct Finance<'a> {
77 pub method: Option<TextElement<'a>>,
78 pub amounts: Vec<TextElement<'a>>,
79 pub balances: Vec<TextElement<'a>>,
80 pub extensions: Vec<XmlNode<'a>>,
81 pub attributes: Vec<Attribute<'a>>,
82 pub span: Span,
83}
84
85#[derive(Debug, Clone, PartialEq, Eq, Default)]
86pub struct Customer<'a> {
87 pub ids: Vec<Id<'a>>,
88 pub contacts: Vec<Contact<'a>>,
89 pub timeframe: Option<Timeframe<'a>>,
90 pub comments: Option<TextElement<'a>>,
91 pub extensions: Vec<XmlNode<'a>>,
92 pub attributes: Vec<Attribute<'a>>,
93 pub span: Span,
94}
95
96#[derive(Debug, Clone, PartialEq, Eq, Default)]
97pub struct Timeframe<'a> {
98 pub description: Option<TextElement<'a>>,
99 pub earliest_date: Option<TextElement<'a>>,
100 pub latest_date: Option<TextElement<'a>>,
101 pub extensions: Vec<XmlNode<'a>>,
102 pub attributes: Vec<Attribute<'a>>,
103 pub span: Span,
104}
105
106#[derive(Debug, Clone, PartialEq, Eq, Default)]
107pub struct Vendor<'a> {
108 pub ids: Vec<Id<'a>>,
109 pub vendor_name: Option<TextElement<'a>>,
110 pub url: Option<TextElement<'a>>,
111 pub contacts: Vec<Contact<'a>>,
112 pub extensions: Vec<XmlNode<'a>>,
113 pub attributes: Vec<Attribute<'a>>,
114 pub span: Span,
115}
116
117#[derive(Debug, Clone, PartialEq, Eq, Default)]
118pub struct Provider<'a> {
119 pub ids: Vec<Id<'a>>,
120 pub name: Option<Name<'a>>,
121 pub service: Option<TextElement<'a>>,
122 pub url: Option<TextElement<'a>>,
123 pub email: Option<TextElement<'a>>,
124 pub phone: Option<TextElement<'a>>,
125 pub contacts: Vec<Contact<'a>>,
126 pub extensions: Vec<XmlNode<'a>>,
127 pub attributes: Vec<Attribute<'a>>,
128 pub span: Span,
129}
130
131#[derive(Debug, Clone, PartialEq, Eq, Default)]
132pub struct Contact<'a> {
133 pub primary_contact: Option<Cow<'a, str>>,
134 pub names: Vec<Name<'a>>,
135 pub emails: Vec<TextElement<'a>>,
136 pub phones: Vec<TextElement<'a>>,
137 pub addresses: Vec<Address<'a>>,
138 pub extensions: Vec<XmlNode<'a>>,
139 pub attributes: Vec<Attribute<'a>>,
140 pub span: Span,
141}
142
143#[derive(Debug, Clone, PartialEq, Eq, Default)]
144pub struct Address<'a> {
145 pub address_type: Option<Cow<'a, str>>,
146 pub streets: Vec<TextElement<'a>>,
147 pub apartment: Option<TextElement<'a>>,
148 pub city: Option<TextElement<'a>>,
149 pub region_code: Option<TextElement<'a>>,
150 pub postal_code: Option<TextElement<'a>>,
151 pub country: Option<TextElement<'a>>,
152 pub extensions: Vec<XmlNode<'a>>,
153 pub attributes: Vec<Attribute<'a>>,
154 pub span: Span,
155}
156
157#[derive(Debug, Clone, PartialEq, Eq, Default)]
158pub struct Id<'a> {
159 pub sequence: Option<Cow<'a, str>>,
160 pub source: Option<Cow<'a, str>>,
161 pub parts: Vec<TextPart<'a>>,
162 pub attributes: Vec<Attribute<'a>>,
163 pub span: Span,
164}
165
166#[derive(Debug, Clone, PartialEq, Eq, Default)]
167pub struct Price<'a> {
168 pub price_type: Option<Cow<'a, str>>,
169 pub currency: Option<Cow<'a, str>>,
170 pub delta: Option<Cow<'a, str>>,
171 pub relative_to: Option<Cow<'a, str>>,
172 pub source: Option<Cow<'a, str>>,
173 pub parts: Vec<TextPart<'a>>,
174 pub attributes: Vec<Attribute<'a>>,
175 pub span: Span,
176}
177
178#[derive(Debug, Clone, PartialEq, Eq, Default)]
179pub struct Name<'a> {
180 pub part: Option<Cow<'a, str>>,
181 pub name_type: Option<Cow<'a, str>>,
182 pub parts: Vec<TextPart<'a>>,
183 pub attributes: Vec<Attribute<'a>>,
184 pub span: Span,
185}
186
187#[derive(Debug, Clone, PartialEq, Eq, Default)]
188pub struct TextElement<'a> {
189 pub parts: Vec<TextPart<'a>>,
190 pub attributes: Vec<Attribute<'a>>,
191 pub span: Span,
192}
193
194impl<'a> TextElement<'a> {
195 pub fn new(value: Cow<'a, str>, attributes: Vec<Attribute<'a>>) -> Self {
196 Self {
197 parts: vec![TextPart::Text(value)],
198 attributes,
199 span: Span::default(),
200 }
201 }
202
203 pub fn from_parts(parts: Vec<TextPart<'a>>, attributes: Vec<Attribute<'a>>) -> Self {
204 Self {
205 parts,
206 attributes,
207 span: Span::default(),
208 }
209 }
210}
211
212macro_rules! impl_text_value {
213 ($t:ident) => {
214 impl<'a> $t<'a> {
215 pub fn value(&self) -> Cow<'a, str> {
216 text_parts_value(&self.parts)
217 }
218
219 pub fn set_value(&mut self, value: impl Into<Cow<'a, str>>) {
220 self.parts = vec![TextPart::Text(value.into())];
221 }
222 }
223 };
224}
225
226impl_text_value!(Id);
227impl_text_value!(Price);
228impl_text_value!(Name);
229impl_text_value!(TextElement);
230
231#[derive(Debug, Clone, PartialEq, Eq)]
232pub enum TextPart<'a> {
233 Text(Cow<'a, str>),
234 CData(Cow<'a, str>),
235 EntityRef(Cow<'a, str>),
236}
237
238fn text_parts_value<'a>(parts: &[TextPart<'a>]) -> Cow<'a, str> {
239 match parts {
240 [] => Cow::Borrowed(""),
241 [TextPart::Text(text) | TextPart::CData(text)] => text.clone(),
242 [TextPart::EntityRef(name)] => match resolve_standard_entity(name) {
243 Some(resolved) => Cow::Borrowed(resolved),
244 None => Cow::Owned(format!("&{name};")),
245 },
246 _ => {
247 let mut joined = String::new();
248 for part in parts {
249 match part {
250 TextPart::Text(text) | TextPart::CData(text) => joined.push_str(text),
251 TextPart::EntityRef(name) => match resolve_standard_entity(name) {
252 Some(resolved) => joined.push_str(resolved),
253 None => {
254 joined.push('&');
255 joined.push_str(name);
256 joined.push(';');
257 }
258 },
259 }
260 }
261 Cow::Owned(joined)
262 }
263 }
264}
265
266pub(crate) fn resolve_standard_entity(name: &str) -> Option<&'static str> {
267 match name {
268 "amp" => Some("&"),
269 "lt" => Some("<"),
270 "gt" => Some(">"),
271 "quot" => Some("\""),
272 "apos" => Some("'"),
273 _ => None,
274 }
275}