nmd_core/dossier/document/chapter/
chapter_header.rs

1use getset::{Getters, MutGetters, Setters};
2use serde::Serialize;
3use crate::{codex::{modifier::{base_modifier::BaseModifier, standard_heading_modifier::StandardHeading, Modifier}, Codex}, load::{LoadConfiguration, LoadError}, load_block::{LoadBlock, LoadBlockContent}};
4use super::{chapter_tag::ChapterTag, heading::{Heading, HeadingLevel}};
5
6
7
8/// `ChapterHeading` represents heading of `Chapter`
9/// 
10/// It contains both `Heading` (title) and `Vec<ChapterTag>` (metadata)
11#[derive(Debug, Clone, Getters, MutGetters, Setters, Serialize)]
12pub struct ChapterHeader {
13
14    #[getset(get = "pub", get_mut = "pub", set = "pub")]
15    heading: Heading,
16
17    #[getset(get = "pub", get_mut = "pub", set = "pub")]
18    tags: Vec<ChapterTag>,
19}
20
21impl ChapterHeader {
22
23    pub fn new(heading: Heading, tags: Vec<ChapterTag>,) -> Self {
24        Self {
25            heading,
26            tags
27        }
28    }
29
30    /// Load headings and chapter tags from `&str`
31    pub fn load(content: &str, codex: &Codex, configuration: &LoadConfiguration) -> Result<Vec<LoadBlock>, LoadError> {
32
33        let mut headers: Vec<LoadBlock> = Vec::new();
34
35        for heading in StandardHeading::ordered() {     // TODO: include `StandardHeading::ordered()` in `Codex`
36
37            let heading_modifier = Into::<BaseModifier>::into(heading);
38
39            for m in heading_modifier.modifier_pattern_regex().find_iter(content) {
40
41                let matched_str = m.as_str().to_string();
42
43                let m_start = m.start();
44                let m_end = m.end();
45
46                log::debug!("header found (between {} and {}): {:?}", m_start, m_end, &matched_str);
47
48                if let Some((heading, tags)) = Self::parse_chapter_heading_and_tags_from_str(&matched_str, codex, configuration)? {
49
50                    headers.push(LoadBlock::new(
51                        m_start,
52                        m_end,
53                        LoadBlockContent::ChapterHeader(ChapterHeader::new(heading, tags))
54                    ));
55                }
56
57            };
58        }
59
60        log::debug!("found headers:\n{:#?}", headers);
61
62        Ok(headers)
63    }
64
65    /// Load the chapter heading and metadata from `&str`. This method returns a tuple with optional heading and a chapter tags vector.
66    fn parse_chapter_heading_and_tags_from_str(content: &str, _codex: &Codex, _configuration: &LoadConfiguration) -> Result<Option<(Heading, Vec<ChapterTag>)>, LoadError> {
67
68        log::debug!("parse headings and chapter tags from:\n{}", content);
69
70        for heading in StandardHeading::ordered() {         // TODO: insert in codex
71
72            let heading_modifier = Into::<BaseModifier>::into(heading.clone());
73
74            if !heading_modifier.modifier_pattern_regex().is_match(content) {
75                continue
76            }
77
78            if let Some(capture) = heading_modifier.modifier_pattern_regex().captures(content) {
79
80                match heading {
81
82                    StandardHeading::MinorHeading => {
83    
84                        let level = HeadingLevel::Minor;
85
86                        let title = capture.get(1).unwrap();    
87    
88                        let tags = ChapterTag::load_chapter_tags_from_str(&content[title.end()..])?;
89                    
90                        return Ok(Some((
91                            Heading::new(level, title.as_str().to_string()),
92                            tags
93                        )))
94                    },
95    
96                    StandardHeading::MajorHeading => {
97                        
98                        let level = HeadingLevel::Major;
99    
100                        let title = capture.get(1).unwrap();
101       
102    
103                        let tags = ChapterTag::load_chapter_tags_from_str(&content[title.end()..])?;
104                    
105                        return Ok(Some((
106                            Heading::new(level, title.as_str().to_string()),
107                            tags
108                        )))
109                    },
110    
111                    StandardHeading::SameHeading => {
112    
113                        
114                        let level = HeadingLevel::Same;
115                        
116                        let title = capture.get(1).unwrap();
117    
118    
119                        let tags = ChapterTag::load_chapter_tags_from_str(&content[title.end()..])?;
120                    
121                        return Ok(Some((
122                            Heading::new(level, title.as_str().to_string()),
123                            tags
124                        )))
125                    },
126    
127                    StandardHeading::HeadingGeneralExtendedVersion(_) => {
128                        let level: u32 = content.chars().take_while(|&c| c == '#').count() as u32;
129    
130                        let title = capture.get(1).unwrap();    
131    
132                        let tags = ChapterTag::load_chapter_tags_from_str(&content[title.end()..])?;
133                    
134                        return Ok(Some((
135                            Heading::new(HeadingLevel::Explicit(level), title.as_str().to_string()),
136                            tags
137                        )))
138                    },
139    
140                    StandardHeading::HeadingGeneralCompactVersion(_) => {
141                        let matched = heading_modifier.modifier_pattern_regex().captures(content).unwrap();
142    
143                        let level = HeadingLevel::Explicit(matched.get(1).unwrap().as_str().parse().unwrap());
144                        let title = capture.get(2).unwrap();    
145    
146                        let tags = ChapterTag::load_chapter_tags_from_str(&content[title.end()..])?;
147                    
148                        return Ok(Some((
149                            Heading::new(level, title.as_str().to_string()),
150                            tags
151                        )))
152                    },
153                }   
154            }
155
156        }
157        
158        Ok(None)
159    }
160
161}