//! This module contains helper traits that are used internally to manage //! sequences of types implementing the `Content` trait, allowing us to //! statically manage parent section lookups without doing extra work on //! runtime. use crate::encoding::Encoder; use crate::simple::simple_section::SimpleSection; use crate::simple::{SimpleValue, SimpleError, SimpleInnerError}; use crate::template::Section; use crate::Content; /// Helper trait used to rotate a queue of parent `Content`s. Think of this as of a /// rotating buffer such that: /// /// ```text /// (A, B, C, D).combine(X) -> (B, C, D, X) /// ``` /// /// This allows us to keep track of up to 3 parent contexts. The constraint is implemented /// so that self-referencing `Content`s don't blow up the stack on compilation. pub trait Combine { /// First type for the result tuple type I: Content + Copy + Sized; /// Second type for the result tuple type J: Content + Copy + Sized; /// Third type for the result tuple type K: Content + Copy + Sized; /// Type when we crawl back one item type Previous: ContentSequence; /// Combines current tuple with a new element. fn combine(self, other: &X) -> (Self::I, Self::J, Self::K, &X); /// Crawl back to the previous tuple fn crawl_back(self) -> Self::Previous; } /// Helper trait that re-exposes `render_field_x` methods of a `Content` trait, /// calling those methods internally on all `Content`s contained within `Self`. pub trait ContentSequence: Combine + Sized + Copy { /// Render a field by the hash **or** string of its name. /// /// This will escape HTML characters, eg: `<` will become `<`. #[inline] fn render_field_escaped( &self, _hash: u64, _name: &str, _encoder: &mut E, ) -> Result<(), E::Error> { Ok(()) } /// Render a field by the hash **or** string of its name. /// /// This doesn't perform any escaping at all. #[inline] fn render_field_unescaped( &self, _hash: u64, _name: &str, _encoder: &mut E, ) -> Result<(), E::Error> { Ok(()) } /// Apply a dto field by the hash **or** string of its name. /// /// This doesn't perform any escaping at all. #[inline] fn apply_field_unescaped( &self, _hash: u64, _name: &str ) -> Result { Err(SimpleInnerError(format!("the data type of field is not supported")).into()) } /// Render a field by the hash **or** string of its name, as a section. #[inline] fn render_field_section( &self, _hash: u64, _name: &str, _section: Section

, _encoder: &mut E, ) -> Result<(), E::Error> where P: ContentSequence, E: Encoder, { Ok(()) } /// Apply a dto field by the hash **or** string of its name, as a section. #[inline] fn apply_field_section

( &self, _hash: u64, _name: &str, _section: SimpleSection

, ) -> Result where P: ContentSequence, { Err(SimpleInnerError(format!("the data type of field: {} is not supported ", _name)).into()) } /// Render a field, by the hash of **or** string its name, as an inverse section. #[inline] fn render_field_inverse( &self, _hash: u64, _name: &str, _section: Section

, _encoder: &mut E, ) -> Result<(), E::Error> where P: ContentSequence, E: Encoder, { Ok(()) } /// Render a field by the hash **or** string of its name, as a section. #[inline] fn render_field_notnone_section( &self, _hash: u64, _name: &str, _section: Section

, _encoder: &mut E, ) -> Result where P: ContentSequence, E: Encoder, { Ok(false) } } impl Combine for () { type I = (); type J = (); type K = (); type Previous = (); #[inline] fn combine(self, other: &X) -> ((), (), (), &X) { ((), (), (), other) } #[inline] fn crawl_back(self) -> Self::Previous {} } impl ContentSequence for () {} impl Combine for (A, B, C, D) where A: Content + Copy, B: Content + Copy, C: Content + Copy, D: Content + Copy, { type I = B; type J = C; type K = D; type Previous = ((), A, B, C); #[inline] fn combine(self, other: &X) -> (B, C, D, &X) { (self.1, self.2, self.3, other) } #[inline] fn crawl_back(self) -> ((), A, B, C) { ((), self.0, self.1, self.2) } } impl ContentSequence for (A, B, C, D) where A: Content + Copy, B: Content + Copy, C: Content + Copy, D: Content + Copy, { #[inline] fn render_field_escaped( &self, hash: u64, name: &str, encoder: &mut E, ) -> Result<(), E::Error> { if !self.3.render_field_escaped(hash, name, encoder)? && !self.2.render_field_escaped(hash, name, encoder)? && !self.1.render_field_escaped(hash, name, encoder)? { self.0.render_field_escaped(hash, name, encoder)?; } Ok(()) } #[inline] fn render_field_unescaped( &self, hash: u64, name: &str, encoder: &mut E, ) -> Result<(), E::Error> { if !self.3.render_field_unescaped(hash, name, encoder)? && !self.2.render_field_unescaped(hash, name, encoder)? && !self.1.render_field_unescaped(hash, name, encoder)? { self.0.render_field_unescaped(hash, name, encoder)?; } Ok(()) } #[inline] fn apply_field_unescaped( &self, hash: u64, name: &str, ) -> Result { self.3.apply_field_unescaped(hash, name) } #[inline] fn render_field_section( &self, hash: u64, name: &str, section: Section

, encoder: &mut E, ) -> Result<(), E::Error> where P: ContentSequence, E: Encoder, { let rst = self.3.render_field_section(hash, name, section, encoder)?; if !rst { let section = section.without_last(); let rst = self.2.render_field_section(hash, name, section, encoder)?; if !rst { let section = section.without_last(); let rst = self.1.render_field_section(hash, name, section, encoder)?; if !rst { let section = section.without_last(); self.0.render_field_section(hash, name, section, encoder)?; } } } Ok(()) } #[inline] fn apply_field_section

( &self, hash: u64, name: &str, section: SimpleSection

, ) -> Result where P: ContentSequence, { self.3.apply_field_section(hash, name, section) } #[inline] fn render_field_inverse( &self, hash: u64, name: &str, section: Section

, encoder: &mut E, ) -> Result<(), E::Error> where P: ContentSequence, E: Encoder, { if !self.3.render_field_inverse(hash, name, section, encoder)? && !self.2.render_field_inverse(hash, name, section, encoder)? && !self.1.render_field_inverse(hash, name, section, encoder)? && !self.0.render_field_inverse(hash, name, section, encoder)? { section.render(encoder, Option::<&()>::None)?; } Ok(()) } #[inline] fn render_field_notnone_section( &self, hash: u64, name: &str, section: Section

, encoder: &mut E, ) -> Result where P: ContentSequence, E: Encoder, { let mut rst = self.3.render_field_notnone_section(hash, name, section, encoder)?; if !rst { let section = section.without_last(); rst = self.2.render_field_notnone_section(hash, name, section, encoder)?; if !rst { let section = section.without_last(); rst = self.1.render_field_notnone_section(hash, name, section, encoder)?; if !rst { let section = section.without_last(); rst = self.0.render_field_notnone_section(hash, name, section, encoder)?; } } } Ok(rst) } }