dysql_tpl/template/
section.rs

1// Ramhorns  Copyright (C) 2019  Maciej Hirsz
2//
3// This file is part of Ramhorns. This program comes with ABSOLUTELY NO WARRANTY;
4// This is free software, and you are welcome to redistribute it under the
5// conditions of the GNU General Public License version 3.0.
6//
7// You should have received a copy of the GNU General Public License
8// along with Ramhorns.  If not, see <http://www.gnu.org/licenses/>
9
10use super::{Block, Tag};
11use crate::encoding::Encoder;
12use crate::{Content, Next};
13use crate::traits::ContentSequence;
14use std::ops::Range;
15
16/// A section of a `Template` that can be rendered individually, usually delimited by
17/// `{{#section}} ... {{/section}}` tags.
18#[derive(Clone, Copy)]
19pub struct Section<'section, Contents: ContentSequence> {
20    blocks: &'section [Block],
21    contents: Contents,
22}
23
24impl<'section> Section<'section, ()> {
25    #[inline]
26    pub(crate) fn new(blocks: &'section [Block]) -> Self {
27        let rst = Self {
28            blocks,
29            contents: (),
30        };
31
32        rst
33    }
34}
35
36impl<'section, C> Section<'section, C>
37where
38    C: ContentSequence,
39{
40    #[inline]
41    fn slice(self, range: Range<usize>) -> Self {
42        let rst = Self {
43            blocks: &self.blocks[range],
44            contents: self.contents,
45        };
46
47        rst
48    }
49
50    /// Attach a `Content` to this section. This will keep track of a stack up to
51    /// 4 `Content`s deep, cycling on overflow.
52    #[inline]
53    pub fn with<X>(self, content: &X) -> Section<'section, Next<'section, C, &X>>
54    where
55        X: Content + ?Sized,
56    {
57        let rst = Section {
58            blocks: self.blocks,
59            contents: self.contents.combine(content),
60        };
61
62        rst
63    }
64
65    /// The section without the last `Content` in the stack
66    #[inline]
67    pub fn without_last(self) -> Section<'section, C::Previous>
68    {
69        let rst = Section {
70            blocks: self.blocks,
71            contents: self.contents.crawl_back(),
72        };
73
74        rst
75    }
76
77    /// Render this section once to the provided `Encoder`.
78    pub fn render<E, IC: Content>(&self, encoder: &mut E, content: Option<&IC>) -> Result<(), E::Error>
79    where
80        E: Encoder,
81    {
82        let mut index = 0;
83
84        while let Some(block) = self.blocks.get(index) { // 消耗本次 render 所需的一层 block
85            index += 1;
86            encoder.write_unescaped(&block.html)?;
87
88            match block.tag {
89                Tag::Escaped => {
90                    if block.name == "$value" {
91                        if let Some(content) = content {
92                            content.render_escaped(encoder)?; 
93                        }
94                    } else {
95                        self.contents.render_field_escaped(block.hash, &block.name, encoder)?;
96                    }
97                }
98                Tag::Unescaped => {
99                    if block.name == "$value" {
100                        if let Some(content) = content {
101                            content.render_unescaped(encoder)?; 
102                        }
103                    } else {
104                        self.contents.render_field_unescaped(block.hash, &block.name, encoder)?;
105                    }
106                    
107                }
108                Tag::Section => {
109                    self.contents.render_field_section(
110                        block.hash, // block0.hash,block0.child = 2
111                        &block.name,  // block0.name
112                        self.slice(index..index + block.children as usize), // 消去本次 render_field_section 后剩下的 子blocks[1,2], block3 不是 block0 的子 block
113                        encoder,
114                    )?;
115                    index += block.children as usize;
116                }
117                Tag::Inverse => {
118                    self.contents.render_field_inverse(
119                        block.hash,
120                        &block.name,
121                        self.slice(index..index + block.children as usize),
122                        encoder,
123                    )?;
124                    index += block.children as usize;
125                }
126                Tag::NotNone => {
127                    let rst = self.contents.render_field_notnone_section(
128                        block.hash,
129                        &block.name,
130                        self.slice(index..index + block.children as usize),
131                        encoder,
132                    )?;
133
134                    if !rst {
135                        index += block.children as usize;
136                    }
137                    // index += block.children as usize;
138                }
139                _ => {}
140            }
141        }
142
143        Ok(())
144    }
145}