aboutsummaryrefslogtreecommitdiffstats
path: root/src/element.rs
diff options
context:
space:
mode:
authorLibravatar cel 🌸 <cel@bunny.garden>2025-05-30 13:29:02 +0100
committerLibravatar cel 🌸 <cel@bunny.garden>2025-05-30 13:29:02 +0100
commit6d1f28eb79c6c70b058fcbae5047dbd744764149 (patch)
tree0dd308a59df464d7c4156a459b56c205c9deb81f /src/element.rs
parent826a17e34c167b53bd63544c5825b689a3a8fca7 (diff)
downloadpeanuts-6d1f28eb79c6c70b058fcbae5047dbd744764149.tar.gz
peanuts-6d1f28eb79c6c70b058fcbae5047dbd744764149.tar.bz2
peanuts-6d1f28eb79c6c70b058fcbae5047dbd744764149.zip
doc: everything
Diffstat (limited to 'src/element.rs')
-rw-r--r--src/element.rs66
1 files changed, 62 insertions, 4 deletions
diff --git a/src/element.rs b/src/element.rs
index 1c1366a..b6b3c15 100644
--- a/src/element.rs
+++ b/src/element.rs
@@ -11,12 +11,15 @@ use crate::{
Result,
};
+/// Result type for the `FromElement` trait.
pub type DeserializeResult<T> = std::result::Result<T, DeserializeError>;
+/// Trait for conversion from an `Element` into another type, for deserialisation from a `Reader`.
pub trait FromElement: Sized {
fn from_element(element: Element) -> DeserializeResult<Self>;
}
+/// Trait for conversion from a type into an `Element`, for serialisation into a `Writer`.
pub trait IntoElement {
fn builder(&self) -> ElementBuilder;
@@ -45,18 +48,24 @@ pub struct Name {
pub local_name: String,
}
+/// `Content` represents anything that can be the content of an XML element.
#[derive(Debug, Clone)]
pub enum Content {
+ /// A child element.
Element(Element),
+ /// A text value.
Text(String),
+ /// A processing instruction.
PI,
+ /// A comment.
Comment(String),
}
// should this be a trait?
+/// `Element` represents an XML element that can be written to a `Writer` or read from a `Reader`.
#[derive(Debug, Clone)]
pub struct Element {
- pub name: Name,
+ pub(crate) name: Name,
// namespace: Name,
// each element once created contains the qualified namespace information for that element
// the name contains the qualified namespace so this is unnecessary
@@ -64,15 +73,15 @@ pub struct Element {
// hashmap of explicit namespace declarations on the element itself only
// possibly not needed as can be calculated at write time depending on context and qualified namespace, and for reading, element validity and namespaces are kept track of by the reader.
// change this to custom namespace declarations only, so you can override the definition of namespaces if you wish
- pub namespace_declaration_overrides: HashSet<NamespaceDeclaration>,
+ pub(crate) namespace_declaration_overrides: HashSet<NamespaceDeclaration>,
// attributes can be in a different namespace than the element. how to make sure they are valid?
// maybe include the namespace instead of or with the prefix
// you can calculate the prefix from the namespaced name and the current writer context
// you can validate the prefix and calculate the namespace from the current reader context
// this results in readers and writers being able to return qualification errors as they aren't able to create elements until every part is qualified.
- pub attributes: HashMap<Name, String>,
+ pub(crate) attributes: HashMap<Name, String>,
// TODO: make a hashmap maybe? to be able to address parts of the content individually
- pub content: VecDeque<Content>,
+ pub(crate) content: VecDeque<Content>,
}
impl FromElement for Element {
@@ -82,10 +91,12 @@ impl FromElement for Element {
}
impl Element {
+ /// Return the namespace the xml element is qualified by, and the localname, for matching on the element when you don't know which kind of element to expect.
pub fn identify(&self) -> (Option<&str>, &str) {
(self.name.namespace.as_deref(), &self.name.local_name)
}
+ /// Check the localname of the element.
pub fn check_name(&self, name: &str) -> DeserializeResult<()> {
if self.name.local_name == name {
Ok(())
@@ -97,6 +108,7 @@ impl Element {
}
}
+ /// Check the element is qualified by a namespace.
pub fn check_namespace(&self, namespace: &str) -> DeserializeResult<()> {
if self.name.namespace.as_deref() == Some(namespace) {
return Ok(());
@@ -114,6 +126,7 @@ impl Element {
}
}
+ /// Optionally extract an attribute from the element.
pub fn attribute_opt<V: FromStr>(&mut self, att_name: &str) -> DeserializeResult<Option<V>> {
if let Some(att_value) = self.attributes.remove(&Name {
namespace: None,
@@ -127,6 +140,7 @@ impl Element {
}
}
+ /// Optionally extract a namespaced attribute from the elmeent.
pub fn attribute_opt_namespaced<V: FromStr>(
&mut self,
att_name: &str,
@@ -144,6 +158,7 @@ impl Element {
}
}
+ /// Extract an attribute from the element.
pub fn attribute<V: FromStr>(&mut self, att_name: &str) -> DeserializeResult<V> {
let name = Name {
namespace: None,
@@ -158,6 +173,7 @@ impl Element {
}
}
+ /// Extract a namespaced attribute from the element.
pub fn attribute_namespaced<V: FromStr>(
&mut self,
att_name: &str,
@@ -176,6 +192,7 @@ impl Element {
}
}
+ /// Ensure there are no more attributes on the element.
pub fn no_more_attributes(self) -> DeserializeResult<Self> {
if self.attributes.is_empty() {
Ok(self)
@@ -186,6 +203,8 @@ impl Element {
// for xs:any
+ /// Extract a child of type `T` from the element.
+ /// E.g. when there is an xs:any.
pub fn child_one<T: FromElement>(&mut self) -> DeserializeResult<T> {
if let Some(position) = self.content.iter().position(|content| match content {
Content::Element(element) => <T as FromElement>::from_element(element.clone()).is_ok(),
@@ -204,6 +223,8 @@ impl Element {
}
}
+ /// Optionally extract a child of type `T` from the element.
+ /// E.g. when there is an xs:any.
pub fn child_opt<T: FromElement>(&mut self) -> DeserializeResult<Option<T>> {
if let Some(position) = self.content.iter().position(|content| match content {
Content::Element(element) => <T as FromElement>::from_element(element.clone()).is_ok(),
@@ -222,6 +243,7 @@ impl Element {
}
}
+ /// Extract several children of type `T` from the element.
pub fn children<T: FromElement>(&mut self) -> DeserializeResult<Vec<T>> {
let (children, rest): (VecDeque<_>, VecDeque<_>) = self
.content
@@ -252,6 +274,7 @@ impl Element {
Ok(children)
}
+ /// Extract a text value from the element.
pub fn value<V: FromStr>(&mut self) -> DeserializeResult<V> {
if let Some(position) = self.content.iter().position(|content| match content {
Content::Element(_) => false,
@@ -270,6 +293,7 @@ impl Element {
}
}
+ /// Optionally extract a text value from the element.
pub fn value_opt<V: FromStr>(&mut self) -> DeserializeResult<Option<V>> {
if let Some(position) = self.content.iter().position(|content| match content {
Content::Element(_) => false,
@@ -290,6 +314,8 @@ impl Element {
// for xs:sequence
+ /// Pop a child element of type `T` from the element.
+ /// E.g. when there is an xs:sequence.
pub fn pop_child_one<T: FromElement>(&mut self) -> DeserializeResult<T> {
loop {
let child = self
@@ -307,6 +333,8 @@ impl Element {
}
}
+ /// Optionally pop a child element of type `T` from the element.
+ /// E.g. when there is an xs:sequence.
pub fn pop_child_opt<T: FromElement>(&mut self) -> DeserializeResult<Option<T>> {
loop {
let child = self.content.pop_front();
@@ -327,6 +355,8 @@ impl Element {
}
}
+ /// Pop several children of type `T` from the element.
+ /// E.g. when there is an xs:sequence.
pub fn pop_children<T: FromElement>(&mut self) -> DeserializeResult<Vec<T>> {
let mut children = Vec::new();
loop {
@@ -360,6 +390,8 @@ impl Element {
}
}
+ /// Pop a text value from the element.
+ /// E.g. when there is an xs:sequence.
pub fn pop_value<V: FromStr>(&mut self) -> DeserializeResult<V> {
loop {
let child = self
@@ -381,6 +413,8 @@ impl Element {
}
}
+ /// Optionally pop a text value from the element.
+ /// E.g. when there is an xs:sequence.
pub fn pop_value_opt<V: FromStr>(&mut self) -> DeserializeResult<Option<V>> {
loop {
let child = self.content.pop_front();
@@ -404,6 +438,7 @@ impl Element {
}
}
+ /// Ensure there is no more element content left.
pub fn no_more_content(self) -> DeserializeResult<Self> {
if self
.content
@@ -423,11 +458,13 @@ impl Element {
}
}
+ /// Create a new `ElementBuilder`.
pub fn builder(name: impl ToString, namespace: Option<impl ToString>) -> ElementBuilder {
ElementBuilder::new(name, namespace)
}
}
+/// Builder for the `Element` type.
pub struct ElementBuilder {
name: Name,
namespace_declaration_overrides: Vec<NamespaceDeclaration>,
@@ -436,6 +473,7 @@ pub struct ElementBuilder {
}
impl ElementBuilder {
+ /// Create a new `ElementBuilder`.
pub fn new(name: impl ToString, namespace: Option<impl ToString>) -> Self {
Self {
name: Name {
@@ -448,6 +486,7 @@ impl ElementBuilder {
}
}
+ /// Push a namespace declaration override onto the element builder.
pub fn push_namespace_declaration_override(
mut self,
prefix: Option<impl ToString>,
@@ -461,6 +500,7 @@ impl ElementBuilder {
self
}
+ /// Push an attribute onto the element builder.
pub fn push_attribute<N: ToString, V: ToString>(mut self, name: N, value: V) -> Self {
self.attributes.push((
// TODO: make sure name is a valid name, same for prefixes
@@ -473,6 +513,7 @@ impl ElementBuilder {
self
}
+ /// Push a namespaced attribute onto the element builder.
pub fn push_attribute_namespaced(
mut self,
namespace: impl ToString,
@@ -490,17 +531,20 @@ impl ElementBuilder {
}
// TODO: use references for everything to avoid cloning
+ /// Push a child element onto the element builder.
pub fn push_child(mut self, child: impl IntoElement) -> Self {
self.content.push(ContentBuilder::Element(child.builder()));
self
}
// TODO: better way for push_text to work, empty string should be empty element no matter what
+ /// Push a text value onto the element builder.
pub fn push_text(mut self, text: impl ToString) -> Self {
self.content.push(ContentBuilder::Text(text.to_string()));
self
}
+ /// Optionally push an attribute onto the element builder.
pub fn push_attribute_opt(self, name: impl ToString, value: Option<impl ToString>) -> Self {
if let Some(value) = value {
self.push_attribute(name, value)
@@ -509,6 +553,7 @@ impl ElementBuilder {
}
}
+ /// Optionally push a namespaced attribute onto the element builder.
pub fn push_attribute_opt_namespaced(
self,
namespace: impl ToString,
@@ -522,6 +567,7 @@ impl ElementBuilder {
}
}
+ /// Optionally push a child onto the element builder.
pub fn push_child_opt(self, child: Option<impl IntoElement>) -> Self {
if let Some(child) = child {
self.push_child(child)
@@ -530,6 +576,7 @@ impl ElementBuilder {
}
}
+ /// Optionally push a text value onto the element builder.
pub fn push_text_opt(self, text: Option<impl ToString>) -> Self {
if let Some(text) = text {
self.push_text(text)
@@ -538,11 +585,13 @@ impl ElementBuilder {
}
}
+ /// Optionally push a content item onto the element builder.
pub fn push_content(mut self, content: ContentBuilder) -> Self {
self.content.push(content);
self
}
+ /// Optionally push content items onto the element builder.
pub fn push_children(self, children: Vec<impl IntoContent>) -> Self {
let mut element_builder = self;
for child in children {
@@ -551,6 +600,7 @@ impl ElementBuilder {
element_builder
}
+ /// Build an `Element` from the `ElementBuilder`.
pub fn build(&self) -> Result<Element> {
let mut namespace_declaration_overrides = HashSet::new();
for namespace_declaration in &self.namespace_declaration_overrides {
@@ -588,6 +638,7 @@ impl ElementBuilder {
}
}
+/// Trait for conversion from a type into an (`Element`) `Content` item.
pub trait IntoContent {
fn into_content(&self) -> Content {
self.builder().build().unwrap()
@@ -605,17 +656,23 @@ where
}
}
+/// Trait for conversion from some `Element` `Content` into another type.
pub trait FromContent: Sized {
fn from_content(content: Content) -> DeserializeResult<Self>;
}
+/// Builder for `Content`.
pub enum ContentBuilder {
+ /// A child element.
Element(ElementBuilder),
+ /// A text value.
Text(String),
+ /// A comment.
Comment(String),
}
impl ContentBuilder {
+ /// Build a `Content` item from the builder.
pub fn build(&self) -> Result<Content> {
match self {
ContentBuilder::Element(element_builder) => {
@@ -627,6 +684,7 @@ impl ContentBuilder {
}
}
+/// Escape a str into an XML escaped string.
pub fn escape_str(s: &str) -> String {
let mut string = String::new();
for str in s.split_inclusive(|c| c == '<' || c == '&' || c == '>') {