Skip to main content

eml_nl/io/
writer.rs

1use std::{borrow::Cow, collections::BTreeMap};
2
3use quick_xml::{
4    Writer,
5    events::{BytesDecl, BytesStart, BytesText, Event, attributes::Attribute},
6};
7
8use crate::{
9    EMLError, EMLErrorKind, EMLResultExt, NS_EML, NS_KR, NS_XAL, NS_XNL, io::QualifiedName,
10};
11
12#[derive(Debug, Clone)]
13pub(crate) struct NsDefinitions {
14    default_namespace_uri: Option<&'static str>,
15    namespace_definitions: BTreeMap<&'static str, &'static str>,
16}
17
18pub(crate) struct EMLWriter {
19    ns_definitions: NsDefinitions,
20    writer: Writer<Vec<u8>>,
21}
22
23impl EMLWriter {
24    /// Resolves the namespace URI to a prefix defined previously.
25    ///
26    /// Note that there is a subtle difference between attributes and elements:
27    /// If elements have no explicit namespace, then they are in the default
28    /// namespace (as specified by the xmlns="" attribute). For attributes, if
29    /// they have no explicit namespace, they are in no namespace at all.
30    ///
31    /// This writer requires each resolved element/attribute to specify its
32    /// namespace URI explicitly. So if you need an element without any prefix
33    /// but have defined a default namespace, you must specify that namespace to
34    /// get no prefix.
35    fn resolve_namespace_prefix(
36        &self,
37        namespace: &str,
38        is_attribute: bool,
39    ) -> Result<Option<&str>, EMLError> {
40        if self.is_default_namespace(Some(namespace)) {
41            if is_attribute {
42                // Attributes cannot be in the default namespace unless there is
43                // an explicit prefix for that URI as well, but this writer does
44                // not support that.
45                return Err(EMLErrorKind::AttributeNamespaceError).without_span();
46            } else {
47                return Ok(None);
48            }
49        }
50
51        for (prefix, uri) in &self.ns_definitions.namespace_definitions {
52            if *uri == namespace {
53                return Ok(Some(*prefix));
54            }
55        }
56        Err(EMLErrorKind::UnknownNamespace(namespace.to_string())).without_span()
57    }
58
59    /// Given an (optional) namespace URI and a local name, returns the
60    /// qualified name that should be used when writing the element or attribute.
61    ///
62    /// This function resolves the namespace URI to a prefix using the previously
63    /// defined namespaces initialized when initializing the EMLWriter.
64    ///
65    /// Note the difference in behavior for attributes and elements as described
66    /// in `resolve_namespace_prefix`.
67    fn format_qname<'b, 'c>(
68        &self,
69        name: &'b QualifiedName<'b, 'c>,
70        is_attribute: bool,
71    ) -> Result<Cow<'b, str>, EMLError> {
72        let namespace_name = name
73            .namespace
74            .as_ref()
75            .map(|n| self.resolve_namespace_prefix(n.as_ref(), is_attribute))
76            .transpose()?
77            .flatten();
78
79        match namespace_name {
80            Some(ns_name) => Ok(Cow::Owned(format!(
81                "{}:{}",
82                ns_name,
83                name.local_name.as_ref()
84            ))),
85            None => Ok(Cow::Borrowed(name.local_name.as_ref())),
86        }
87    }
88
89    /// Checks if the given namespace URI is configured as the default namespace.
90    fn is_default_namespace(&self, namespace: Option<&str>) -> bool {
91        match (namespace, self.ns_definitions.default_namespace_uri) {
92            (Some(ns), Some(def_ns)) => ns == def_ns,
93            (None, None) => true,
94            _ => false,
95        }
96    }
97
98    /// Checks if there is a default namespace defined.
99    fn has_default_namespace(&self) -> bool {
100        self.ns_definitions.default_namespace_uri.is_some()
101    }
102}
103
104pub(crate) struct EMLElementWriter<'a> {
105    start_tag: BytesStart<'a>,
106    writer: &'a mut EMLWriter,
107}
108
109impl<'a> EMLElementWriter<'a> {
110    pub(crate) fn new(
111        writer: &'a mut EMLWriter,
112        name: &'a QualifiedName<'a, 'a>,
113    ) -> Result<Self, EMLError> {
114        let elem_name = writer.format_qname(name, false)?;
115        if name.namespace.is_none() && writer.has_default_namespace() {
116            // Technically this is something that XML allows, but as it is not
117            // needed for EML we do not support it here.
118            return Err(EMLErrorKind::ElementNamespaceError).without_span();
119        }
120
121        let start_tag = BytesStart::new(elem_name);
122        Ok(EMLElementWriter { start_tag, writer })
123    }
124
125    pub fn attr<'b, 'c>(
126        mut self,
127        name: impl Into<QualifiedName<'b, 'c>>,
128        value: &str,
129    ) -> Result<Self, EMLError> {
130        let name = name.into();
131        let attr_name = self.writer.format_qname(&name, true)?;
132        self = self.attr_raw((attr_name.as_ref(), value));
133        Ok(self)
134    }
135
136    pub fn attr_opt<'b, 'c>(
137        self,
138        name: impl Into<QualifiedName<'b, 'c>>,
139        value: Option<impl AsRef<str>>,
140    ) -> Result<Self, EMLError> {
141        if let Some(v) = value {
142            self.attr(name, v.as_ref())
143        } else {
144            Ok(self)
145        }
146    }
147
148    fn attr_raw<'b>(mut self, attr: impl Into<Attribute<'b>>) -> Self {
149        self.start_tag.push_attribute(attr);
150        self
151    }
152
153    pub fn content(self) -> Result<EMLElementContentWriter<'a>, EMLError> {
154        self.writer
155            .writer
156            .write_event(Event::Start(self.start_tag.borrow()))
157            .without_span()?;
158        Ok(EMLElementContentWriter {
159            start_tag: self.start_tag,
160            writer: self.writer,
161        })
162    }
163
164    pub fn child_option<'b, 'c, T>(
165        self,
166        name: impl Into<QualifiedName<'b, 'c>>,
167        value: Option<T>,
168        child_writer: impl FnOnce(EMLElementWriter, T) -> Result<(), EMLError>,
169    ) -> Result<EMLElementContentWriter<'a>, EMLError> {
170        self.content()?.child_option(name, value, child_writer)
171    }
172
173    pub fn child<'b, 'c>(
174        self,
175        name: impl Into<QualifiedName<'b, 'c>>,
176        child_writer: impl FnOnce(EMLElementWriter) -> Result<(), EMLError>,
177    ) -> Result<EMLElementContentWriter<'a>, EMLError> {
178        self.content()?.child(name, child_writer)
179    }
180
181    pub fn child_elem<'b, 'c>(
182        self,
183        name: impl Into<QualifiedName<'b, 'c>>,
184        value: &impl EMLWriteElement,
185    ) -> Result<EMLElementContentWriter<'a>, EMLError> {
186        self.content()?.child_elem(name, value)
187    }
188
189    pub fn child_elem_option<'b, 'c>(
190        self,
191        name: impl Into<QualifiedName<'b, 'c>>,
192        value: Option<&impl EMLWriteElement>,
193    ) -> Result<EMLElementContentWriter<'a>, EMLError> {
194        self.content()?.child_elem_option(name, value)
195    }
196
197    pub fn child_elems<'b, 'c>(
198        self,
199        name: impl Into<QualifiedName<'b, 'c>>,
200        children: &[impl EMLWriteElement],
201    ) -> Result<EMLElementContentWriter<'a>, EMLError> {
202        self.content()?.child_elems(name, children)
203    }
204
205    pub fn text(self, text: &str) -> Result<EMLElementContentWriter<'a>, EMLError> {
206        self.content()?.text(text)
207    }
208
209    pub fn empty(self) -> Result<(), EMLError> {
210        self.writer
211            .writer
212            .write_event(Event::Empty(self.start_tag.borrow()))
213            .without_span()?;
214        Ok(())
215    }
216}
217
218pub(crate) struct EMLElementContentWriter<'a> {
219    start_tag: BytesStart<'a>,
220    writer: &'a mut EMLWriter,
221}
222
223impl<'a> EMLElementContentWriter<'a> {
224    pub fn child<'b, 'c>(
225        self,
226        name: impl Into<QualifiedName<'b, 'c>>,
227        child_writer: impl FnOnce(EMLElementWriter) -> Result<(), EMLError>,
228    ) -> Result<Self, EMLError> {
229        let name = name.into();
230        let elem_writer = EMLElementWriter::new(self.writer, &name)?;
231        child_writer(elem_writer)?;
232        Ok(self)
233    }
234
235    pub fn child_option<'b, 'c, T>(
236        self,
237        name: impl Into<QualifiedName<'b, 'c>>,
238        value: Option<T>,
239        child_writer: impl FnOnce(EMLElementWriter, T) -> Result<(), EMLError>,
240    ) -> Result<EMLElementContentWriter<'a>, EMLError> {
241        if let Some(v) = value {
242            self.child(name, |w| child_writer(w, v))
243        } else {
244            Ok(self)
245        }
246    }
247
248    pub fn child_elem<'b, 'c>(
249        self,
250        name: impl Into<QualifiedName<'b, 'c>>,
251        value: &impl EMLWriteElement,
252    ) -> Result<Self, EMLError> {
253        self.child(name, write_eml_element(value))
254    }
255
256    pub fn child_elem_option<'b, 'c>(
257        self,
258        name: impl Into<QualifiedName<'b, 'c>>,
259        value: Option<&impl EMLWriteElement>,
260    ) -> Result<EMLElementContentWriter<'a>, EMLError> {
261        self.child_option(name, value, |writer, value| {
262            write_eml_element(value)(writer)
263        })
264    }
265
266    pub fn child_elems<'b, 'c>(
267        mut self,
268        name: impl Into<QualifiedName<'b, 'c>>,
269        children: &[impl EMLWriteElement],
270    ) -> Result<EMLElementContentWriter<'a>, EMLError> {
271        let name = name.into();
272        for child in children {
273            self = self.child_elem(name.clone(), child)?;
274        }
275        Ok(self)
276    }
277
278    pub fn child_elems_map<'b, 'c, T>(
279        mut self,
280        name: impl Into<QualifiedName<'b, 'c>>,
281        children: impl IntoIterator<Item = T>,
282        child_map: impl Fn(EMLElementWriter, T) -> Result<(), EMLError>,
283    ) -> Result<EMLElementContentWriter<'a>, EMLError> {
284        let name = name.into();
285        for child in children {
286            self = self.child(name.clone(), |writer| child_map(writer, child))?;
287        }
288        Ok(self)
289    }
290
291    pub fn text(self, text: &str) -> Result<Self, EMLError> {
292        self.writer
293            .writer
294            .write_event(Event::Text(BytesText::new(text)))
295            .without_span()?;
296        Ok(self)
297    }
298
299    pub fn finish(self) -> Result<(), EMLError> {
300        self.writer
301            .writer
302            .write_event(quick_xml::events::Event::End(self.start_tag.to_end()))
303            .without_span()?;
304        Ok(())
305    }
306}
307
308pub(crate) trait EMLWriteInternal {
309    fn write_root<'a, 'b>(
310        &self,
311        root_name: Option<impl Into<QualifiedName<'a, 'b>>>,
312        default_namespace_uri: Option<Option<&'static str>>,
313        namespace_definitions: Option<BTreeMap<&'static str, &'static str>>,
314        pretty_print: bool,
315        include_declaration: bool,
316    ) -> Result<Vec<u8>, EMLError>;
317
318    fn write_root_str<'a, 'b>(
319        &self,
320        root_name: Option<impl Into<QualifiedName<'a, 'b>>>,
321        default_namespace_uri: Option<Option<&'static str>>,
322        namespace_definitions: Option<BTreeMap<&'static str, &'static str>>,
323        pretty_print: bool,
324        include_declaration: bool,
325    ) -> Result<String, EMLError>;
326}
327
328impl<T> EMLWriteInternal for T
329where
330    T: EMLWriteElement,
331{
332    fn write_root<'a, 'b>(
333        &self,
334        root_name: Option<impl Into<QualifiedName<'a, 'b>>>,
335        default_namespace_uri: Option<Option<&'static str>>,
336        namespace_definitions: Option<BTreeMap<&'static str, &'static str>>,
337        pretty_print: bool,
338        include_declaration: bool,
339    ) -> Result<Vec<u8>, EMLError> {
340        // default values are for EML root element
341        let root = root_name
342            .map(|v| v.into())
343            .unwrap_or_else(|| QualifiedName::new("EML", Some(NS_EML)));
344        let default_namespace_uri = default_namespace_uri.unwrap_or(Some(NS_EML));
345        let namespace_definitions = namespace_definitions.unwrap_or_else(|| {
346            let mut ns_defs = BTreeMap::new();
347            ns_defs.insert("kr", NS_KR);
348            ns_defs.insert("xal", NS_XAL);
349            ns_defs.insert("xnl", NS_XNL);
350            // ns_defs.insert("ds", NS_DS);
351            // ns_defs.insert("xmlns", NS_XMLNS);
352            // ns_defs.insert("xml", NS_XML);
353            ns_defs
354        });
355
356        let ns_definitions = NsDefinitions {
357            default_namespace_uri,
358            namespace_definitions,
359        };
360
361        let mut writer = if pretty_print {
362            Writer::new_with_indent(Vec::new(), b' ', 4)
363        } else {
364            Writer::new(Vec::new())
365        };
366
367        if include_declaration {
368            writer
369                .write_event(Event::Decl(BytesDecl::new("1.0", Some("UTF-8"), None)))
370                .without_span()?;
371        }
372        let mut eml_writer = EMLWriter {
373            ns_definitions: ns_definitions.clone(),
374            writer,
375        };
376        let mut element = EMLElementWriter::new(&mut eml_writer, &root)?;
377        if let Some(ns_uri) = ns_definitions.default_namespace_uri {
378            element = element.attr_raw(("xmlns", ns_uri));
379        }
380        for (prefix, uri) in &ns_definitions.namespace_definitions {
381            element = element.attr_raw((format!("xmlns:{}", *prefix).as_str(), *uri));
382        }
383        self.write_eml_element(element)?;
384
385        // Add a final newline for properly formatted output if pretty_print is enabled
386        if pretty_print {
387            eml_writer
388                .writer
389                .write_event(Event::Text(BytesText::new("\n")))
390                .without_span()?;
391        }
392        Ok(eml_writer.writer.into_inner())
393    }
394
395    fn write_root_str<'a, 'b>(
396        &self,
397        root_name: Option<impl Into<QualifiedName<'a, 'b>>>,
398        default_namespace_uri: Option<Option<&'static str>>,
399        namespace_definitions: Option<BTreeMap<&'static str, &'static str>>,
400        pretty_print: bool,
401        include_declaration: bool,
402    ) -> Result<String, EMLError> {
403        String::from_utf8(self.write_root(
404            root_name,
405            default_namespace_uri,
406            namespace_definitions,
407            pretty_print,
408            include_declaration,
409        )?)
410        .without_span()
411    }
412}
413
414/// Writing EML documents to a [`String`] or [`Vec<u8>`].
415///
416/// The errors generated during writing do not contain location information, as
417/// there is no document to refer to yet. Most of the time errors generated
418/// during writing are underlying errors or logic errors in your implementation,
419/// so location information would be of limited use anyway.
420pub trait EMLWrite {
421    /// Writes an EML document with an EML root element to a byte vector.
422    fn write_eml_root(
423        &self,
424        pretty_print: bool,
425        include_declaration: bool,
426    ) -> Result<Vec<u8>, EMLError>;
427
428    /// Writes an EML document with an EML root element to a string.
429    fn write_eml_root_str(
430        &self,
431        pretty_print: bool,
432        include_declaration: bool,
433    ) -> Result<String, EMLError>;
434}
435
436impl<T> EMLWrite for T
437where
438    T: EMLWriteInternal,
439{
440    fn write_eml_root(
441        &self,
442        pretty_print: bool,
443        include_declaration: bool,
444    ) -> Result<Vec<u8>, EMLError> {
445        self.write_root(
446            None::<QualifiedName<'_, '_>>,
447            None,
448            None,
449            pretty_print,
450            include_declaration,
451        )
452    }
453
454    /// Writes an EML document with an EML root element to a string.
455    fn write_eml_root_str(
456        &self,
457        pretty_print: bool,
458        include_declaration: bool,
459    ) -> Result<String, EMLError> {
460        self.write_root_str(
461            None::<QualifiedName<'_, '_>>,
462            None,
463            None,
464            pretty_print,
465            include_declaration,
466        )
467    }
468}
469
470pub(crate) trait EMLWriteElement {
471    fn write_eml_element(&self, writer: EMLElementWriter) -> Result<(), EMLError>;
472}
473
474pub(crate) fn write_eml_element(
475    element: &impl EMLWriteElement,
476) -> impl FnOnce(EMLElementWriter) -> Result<(), EMLError> {
477    |writer| element.write_eml_element(writer)
478}
479
480#[cfg(test)]
481pub(crate) fn test_write_eml_element<T: crate::io::EMLElement>(
482    element: &T,
483    namespaces: &[&str],
484) -> Result<String, EMLError> {
485    let mut namespace_definitions = BTreeMap::new();
486    let mut default_namespace_uri = Some(None);
487    for ns in namespaces {
488        use crate::NS_DS;
489
490        match *ns {
491            NS_EML => {
492                default_namespace_uri = Some(Some(NS_EML));
493            }
494            NS_KR => {
495                namespace_definitions.insert("kr", NS_KR);
496            }
497            NS_XAL => {
498                namespace_definitions.insert("xal", NS_XAL);
499            }
500            NS_XNL => {
501                namespace_definitions.insert("xnl", NS_XNL);
502            }
503            NS_DS => {
504                namespace_definitions.insert("ds", NS_DS);
505            }
506            _ => {}
507        }
508    }
509
510    element.write_root_str(
511        Some(T::EML_NAME),
512        default_namespace_uri,
513        Some(namespace_definitions),
514        true,
515        false,
516    )
517}