diff options
Diffstat (limited to 'src/writer.rs')
| -rw-r--r-- | src/writer.rs | 97 |
1 files changed, 67 insertions, 30 deletions
diff --git a/src/writer.rs b/src/writer.rs index 40a935b..5cd0d0e 100644 --- a/src/writer.rs +++ b/src/writer.rs @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden> +// +// SPDX-License-Identifier: AGPL-3.0-or-later + use std::collections::HashSet; use async_recursion::async_recursion; @@ -9,14 +13,17 @@ use web_sys::WebSocket; use crate::{ declaration::{Declaration, VersionInfo}, element::{escape_str, Content, Element, IntoContent, IntoElement, Name, NamespaceDeclaration}, - endable::Endable, error::Error, - loggable::Loggable, xml::{self, composers::Composer, parsers_complete::Parser}, Result, XMLNS_NS, XML_NS, }; +use endable::Endable; +pub use loggable::Loggable; + +mod endable; +mod loggable; -// pub struct Writer<W, C = Composer> { +/// Writer that tracks depth and corresponding declared/available namespaces. #[derive(Debug)] pub struct Writer<W> { inner: Endable<W>, @@ -26,6 +33,7 @@ pub struct Writer<W> { } 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 { @@ -44,6 +52,7 @@ impl<W> Writer<Loggable<W>> { } } + /// 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 { @@ -62,6 +71,7 @@ impl<W> Writer<Loggable<W>> { } } + /// Extract the inner type from the `Writer`. pub fn into_inner(self) -> W { self.inner.into_inner().into_inner() } @@ -69,6 +79,7 @@ impl<W> Writer<Loggable<W>> { #[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 { @@ -87,10 +98,12 @@ impl Writer<WebSocket> { } } + /// 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 { @@ -109,6 +122,7 @@ impl Writer<WebSocket> { } } + /// 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; @@ -132,6 +146,7 @@ impl Writer<WebSocket> { 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(); @@ -141,6 +156,7 @@ impl Writer<WebSocket> { 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(); @@ -150,6 +166,7 @@ impl Writer<WebSocket> { 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() { @@ -160,6 +177,7 @@ impl Writer<WebSocket> { 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(); @@ -169,7 +187,7 @@ impl Writer<WebSocket> { Ok(()) } - // pub async fn write_end(&mut self) + /// 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)?; @@ -178,7 +196,7 @@ impl Writer<WebSocket> { Ok(()) } - pub fn write_element_to_frame(&mut self, element: &Element, frame: &mut String) -> Result<()> { + 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 { @@ -191,8 +209,8 @@ impl Writer<WebSocket> { Ok(()) } - pub fn write_empty_to_frame(&mut self, element: &Element, frame: &mut String) -> Result<()> { - let writer = if self.unendable { + 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()? @@ -306,12 +324,12 @@ impl Writer<WebSocket> { Ok(()) } - pub fn write_element_start_to_frame( + fn write_element_start_to_frame( &mut self, element: &Element, frame: &mut String, ) -> Result<()> { - let writer = if self.unendable { + let _writer = if self.unendable { self.inner.ignore_end() } else { self.inner.try_as_mut()? @@ -424,11 +442,11 @@ impl Writer<WebSocket> { Ok(()) } - pub fn write_content_to_frame(&mut self, content: &Content, frame: &mut String) -> Result<()> { + 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 { + let _writer = if self.unendable { self.inner.ignore_end() } else { self.inner.try_as_mut()? @@ -442,8 +460,8 @@ impl Writer<WebSocket> { Ok(()) } - pub fn write_end_tag_to_frame(&mut self, frame: &mut String) -> Result<()> { - let writer = if self.unendable { + 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()? @@ -491,8 +509,8 @@ impl Writer<WebSocket> { } } -#[cfg(not(target_arch = "wasm32"))] 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() @@ -516,53 +534,68 @@ impl<W: AsyncWrite + Unpin + Send> Writer<Loggable<W>> { 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 = str::from_utf8(bytes).unwrap_or("failed to convert bytes written to str"); - info!("wrote element: {}", log); + + 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 = str::from_utf8(bytes).unwrap_or("failed to convert bytes written to str"); - info!("wrote element start: {}", log); + + 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 = str::from_utf8(bytes).unwrap_or("failed to convert bytes written to str"); - info!("wrote element content: {}", log); + + 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 = str::from_utf8(bytes).unwrap_or("failed to convert bytes written to str"); - info!("wrote element: {}", log); + + 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(()) } - // pub async fn write_end(&mut self) + /// 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 = str::from_utf8(bytes).unwrap_or("failed to convert bytes written to str"); - info!("wrote element end: {}", log); + + 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?; @@ -576,6 +609,7 @@ impl<W: AsyncWrite + Unpin + Send> Writer<Loggable<W>> { 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() @@ -690,6 +724,7 @@ impl<W: AsyncWrite + Unpin + Send> Writer<Loggable<W>> { 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() @@ -803,6 +838,7 @@ impl<W: AsyncWrite + Unpin + Send> Writer<Loggable<W>> { Ok(()) } + /// Write some `Content`. pub async fn write_content(&mut self, content: &Content) -> Result<()> { match content { Content::Element(element) => self.write_element(element).await?, @@ -821,6 +857,7 @@ impl<W: AsyncWrite + Unpin + Send> Writer<Loggable<W>> { 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() |
