aboutsummaryrefslogblamecommitdiffstats
path: root/src/writer.rs
blob: 1818fca1ef6805f9824086d518ff3e8d2c7bce16 (plain) (tree)
1
2
3
4
5
6
7
8
9
                              
 
                                     
                                           
                  

                              

            
                                            
                                                                                                  
                 
                                                               
                             
  
                     
                           
 



                                                                             
                
                      

                      
                     
                                                               

 
                             
                                                                            
                                   








                                                          
              
                                                       





                                                               
                                                                                












                                                          
                              
                                                               
         
     
 
                                                 
                                  
                                            
     
 
 

                              
                                                                            

















                                                               
                                                        



                                          
                                                                                

















                                                               
                                                                 






















                                                                                                    
                                                                                                                    








                                                                                       
                                                                                                








                                                                                        
                                                                                                                                                                                         









                                                                                              
                                                                                                                                                        








                                                                                  
                                                                                                                          







                                                       
                                                                                               











                                                               

                                                                                             
















































































































                                                                                                  
                                    



                           
                                         















































































































                                                                                                  
                                                                                               


                                                                                      
                                                 












                                                 

                                                                            














































                                                                                                 
                                                        
                                                                 
                                                                                   




                                        












                                                                                                    
                                         


              
                                                                                                                    

                                                                                       
                                            




                                                                                     
              

     
                                                                                                

                                                                                        
                                                  




                                                                                     
              

     
                                                                                                                                                                                         



                                                                                              




                                                                                     


              
                                                                                                                                                        

                                                                                  
                                            




                                                                                     


              
                                                                                                                          

                                                     




                                                                                     
              

     
                      
                           



                                                                            
                                                     


                                                   
                                        



              
                                                                            
                                                                          




                                        
                                                           


                                   
                                                            
                       
 


                                                                                         

                                                          
                                                                                  

                                                                                             
















                                                                                          



                         











                                                                                  

                                        
                                                                    



                                               

























                                                                                                 
                                                                              

                    

                                                                                                  












                                                            




                                  



              
                                                                                      
                                                                                  




                                        
                                                           


                                   
                                                            

                       


                                                                                         

                                                          
                                                                                  

                                                                                             
















                                                                                          

















                                                                                  
                                                                    






                                                                            






                                                                                        















                                                                                                 
                                                                              

                    

                                                                                                  










                                                            

                                                   
                                   
 
                                              
                                   
                                                  


              
                             


                                                                            
                                    





                                                                    
             



                                     
              
     
 
                                                                                                       
                                                         




                                        
                                               
                      

                                                                       











                                                                                                 













                                                                                     
                                       
                                              



                                      



                                                            
     

 
















                                                           
use std::collections::HashSet;

use async_recursion::async_recursion;
use tokio::io::{AsyncWrite, AsyncWriteExt};
use tracing::info;
#[cfg(target_arch = "wasm32")]
use web_sys::WebSocket;

use crate::{
    declaration::{Declaration, VersionInfo},
    element::{escape_str, Content, Element, IntoContent, IntoElement, Name, NamespaceDeclaration},
    error::Error,
    xml::{self, composers::Composer, parsers_complete::Parser},
    Result, XMLNS_NS, XML_NS,
};
use endable::Endable;
pub use loggable::Loggable;

mod endable;
mod loggable;

/// Writer that tracks depth and corresponding declared/available namespaces.
#[derive(Debug)]
pub struct Writer<W> {
    inner: Endable<W>,
    unendable: bool,
    depth: Vec<Name>,
    namespace_declarations: Vec<HashSet<NamespaceDeclaration>>,
}

impl<W> Writer<Loggable<W>> {
    /// Create a new `Writer` which is constrained to a single root element.
    pub fn new(writer: W) -> Self {
        let mut default_declarations = HashSet::new();
        default_declarations.insert(NamespaceDeclaration {
            prefix: Some("xml".to_string()),
            namespace: XML_NS.to_string(),
        });
        default_declarations.insert(NamespaceDeclaration {
            prefix: Some("xmlns".to_string()),
            namespace: XMLNS_NS.to_string(),
        });
        Self {
            inner: Endable::new(Loggable::new(writer)),
            unendable: false,
            depth: Vec::new(),
            namespace_declarations: vec![default_declarations],
        }
    }

    /// Create a new `Writer` which is not constrained to a single root element.
    pub fn new_unendable(writer: W) -> Self {
        let mut default_declarations = HashSet::new();
        default_declarations.insert(NamespaceDeclaration {
            prefix: Some("xml".to_string()),
            namespace: XML_NS.to_string(),
        });
        default_declarations.insert(NamespaceDeclaration {
            prefix: Some("xmlns".to_string()),
            namespace: XMLNS_NS.to_string(),
        });
        Self {
            inner: Endable::new(Loggable::new(writer)),
            unendable: true,
            depth: Vec::new(),
            namespace_declarations: vec![default_declarations],
        }
    }

    /// Extract the inner type from the `Writer`.
    pub fn into_inner(self) -> W {
        self.inner.into_inner().into_inner()
    }
}

#[cfg(target_arch = "wasm32")]
impl Writer<WebSocket> {
    /// Create a new `Writer` which is constrained to a single root element.
    pub fn new(websocket: WebSocket) -> Self {
        let mut default_declarations = HashSet::new();
        default_declarations.insert(NamespaceDeclaration {
            prefix: Some("xml".to_string()),
            namespace: XML_NS.to_string(),
        });
        default_declarations.insert(NamespaceDeclaration {
            prefix: Some("xmlns".to_string()),
            namespace: XMLNS_NS.to_string(),
        });
        Self {
            inner: Endable::new(websocket),
            unendable: false,
            depth: Vec::new(),
            namespace_declarations: vec![default_declarations],
        }
    }

    /// Extract the inner `WebSocket` from the `Writer`.
    pub fn into_inner(self) -> WebSocket {
        self.inner.into_inner()
    }

    /// Create a new `Writer` which is not constrained to a single root element.
    pub fn new_unendable(websocket: WebSocket) -> Self {
        let mut default_declarations = HashSet::new();
        default_declarations.insert(NamespaceDeclaration {
            prefix: Some("xml".to_string()),
            namespace: XML_NS.to_string(),
        });
        default_declarations.insert(NamespaceDeclaration {
            prefix: Some("xmlns".to_string()),
            namespace: XMLNS_NS.to_string(),
        });
        Self {
            inner: Endable::new(websocket),
            unendable: true,
            depth: Vec::new(),
            namespace_declarations: vec![default_declarations],
        }
    }

    /// Write an XML declaration with the provided `VersionInfo`.
    pub async fn write_declaration(&mut self, version: VersionInfo) -> Result<()> {
        let declaration = Declaration::version(version);
        let version_info;
        match declaration.version_info {
            VersionInfo::One => version_info = xml::VersionInfo::SingleQuoted(xml::VersionNum::One),
            VersionInfo::OneDotOne => {
                version_info = xml::VersionInfo::SingleQuoted(xml::VersionNum::OneDotOne)
            }
        }
        let declaration = xml::XMLDecl {
            version_info,
            encoding_decl: None,
            sd_decl: None,
        };
        let declaration = declaration.to_string();
        if self.unendable {
            self.inner.ignore_end().send_with_str(&declaration)?;
        } else {
            self.inner.try_as_mut()?.send_with_str(&declaration)?;
        }
        Ok(())
    }

    /// Write a full element corresponding with the item implementing `IntoElement` (start tag + content + end tag).
    pub async fn write_full(&mut self, into_element: &impl IntoElement) -> Result<()> {
        let element = into_element.into_element();
        let mut frame = String::new();
        self.write_element_to_frame(&element, &mut frame)?;
        self.inner.ignore_end().send_with_str(&frame)?;
        info!("wrote element: {}", frame);
        Ok(())
    }

    /// Write the start tag of an item that implements `IntoElement`. Navigates up the document.
    pub async fn write_start(&mut self, into_element: &impl IntoElement) -> Result<()> {
        let element = into_element.into_element();
        let mut frame = String::new();
        self.write_element_start_to_frame(&element, &mut frame)?;
        self.inner.ignore_end().send_with_str(&frame)?;
        info!("wrote element: {}", frame);
        Ok(())
    }

    /// Write all the inner content (everything within the start and end tag of an xml element) of an item that implements `IntoElement`. In the case of an empty element, write nothing.
    pub async fn write_all_content(&mut self, into_element: &impl IntoElement) -> Result<()> {
        let mut frame = String::new();
        for content in &into_element.get_content() {
            self.write_content_to_frame(content, &mut frame)?;
        }
        self.inner.ignore_end().send_with_str(&frame)?;
        info!("wrote element: {}", frame);
        Ok(())
    }

    /// Write an item that implements `IntoContent`. Could be an element, some text, a comment, etc. Anything that could be included in an element body.
    pub async fn write(&mut self, into_content: &impl IntoContent) -> Result<()> {
        let content = into_content.into_content();
        let mut frame = String::new();
        self.write_content_to_frame(&content, &mut frame)?;
        self.inner.ignore_end().send_with_str(&frame)?;
        info!("wrote element: {}", frame);
        Ok(())
    }

    /// Navigate down the document structure and write the end tag for the current element opened in the document context.
    pub async fn write_end(&mut self) -> Result<()> {
        let mut frame = String::new();
        self.write_end_tag_to_frame(&mut frame)?;
        self.inner.ignore_end().send_with_str(&frame)?;
        info!("wrote element: {}", frame);
        Ok(())
    }

    fn write_element_to_frame(&mut self, element: &Element, frame: &mut String) -> Result<()> {
        if element.content.is_empty() {
            self.write_empty_to_frame(element, frame)?;
        } else {
            self.write_element_start_to_frame(element, frame)?;
            for content in &element.content {
                self.write_content_to_frame(content, frame)?;
            }
            self.write_end_tag_to_frame(frame)?;
        }
        Ok(())
    }

    fn write_empty_to_frame(&mut self, element: &Element, frame: &mut String) -> Result<()> {
        let _writer = if self.unendable {
            self.inner.ignore_end()
        } else {
            self.inner.try_as_mut()?
        };

        let mut namespace_declarations_stack: Vec<_> = self
            .namespace_declarations
            .iter()
            .flatten()
            .chain(&element.namespace_declaration_overrides)
            .collect();

        let mut namespace_declarations = element.namespace_declaration_overrides.clone();

        let default_namespace_declaration;
        let prefix;
        if let Some(namespace) = &element.name.namespace {
            if let Some(name_namespace_declaration) = namespace_declarations_stack
                .iter()
                .rfind(|namespace_declaration| namespace_declaration.namespace == *namespace)
            {
                prefix = name_namespace_declaration.prefix.as_ref();
            } else {
                default_namespace_declaration = NamespaceDeclaration {
                    prefix: None,
                    namespace: namespace.clone(),
                };
                if namespace_declarations.insert(default_namespace_declaration.clone()) {
                    namespace_declarations_stack.push(&default_namespace_declaration);
                    prefix = None
                } else {
                    return Err(Error::DuplicateNameSpaceDeclaration(NamespaceDeclaration {
                        prefix: None,
                        namespace: namespace.clone(),
                    }));
                }
            }
        } else {
            prefix = None
        }

        let name;
        if let Some(prefix) = &prefix {
            name = xml::QName::PrefixedName(xml::PrefixedName {
                prefix: xml::Prefix::parse_full(prefix)?,
                local_part: xml::LocalPart::parse_full(&element.name.local_name)?,
            })
        } else {
            name = xml::QName::UnprefixedName(xml::UnprefixedName::parse_full(
                &element.name.local_name,
            )?)
        }

        let mut attributes = Vec::new();

        for namespace_declaration in namespace_declarations.iter() {
            let ns_name = namespace_declaration
                .prefix
                .as_ref()
                .map(|prefix| -> Result<_> {
                    Ok(xml::NSAttName::PrefixedAttName(xml::PrefixedAttName(
                        xml::NCName::parse_full(&prefix)?,
                    )))
                })
                .unwrap_or(Ok(xml::NSAttName::DefaultAttName))?;
            let value = xml::AttValue::from(namespace_declaration.namespace.as_str());
            let xml_attribute = xml::Attribute::NamespaceDeclaration { ns_name, value };
            attributes.push(xml_attribute);
        }

        for (name, value) in &element.attributes {
            let prefix;
            if let Some(namespace) = &name.namespace {
                let name_namespace_declaration = namespace_declarations_stack
                    .iter()
                    .rfind(|namespace_declaration| namespace_declaration.namespace == *namespace)
                    .ok_or(Error::UndeclaredNamespace(namespace.clone()))?;
                prefix = name_namespace_declaration.prefix.as_ref();
            } else {
                prefix = None
            }

            let att_name;
            if let Some(prefix) = &prefix {
                att_name = xml::QName::PrefixedName(xml::PrefixedName {
                    prefix: xml::Prefix::parse_full(prefix)?,
                    local_part: xml::LocalPart::parse_full(&name.local_name)?,
                })
            } else {
                att_name =
                    xml::QName::UnprefixedName(xml::UnprefixedName::parse_full(&name.local_name)?)
            }

            let value = xml::AttValue::from(value.as_str());

            let xml_attribute = xml::Attribute::Attribute {
                name: att_name,
                value,
            };
            attributes.push(xml_attribute);
        }

        let tag = xml::EmptyElemTag { name, attributes };

        frame.push_str(&tag.to_string());

        // if self.depth.is_empty() {
        //     self.inner.end();
        // }

        Ok(())
    }

    fn write_element_start_to_frame(
        &mut self,
        element: &Element,
        frame: &mut String,
    ) -> Result<()> {
        let _writer = if self.unendable {
            self.inner.ignore_end()
        } else {
            self.inner.try_as_mut()?
        };

        let mut namespace_declarations_stack: Vec<_> = self
            .namespace_declarations
            .iter()
            .flatten()
            .chain(&element.namespace_declaration_overrides)
            .collect();

        let mut namespace_declarations = element.namespace_declaration_overrides.clone();

        let default_namespace_declaration;
        let prefix;
        if let Some(namespace) = &element.name.namespace {
            if let Some(name_namespace_declaration) = namespace_declarations_stack
                .iter()
                .rfind(|namespace_declaration| namespace_declaration.namespace == *namespace)
            {
                prefix = name_namespace_declaration.prefix.as_ref();
            } else {
                default_namespace_declaration = NamespaceDeclaration {
                    prefix: None,
                    namespace: namespace.clone(),
                };
                if namespace_declarations.insert(default_namespace_declaration.clone()) {
                    namespace_declarations_stack.push(&default_namespace_declaration);
                    prefix = None
                } else {
                    return Err(Error::DuplicateNameSpaceDeclaration(NamespaceDeclaration {
                        prefix: None,
                        namespace: namespace.clone(),
                    }));
                }
            }
        } else {
            prefix = None
        }

        let name;
        if let Some(prefix) = &prefix {
            name = xml::QName::PrefixedName(xml::PrefixedName {
                prefix: xml::Prefix::parse_full(prefix)?,
                local_part: xml::LocalPart::parse_full(&element.name.local_name)?,
            })
        } else {
            name = xml::QName::UnprefixedName(xml::UnprefixedName::parse_full(
                &element.name.local_name,
            )?)
        }

        let mut attributes = Vec::new();

        for namespace_declaration in namespace_declarations.iter() {
            let ns_name = namespace_declaration
                .prefix
                .as_ref()
                .map(|prefix| -> Result<_> {
                    Ok(xml::NSAttName::PrefixedAttName(xml::PrefixedAttName(
                        xml::NCName::parse_full(&prefix)?,
                    )))
                })
                .unwrap_or(Ok(xml::NSAttName::DefaultAttName))?;
            let value = xml::AttValue::from(namespace_declaration.namespace.as_str());
            let xml_attribute = xml::Attribute::NamespaceDeclaration { ns_name, value };
            attributes.push(xml_attribute);
        }

        for (name, value) in &element.attributes {
            let prefix;
            if let Some(namespace) = &name.namespace {
                let name_namespace_declaration = namespace_declarations_stack
                    .iter()
                    .rfind(|namespace_declaration| namespace_declaration.namespace == *namespace)
                    .ok_or(Error::UndeclaredNamespace(namespace.clone()))?;
                prefix = name_namespace_declaration.prefix.as_ref();
            } else {
                prefix = None
            }

            let att_name;
            if let Some(prefix) = &prefix {
                att_name = xml::QName::PrefixedName(xml::PrefixedName {
                    prefix: xml::Prefix::parse_full(prefix)?,
                    local_part: xml::LocalPart::parse_full(&name.local_name)?,
                })
            } else {
                att_name =
                    xml::QName::UnprefixedName(xml::UnprefixedName::parse_full(&name.local_name)?)
            }

            let value = xml::AttValue::from(value.as_str());

            let xml_attribute = xml::Attribute::Attribute {
                name: att_name,
                value,
            };
            attributes.push(xml_attribute);
        }

        let s_tag = xml::STag { name, attributes };

        frame.push_str(&s_tag.to_string());

        self.depth.push(element.name.clone());
        self.namespace_declarations
            .push(namespace_declarations.clone());
        Ok(())
    }

    fn write_content_to_frame(&mut self, content: &Content, frame: &mut String) -> Result<()> {
        match content {
            Content::Element(element) => self.write_element_to_frame(element, frame)?,
            Content::Text(text) => {
                let _writer = if self.unendable {
                    self.inner.ignore_end()
                } else {
                    self.inner.try_as_mut()?
                };
                frame.push_str(&escape_str(text))
            }
            // TODO: comments and PI
            Content::PI => {}
            Content::Comment(_) => {}
        }
        Ok(())
    }

    fn write_end_tag_to_frame(&mut self, frame: &mut String) -> Result<()> {
        let _writer = if self.unendable {
            self.inner.ignore_end()
        } else {
            self.inner.try_as_mut()?
        };
        if let Some(name) = &self.depth.pop() {
            let e_tag;
            let namespace_declarations_stack: Vec<_> =
                self.namespace_declarations.iter().flatten().collect();

            let prefix;
            if let Some(namespace) = &name.namespace {
                let name_namespace_declaration = namespace_declarations_stack
                    .iter()
                    .rfind(|namespace_declaration| namespace_declaration.namespace == *namespace)
                    .ok_or(Error::UndeclaredNamespace(namespace.clone()))?;
                prefix = name_namespace_declaration.prefix.as_ref();
            } else {
                prefix = None
            }

            if let Some(prefix) = &prefix {
                e_tag = xml::ETag {
                    name: xml::QName::PrefixedName(xml::PrefixedName {
                        prefix: xml::Prefix::parse_full(prefix)?,
                        local_part: xml::LocalPart::parse_full(&name.local_name)?,
                    }),
                };
            } else {
                e_tag = xml::ETag {
                    name: xml::QName::UnprefixedName(xml::UnprefixedName::parse_full(
                        &name.local_name,
                    )?),
                };
            }
            frame.push_str(&e_tag.to_string());
            self.namespace_declarations.pop();

            // if self.depth.is_empty() {
            //     self.inner.end();
            // }
            Ok(())
        } else {
            return Err(Error::NotInElement("".to_string()));
        }
    }
}

impl<W: AsyncWrite + Unpin + Send> Writer<Loggable<W>> {
    /// Write an XML declaration with the provided `VersionInfo`.
    pub async fn write_declaration(&mut self, version: VersionInfo) -> Result<()> {
        let writer = if self.unendable {
            self.inner.ignore_end()
        } else {
            self.inner.try_as_mut()?
        };
        let declaration = Declaration::version(version);
        let version_info;
        match declaration.version_info {
            VersionInfo::One => version_info = xml::VersionInfo::SingleQuoted(xml::VersionNum::One),
            VersionInfo::OneDotOne => {
                version_info = xml::VersionInfo::SingleQuoted(xml::VersionNum::OneDotOne)
            }
        }
        let declaration = xml::XMLDecl {
            version_info,
            encoding_decl: None,
            sd_decl: None,
        };
        declaration.write(writer).await?;
        Ok(())
    }

    /// Write a full element corresponding with the item implementing `IntoElement` (start tag + content + end tag).
    pub async fn write_full(&mut self, into_element: &impl IntoElement) -> Result<()> {
        let element = into_element.into_element();
        self.write_element(&element).await?;

        let bytes = self.inner.ignore_end().take_log();
        let log = String::from_utf8(bytes)
            .map_err(|err| format!("failed to convert bytes written to str: {err}"));
        info!("wrote element: {log:?}");
        Ok(())
    }

    /// Write the start tag of an item that implements `IntoElement`. Navigates up the document.
    pub async fn write_start(&mut self, into_element: &impl IntoElement) -> Result<()> {
        let element = into_element.into_element();
        self.write_element_start(&element).await?;

        let bytes = self.inner.ignore_end().take_log();
        let log = String::from_utf8(bytes)
            .map_err(|err| format!("failed to convert bytes written to str: {err}"));
        info!("wrote element start: {log:?}");
        Ok(())
    }

    /// Write all the inner content (everything within the start and end tag of an xml element) of an item that implements `IntoElement`. In the case of an empty element, write nothing.
    pub async fn write_all_content(&mut self, into_element: &impl IntoElement) -> Result<()> {
        for content in &into_element.get_content() {
            self.write_content(content).await?;
        }

        let bytes = self.inner.ignore_end().take_log();
        let log = String::from_utf8(bytes)
            .map_err(|err| format!("failed to convert bytes written to str: {err}"));
        info!("wrote element content: {log:?}");
        Ok(())
    }

    /// Write an item that implements `IntoContent`. Could be an element, some text, a comment, etc. Anything that could be included in an element body.
    pub async fn write(&mut self, into_content: &impl IntoContent) -> Result<()> {
        let content = into_content.into_content();
        self.write_content(&content).await?;

        let bytes = self.inner.ignore_end().take_log();
        let log = String::from_utf8(bytes)
            .map_err(|err| format!("failed to convert bytes written to str: {err}"));
        info!("wrote element: {log:?}");
        Ok(())
    }

    /// Navigate down the document structure and write the end tag for the current element opened in the document context.
    pub async fn write_end(&mut self) -> Result<()> {
        self.write_end_tag().await?;

        let bytes = self.inner.ignore_end().take_log();
        let log = String::from_utf8(bytes)
            .map_err(|err| format!("failed to convert bytes written to str: {err}"));
        info!("wrote element end: {log:?}");
        Ok(())
    }

    #[async_recursion]
    /// Write an `Element`.
    pub async fn write_element(&mut self, element: &Element) -> Result<()> {
        if element.content.is_empty() {
            self.write_empty(element).await?;
        } else {
            self.write_element_start(element).await?;
            for content in &element.content {
                self.write_content(content).await?;
            }
            self.write_end_tag().await?;
        }
        Ok(())
    }

    /// Write an empty element tag from an `Element` (ignoring any content).
    pub async fn write_empty(&mut self, element: &Element) -> Result<()> {
        let writer = if self.unendable {
            self.inner.ignore_end()
        } else {
            self.inner.try_as_mut()?
        };
        let mut namespace_declarations_stack: Vec<_> = self
            .namespace_declarations
            .iter()
            .flatten()
            .chain(&element.namespace_declaration_overrides)
            .collect();

        let mut namespace_declarations = element.namespace_declaration_overrides.clone();

        let default_namespace_declaration;
        let prefix;
        if let Some(namespace) = &element.name.namespace {
            if let Some(name_namespace_declaration) = namespace_declarations_stack
                .iter()
                .rfind(|namespace_declaration| namespace_declaration.namespace == *namespace)
            {
                prefix = name_namespace_declaration.prefix.as_ref();
            } else {
                default_namespace_declaration = NamespaceDeclaration {
                    prefix: None,
                    namespace: namespace.clone(),
                };
                if namespace_declarations.insert(default_namespace_declaration.clone()) {
                    namespace_declarations_stack.push(&default_namespace_declaration);
                    prefix = None
                } else {
                    return Err(Error::DuplicateNameSpaceDeclaration(NamespaceDeclaration {
                        prefix: None,
                        namespace: namespace.clone(),
                    }));
                }
            }
        } else {
            prefix = None
        }

        let name;
        if let Some(prefix) = &prefix {
            name = xml::QName::PrefixedName(xml::PrefixedName {
                prefix: xml::Prefix::parse_full(prefix)?,
                local_part: xml::LocalPart::parse_full(&element.name.local_name)?,
            })
        } else {
            name = xml::QName::UnprefixedName(xml::UnprefixedName::parse_full(
                &element.name.local_name,
            )?)
        }

        let mut attributes = Vec::new();

        for namespace_declaration in namespace_declarations.iter() {
            let ns_name = namespace_declaration
                .prefix
                .as_ref()
                .map(|prefix| -> Result<_> {
                    Ok(xml::NSAttName::PrefixedAttName(xml::PrefixedAttName(
                        xml::NCName::parse_full(&prefix)?,
                    )))
                })
                .unwrap_or(Ok(xml::NSAttName::DefaultAttName))?;
            let value = xml::AttValue::from(namespace_declaration.namespace.as_str());
            let xml_attribute = xml::Attribute::NamespaceDeclaration { ns_name, value };
            attributes.push(xml_attribute);
        }

        for (name, value) in &element.attributes {
            let prefix;
            if let Some(namespace) = &name.namespace {
                let name_namespace_declaration = namespace_declarations_stack
                    .iter()
                    .rfind(|namespace_declaration| namespace_declaration.namespace == *namespace)
                    .ok_or(Error::UndeclaredNamespace(namespace.clone()))?;
                prefix = name_namespace_declaration.prefix.as_ref();
            } else {
                prefix = None
            }

            let att_name;
            if let Some(prefix) = &prefix {
                att_name = xml::QName::PrefixedName(xml::PrefixedName {
                    prefix: xml::Prefix::parse_full(prefix)?,
                    local_part: xml::LocalPart::parse_full(&name.local_name)?,
                })
            } else {
                att_name =
                    xml::QName::UnprefixedName(xml::UnprefixedName::parse_full(&name.local_name)?)
            }

            let value = xml::AttValue::from(value.as_str());

            let xml_attribute = xml::Attribute::Attribute {
                name: att_name,
                value,
            };
            attributes.push(xml_attribute);
        }

        let tag = xml::EmptyElemTag { name, attributes };

        tag.write(writer).await?;

        if self.depth.is_empty() {
            self.inner.end();
        }

        Ok(())
    }

    /// Write an element start tag from an `Element`, navigating up in document depth.
    pub async fn write_element_start(&mut self, element: &Element) -> Result<()> {
        let writer = if self.unendable {
            self.inner.ignore_end()
        } else {
            self.inner.try_as_mut()?
        };
        let mut namespace_declarations_stack: Vec<_> = self
            .namespace_declarations
            .iter()
            .flatten()
            .chain(&element.namespace_declaration_overrides)
            .collect();

        let mut namespace_declarations = element.namespace_declaration_overrides.clone();

        let default_namespace_declaration;
        let prefix;
        if let Some(namespace) = &element.name.namespace {
            if let Some(name_namespace_declaration) = namespace_declarations_stack
                .iter()
                .rfind(|namespace_declaration| namespace_declaration.namespace == *namespace)
            {
                prefix = name_namespace_declaration.prefix.as_ref();
            } else {
                default_namespace_declaration = NamespaceDeclaration {
                    prefix: None,
                    namespace: namespace.clone(),
                };
                if namespace_declarations.insert(default_namespace_declaration.clone()) {
                    namespace_declarations_stack.push(&default_namespace_declaration);
                    prefix = None
                } else {
                    return Err(Error::DuplicateNameSpaceDeclaration(NamespaceDeclaration {
                        prefix: None,
                        namespace: namespace.clone(),
                    }));
                }
            }
        } else {
            prefix = None
        }

        let name;
        if let Some(prefix) = &prefix {
            name = xml::QName::PrefixedName(xml::PrefixedName {
                prefix: xml::Prefix::parse_full(prefix)?,
                local_part: xml::LocalPart::parse_full(&element.name.local_name)?,
            })
        } else {
            name = xml::QName::UnprefixedName(xml::UnprefixedName::parse_full(
                &element.name.local_name,
            )?)
        }

        let mut attributes = Vec::new();

        for namespace_declaration in namespace_declarations.iter() {
            let ns_name = namespace_declaration
                .prefix
                .as_ref()
                .map(|prefix| -> Result<_> {
                    Ok(xml::NSAttName::PrefixedAttName(xml::PrefixedAttName(
                        xml::NCName::parse_full(&prefix)?,
                    )))
                })
                .unwrap_or(Ok(xml::NSAttName::DefaultAttName))?;
            let value = xml::AttValue::from(namespace_declaration.namespace.as_str());
            let xml_attribute = xml::Attribute::NamespaceDeclaration { ns_name, value };
            attributes.push(xml_attribute);
        }

        for (name, value) in &element.attributes {
            let prefix;
            if let Some(namespace) = &name.namespace {
                let name_namespace_declaration = namespace_declarations_stack
                    .iter()
                    .rfind(|namespace_declaration| namespace_declaration.namespace == *namespace)
                    .ok_or(Error::UndeclaredNamespace(namespace.clone()))?;
                prefix = name_namespace_declaration.prefix.as_ref();
            } else {
                prefix = None
            }

            let att_name;
            if let Some(prefix) = &prefix {
                att_name = xml::QName::PrefixedName(xml::PrefixedName {
                    prefix: xml::Prefix::parse_full(prefix)?,
                    local_part: xml::LocalPart::parse_full(&name.local_name)?,
                })
            } else {
                att_name =
                    xml::QName::UnprefixedName(xml::UnprefixedName::parse_full(&name.local_name)?)
            }

            let value = xml::AttValue::from(value.as_str());

            let xml_attribute = xml::Attribute::Attribute {
                name: att_name,
                value,
            };
            attributes.push(xml_attribute);
        }

        let s_tag = xml::STag { name, attributes };

        s_tag.write(writer).await?;

        self.depth.push(element.name.clone());
        self.namespace_declarations
            .push(namespace_declarations.clone());
        Ok(())
    }

    /// Write some `Content`.
    pub async fn write_content(&mut self, content: &Content) -> Result<()> {
        match content {
            Content::Element(element) => self.write_element(element).await?,
            Content::Text(text) => {
                let writer = if self.unendable {
                    self.inner.ignore_end()
                } else {
                    self.inner.try_as_mut()?
                };
                writer.write_all(escape_str(text).as_bytes()).await?
            }
            // TODO: comments and PI
            Content::PI => {}
            Content::Comment(_) => {}
        }
        Ok(())
    }

    /// Write an end tag (depending on the current document context), moving back down in the document.
    pub async fn write_end_tag(&mut self) -> Result<()> {
        let writer = if self.unendable {
            self.inner.ignore_end()
        } else {
            self.inner.try_as_mut()?
        };
        if let Some(name) = &self.depth.pop() {
            let e_tag;
            let namespace_declarations_stack: Vec<_> =
                self.namespace_declarations.iter().flatten().collect();

            let prefix;
            if let Some(namespace) = &name.namespace {
                let name_namespace_declaration = namespace_declarations_stack
                    .iter()
                    .rfind(|namespace_declaration| namespace_declaration.namespace == *namespace)
                    .ok_or(Error::UndeclaredNamespace(namespace.clone()))?;
                prefix = name_namespace_declaration.prefix.as_ref();
            } else {
                prefix = None
            }

            if let Some(prefix) = &prefix {
                e_tag = xml::ETag {
                    name: xml::QName::PrefixedName(xml::PrefixedName {
                        prefix: xml::Prefix::parse_full(prefix)?,
                        local_part: xml::LocalPart::parse_full(&name.local_name)?,
                    }),
                };
            } else {
                e_tag = xml::ETag {
                    name: xml::QName::UnprefixedName(xml::UnprefixedName::parse_full(
                        &name.local_name,
                    )?),
                };
            }
            e_tag.write(writer).await?;
            self.namespace_declarations.pop();

            if self.depth.is_empty() {
                self.inner.end();
            }
            Ok(())
        } else {
            return Err(Error::NotInElement("".to_string()));
        }
    }
}

#[cfg(test)]
mod test {
    use crate::{
        reader::{test::*, Reader},
        writer::Writer,
    };

    #[tokio::test]
    async fn test_element_write() {
        let mock = MockAsyncReader::new(TEST_DOC);
        let mut reader = Reader::new(mock);
        let element = reader.read_element().await.unwrap();
        let stdout = tokio::io::stdout();
        let mut writer = Writer::new(stdout);
        writer.write_element(&element).await.unwrap();
    }
}