diff options
author | 2025-05-30 13:29:02 +0100 | |
---|---|---|
committer | 2025-05-30 13:29:02 +0100 | |
commit | 6d1f28eb79c6c70b058fcbae5047dbd744764149 (patch) | |
tree | 0dd308a59df464d7c4156a459b56c205c9deb81f /src/element.rs | |
parent | 826a17e34c167b53bd63544c5825b689a3a8fca7 (diff) | |
download | peanuts-6d1f28eb79c6c70b058fcbae5047dbd744764149.tar.gz peanuts-6d1f28eb79c6c70b058fcbae5047dbd744764149.tar.bz2 peanuts-6d1f28eb79c6c70b058fcbae5047dbd744764149.zip |
doc: everything
Diffstat (limited to 'src/element.rs')
-rw-r--r-- | src/element.rs | 66 |
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 == '>') { |