aboutsummaryrefslogtreecommitdiffstats
path: root/src/writer.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/writer.rs')
-rw-r--r--src/writer.rs97
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()