ddex_builder/generator/
mod.rs1pub mod optimized_xml_writer;
75pub mod xml_writer;
76
77use crate::ast::{Element, AST}; use crate::builder::{BuildRequest, ReleaseRequest};
79use crate::error::BuildError;
80use indexmap::IndexMap;
81
82pub struct ASTGenerator {
84 version: String,
85}
86
87impl ASTGenerator {
88 pub fn new(version: String) -> Self {
90 Self { version }
91 }
92
93 pub fn generate(&mut self, request: &BuildRequest) -> Result<AST, BuildError> {
95 let mut root = Element::new("NewReleaseMessage");
97 root.namespace = Some("ern".to_string());
98
99 root.attributes.insert(
101 "MessageSchemaVersionId".to_string(),
102 format!("ern/{}", self.version),
103 );
104
105 root.add_child(self.generate_message_header(request)?);
107
108 root.add_child(self.generate_resource_list(&request.releases)?);
110
111 root.add_child(self.generate_release_list(&request.releases)?);
113
114 let mut namespaces = IndexMap::new();
116 namespaces.insert(
117 "ern".to_string(),
118 format!("http://ddex.net/xml/ern/{}", self.version.replace('.', "")),
119 );
120 namespaces.insert(
121 "xsi".to_string(),
122 "http://www.w3.org/2001/XMLSchema-instance".to_string(),
123 );
124
125 Ok(AST {
126 root,
127 namespaces,
128 schema_location: None,
129 })
130 }
131
132 fn generate_message_header(&self, request: &BuildRequest) -> Result<Element, BuildError> {
133 let mut header = Element::new("MessageHeader");
134
135 if let Some(ref msg_id) = request.header.message_id {
137 header.add_child(Element::new("MessageThreadId").with_text(msg_id));
138 header.add_child(Element::new("MessageId").with_text(msg_id));
139 }
140
141 let created_time = request
143 .header
144 .message_created_date_time
145 .as_ref()
146 .map(|t| t.clone())
147 .unwrap_or_else(|| chrono::Utc::now().to_rfc3339());
148
149 header.add_child(Element::new("MessageCreatedDateTime").with_text(created_time));
150
151 header.add_child(self.generate_party("MessageSender", &request.header.message_sender)?);
153
154 header
156 .add_child(self.generate_party("MessageRecipient", &request.header.message_recipient)?);
157
158 Ok(header)
159 }
160
161 fn generate_party(
162 &self,
163 element_name: &str,
164 party: &crate::builder::PartyRequest,
165 ) -> Result<Element, BuildError> {
166 let mut party_elem = Element::new(element_name);
167
168 if let Some(ref party_id) = party.party_id {
170 party_elem.add_child(Element::new("PartyId").with_text(party_id));
171 }
172
173 if let Some(ref party_ref) = party.party_reference {
175 party_elem.add_child(Element::new("PartyReference").with_text(party_ref));
176 }
177
178 for party_name in &party.party_name {
180 let mut name_elem = Element::new("PartyName");
181 if let Some(ref lang) = party_name.language_code {
182 name_elem
183 .attributes
184 .insert("LanguageCode".to_string(), lang.clone());
185 }
186 name_elem.add_text(&party_name.text);
187 party_elem.add_child(name_elem);
188 }
189
190 Ok(party_elem)
191 }
192
193 fn generate_resource_list(&self, releases: &[ReleaseRequest]) -> Result<Element, BuildError> {
194 let mut resource_list = Element::new("ResourceList");
195
196 for release in releases {
198 for track in &release.tracks {
199 let mut sound_recording = Element::new("SoundRecording");
200
201 let resource_ref = track
204 .resource_reference
205 .clone()
206 .unwrap_or_else(|| format!("A{}", track.track_id));
207 sound_recording
208 .add_child(Element::new("ResourceReference").with_text(&resource_ref));
209
210 let mut resource_id = Element::new("ResourceId");
212 resource_id.add_child(Element::new("ISRC").with_text(&track.isrc));
213 sound_recording.add_child(resource_id);
214
215 let mut ref_title = Element::new("ReferenceTitle");
217 ref_title.add_child(Element::new("TitleText").with_text(&track.title));
218 sound_recording.add_child(ref_title);
219
220 sound_recording.add_child(Element::new("Duration").with_text(&track.duration));
222
223 resource_list.add_child(sound_recording);
224 }
225 }
226
227 Ok(resource_list)
228 }
229
230 fn generate_release_list(&self, releases: &[ReleaseRequest]) -> Result<Element, BuildError> {
231 let mut release_list = Element::new("ReleaseList");
232
233 for release in releases {
234 let mut release_elem = Element::new("Release");
235
236 let release_ref = release
239 .release_reference
240 .clone()
241 .unwrap_or_else(|| format!("R{}", release.release_id));
242 release_elem.add_child(Element::new("ReleaseReference").with_text(&release_ref));
243
244 let mut release_id = Element::new("ReleaseId");
246 release_id.add_child(Element::new("GRid").with_text(&release.release_id));
247 release_elem.add_child(release_id);
248
249 if !release.title.is_empty() {
251 for title in &release.title {
252 let mut title_elem = Element::new("ReferenceTitle");
253 let mut title_text = Element::new("TitleText").with_text(&title.text);
254 if let Some(ref lang) = title.language_code {
255 title_text
256 .attributes
257 .insert("LanguageAndScriptCode".to_string(), lang.clone());
258 }
259 title_elem.add_child(title_text);
260 release_elem.add_child(title_elem);
261 }
262 }
263
264 let mut display_artist_name = Element::new("DisplayArtistName");
266 display_artist_name.add_child(Element::new("FullName").with_text(&release.artist));
267 release_elem.add_child(display_artist_name);
268
269 if let Some(ref label) = release.label {
271 let mut label_name = Element::new("LabelName");
272 label_name.add_child(Element::new("LabelName").with_text(label));
273 release_elem.add_child(label_name);
274 }
275
276 if let Some(ref upc) = release.upc {
278 let mut release_id_upc = Element::new("ReleaseId");
279 release_id_upc.add_child(Element::new("ICPN").with_text(upc));
280 release_elem.add_child(release_id_upc);
281 }
282
283 if let Some(ref release_date) = release.release_date {
285 release_elem.add_child(Element::new("ReleaseDate").with_text(release_date));
286 }
287
288 if let Some(ref resource_refs) = release.resource_references {
290 for resource_ref in resource_refs {
291 release_elem.add_child(
292 Element::new("ReleaseResourceReference").with_text(resource_ref),
293 );
294 }
295 } else {
296 for track in &release.tracks {
298 let resource_ref = track
300 .resource_reference
301 .clone()
302 .unwrap_or_else(|| format!("A{}", track.track_id));
303 release_elem.add_child(
304 Element::new("ReleaseResourceReference").with_text(&resource_ref),
305 );
306 }
307 }
308
309 release_list.add_child(release_elem);
310 }
311
312 Ok(release_list)
313 }
314
315 #[allow(dead_code)]
316 fn generate_deal_list(
317 &self,
318 deals: &[crate::builder::DealRequest],
319 ) -> Result<Element, BuildError> {
320 let mut deal_list = Element::new("DealList");
321
322 for deal in deals {
323 let mut deal_elem = Element::new("ReleaseDeal");
324
325 if let Some(ref deal_ref) = deal.deal_reference {
327 deal_elem.add_child(Element::new("DealReference").with_text(deal_ref));
328 }
329
330 let mut deal_terms = Element::new("Deal");
332 deal_terms.add_child(
333 Element::new("CommercialModelType")
334 .with_text(&deal.deal_terms.commercial_model_type),
335 );
336
337 for territory in &deal.deal_terms.territory_code {
339 deal_terms.add_child(Element::new("TerritoryCode").with_text(territory));
340 }
341
342 deal_elem.add_child(deal_terms);
343
344 for release_ref in &deal.release_references {
346 deal_elem.add_child(Element::new("DealReleaseReference").with_text(release_ref));
347 }
348
349 deal_list.add_child(deal_elem);
350 }
351
352 Ok(deal_list)
353 }
354}