diff options
author | 2025-04-13 10:38:58 +0100 | |
---|---|---|
committer | 2025-04-13 10:38:58 +0100 | |
commit | 1c55b582a8e874b5dc776950c92a1ecf622d03fe (patch) | |
tree | cc3f21fa0a8d1fa6ea32ab0c3fd391c14c3ba29f /src/writer.rs | |
parent | c658ab440f8e69ac406b18732dbf276c084926b6 (diff) | |
download | peanuts-1c55b582a8e874b5dc776950c92a1ecf622d03fe.tar.gz peanuts-1c55b582a8e874b5dc776950c92a1ecf622d03fe.tar.bz2 peanuts-1c55b582a8e874b5dc776950c92a1ecf622d03fe.zip |
feat: websocket-framed reader and writer
Diffstat (limited to '')
-rw-r--r-- | src/writer.rs | 483 |
1 files changed, 472 insertions, 11 deletions
diff --git a/src/writer.rs b/src/writer.rs index 7ed1775..adb7852 100644 --- a/src/writer.rs +++ b/src/writer.rs @@ -3,6 +3,8 @@ 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}, @@ -17,12 +19,13 @@ use crate::{ // pub struct Writer<W, C = Composer> { #[derive(Debug)] pub struct Writer<W> { - inner: Endable<Loggable<W>>, + inner: Endable<W>, + unendable: bool, depth: Vec<Name>, namespace_declarations: Vec<HashSet<NamespaceDeclaration>>, } -impl<W> Writer<W> { +impl<W> Writer<Loggable<W>> { pub fn new(writer: W) -> Self { let mut default_declarations = HashSet::new(); default_declarations.insert(NamespaceDeclaration { @@ -35,6 +38,25 @@ impl<W> Writer<W> { }); Self { inner: Endable::new(Loggable::new(writer)), + unendable: false, + depth: Vec::new(), + namespace_declarations: vec![default_declarations], + } + } + + 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], } @@ -45,9 +67,434 @@ impl<W> Writer<W> { } } -impl<W: AsyncWrite + Unpin + Send> Writer<W> { +#[cfg(target_arch = "wasm32")] +impl Writer<WebSocket> { + 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], + } + } + + 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], + } + } + + 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(()) + } + + 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(()) + } + + 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(()) + } + + 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(()) + } + + 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(()) + } + + // pub async fn write_end(&mut self) + 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(()) + } + + pub 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(()) + } + + pub 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(()) + } + + pub 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(()) + } + + pub 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(()) + } + + pub 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())); + } + } +} + +#[cfg(not(target_arch = "wasm32"))] +impl<W: AsyncWrite + Unpin + Send> Writer<Loggable<W>> { pub async fn write_declaration(&mut self, version: VersionInfo) -> Result<()> { - let writer = self.inner.try_as_mut()?; + 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 { @@ -126,7 +573,11 @@ impl<W: AsyncWrite + Unpin + Send> Writer<W> { } pub async fn write_empty(&mut self, element: &Element) -> Result<()> { - let writer = self.inner.try_as_mut()?; + 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() @@ -236,7 +687,11 @@ impl<W: AsyncWrite + Unpin + Send> Writer<W> { } pub async fn write_element_start(&mut self, element: &Element) -> Result<()> { - let writer = self.inner.try_as_mut()?; + 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() @@ -348,10 +803,12 @@ impl<W: AsyncWrite + Unpin + Send> Writer<W> { match content { Content::Element(element) => self.write_element(element).await?, Content::Text(text) => { - self.inner - .try_as_mut()? - .write_all(escape_str(text).as_bytes()) - .await? + 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 => {} @@ -361,7 +818,11 @@ impl<W: AsyncWrite + Unpin + Send> Writer<W> { } pub async fn write_end_tag(&mut self) -> Result<()> { - let writer = self.inner.try_as_mut()?; + 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<_> = |