Skip to main content

adf/
model.rs

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}