docx_reader/documents/elements/
instr_toc.rs1use serde::Serialize;
2
3use crate::documents::*;
4
5#[derive(Serialize, Debug, Clone, PartialEq, Default)]
6pub struct StyleWithLevel(pub (String, usize));
7
8impl StyleWithLevel {
9 pub fn new(s: impl Into<String>, l: usize) -> Self {
10 Self((s.into(), l))
11 }
12}
13#[derive(Serialize, Debug, Clone, PartialEq, Default)]
15#[serde(rename_all = "camelCase")]
16pub struct InstrToC {
17 #[serde(skip_serializing_if = "Option::is_none")]
19 pub heading_styles_range: Option<(usize, usize)>,
20 #[serde(skip_serializing_if = "Option::is_none")]
24 pub tc_field_level_range: Option<(usize, usize)>,
25 #[serde(skip_serializing_if = "Option::is_none")]
29 pub omit_page_numbers_level_range: Option<(usize, usize)>,
30 #[serde(skip_serializing_if = "Option::is_none")]
32 pub entry_bookmark_name: Option<String>,
33 pub styles_with_levels: Vec<StyleWithLevel>,
37 #[serde(skip_serializing_if = "Option::is_none")]
40 pub entry_and_page_number_separator: Option<String>,
41 #[serde(skip_serializing_if = "Option::is_none")]
43 pub sequence_and_page_numbers_separator: Option<String>,
44 pub caption_label: Option<String>,
46 #[serde(skip_serializing_if = "Option::is_none")]
48 pub caption_label_including_numbers: Option<String>,
49 #[serde(skip_serializing_if = "Option::is_none")]
51 pub seq_field_identifier_for_prefix: Option<String>,
52 #[serde(skip_serializing_if = "Option::is_none")]
54 pub tc_field_identifier: Option<String>,
55 pub hyperlink: bool,
57 pub preserve_tab: bool,
59 pub preserve_new_line: bool,
61 pub use_applied_paragraph_line_level: bool,
63 pub hide_tab_and_page_numbers_in_webview: bool,
65}
66
67impl InstrToC {
68 pub fn new() -> Self {
69 Self::default()
70 }
71
72 pub fn with_instr_text(s: &str) -> Self {
73 Self::from_str(s).expect("should convert to InstrToC")
74 }
75
76 pub fn heading_styles_range(mut self, start: usize, end: usize) -> Self {
77 self.heading_styles_range = Some((start, end));
78 self
79 }
80
81 pub fn tc_field_level_range(mut self, start: usize, end: usize) -> Self {
82 self.tc_field_level_range = Some((start, end));
83 self
84 }
85
86 pub fn tc_field_identifier(mut self, t: impl Into<String>) -> Self {
87 self.tc_field_identifier = Some(t.into());
88 self
89 }
90
91 pub fn omit_page_numbers_level_range(mut self, start: usize, end: usize) -> Self {
92 self.omit_page_numbers_level_range = Some((start, end));
93 self
94 }
95
96 pub fn entry_and_page_number_separator(mut self, t: impl Into<String>) -> Self {
97 self.entry_and_page_number_separator = Some(t.into());
98 self
99 }
100
101 pub fn entry_bookmark_name(mut self, t: impl Into<String>) -> Self {
102 self.entry_bookmark_name = Some(t.into());
103 self
104 }
105
106 pub fn caption_label(mut self, t: impl Into<String>) -> Self {
107 self.caption_label = Some(t.into());
108 self
109 }
110
111 pub fn caption_label_including_numbers(mut self, t: impl Into<String>) -> Self {
112 self.caption_label_including_numbers = Some(t.into());
113 self
114 }
115
116 pub fn sequence_and_page_numbers_separator(mut self, t: impl Into<String>) -> Self {
117 self.sequence_and_page_numbers_separator = Some(t.into());
118 self
119 }
120
121 pub fn seq_field_identifier_for_prefix(mut self, t: impl Into<String>) -> Self {
122 self.seq_field_identifier_for_prefix = Some(t.into());
123 self
124 }
125
126 pub fn hyperlink(mut self) -> Self {
127 self.hyperlink = true;
128 self
129 }
130
131 pub fn preserve_tab(mut self) -> Self {
132 self.preserve_tab = true;
133 self
134 }
135
136 pub fn preserve_new_line(mut self) -> Self {
137 self.preserve_new_line = true;
138 self
139 }
140
141 pub fn use_applied_paragraph_line_level(mut self) -> Self {
142 self.use_applied_paragraph_line_level = true;
143 self
144 }
145
146 pub fn hide_tab_and_page_numbers_in_webview(mut self) -> Self {
147 self.hide_tab_and_page_numbers_in_webview = true;
148 self
149 }
150
151 pub fn add_style_with_level(mut self, s: StyleWithLevel) -> Self {
152 self.styles_with_levels.push(s);
153 self
154 }
155}
156
157fn parse_level_range(i: &str) -> Option<(usize, usize)> {
158 let r = i.replace(""", "").replace("\"", "");
159 let r: Vec<&str> = r.split('-').collect();
160 if let Some(s) = r.get(0) {
161 if let Ok(s) = usize::from_str(s) {
162 if let Some(e) = r.get(1) {
163 if let Ok(e) = usize::from_str(e) {
164 return Some((s, e));
165 }
166 }
167 }
168 }
169 None
170}
171
172impl std::str::FromStr for InstrToC {
173 type Err = ();
174
175 fn from_str(instr: &str) -> Result<Self, Self::Err> {
176 let mut s = instr.split(' ');
177 let mut toc = InstrToC::new();
178 loop {
179 if let Some(i) = s.next() {
180 match i {
181 "\\a" => {
182 if let Some(r) = s.next() {
183 let r = r.replace(""", "").replace("\"", "");
184 toc = toc.caption_label(r);
185 }
186 }
187 "\\b" => {
188 if let Some(r) = s.next() {
189 let r = r.replace(""", "").replace("\"", "");
190 toc = toc.entry_bookmark_name(r);
191 }
192 }
193 "\\c" => {
194 if let Some(r) = s.next() {
195 let r = r.replace(""", "").replace("\"", "");
196 toc = toc.caption_label_including_numbers(r);
197 }
198 }
199 "\\d" => {
200 if let Some(r) = s.next() {
201 let r = r.replace(""", "").replace("\"", "");
202 toc = toc.sequence_and_page_numbers_separator(r);
203 }
204 }
205 "\\f" => {
206 if let Some(r) = s.next() {
207 let r = r.replace(""", "").replace("\"", "");
208 toc = toc.tc_field_identifier(r);
209 }
210 }
211 "\\h" => toc = toc.hyperlink(),
212 "\\l" => {
213 if let Some(r) = s.next() {
214 if let Some((s, e)) = parse_level_range(r) {
215 toc = toc.tc_field_level_range(s, e);
216 }
217 }
218 }
219 "\\n" => {
220 if let Some(r) = s.next() {
221 if let Some((s, e)) = parse_level_range(r) {
222 toc = toc.omit_page_numbers_level_range(s, e);
223 }
224 }
225 }
226 "\\o" => {
227 if let Some(r) = s.next() {
228 if let Some((s, e)) = parse_level_range(r) {
229 toc = toc.heading_styles_range(s, e);
230 }
231 }
232 }
233 "\\p" => {
234 if let Some(r) = s.next() {
235 let r = r.replace(""", "").replace("\"", "");
236 toc = toc.entry_and_page_number_separator(r);
237 }
238 }
239 "\\s" => {
240 if let Some(r) = s.next() {
241 let r = r.replace(""", "").replace("\"", "");
242 toc = toc.seq_field_identifier_for_prefix(r);
243 }
244 }
245 "\\t" => {
246 if let Some(r) = s.next() {
247 let r = r.replace(""", "").replace("\"", "");
248 let mut r = r.split(',');
249 loop {
250 if let Some(style) = r.next() {
251 if let Some(level) = r.next() {
252 if let Ok(level) = usize::from_str(level) {
253 toc = toc.add_style_with_level(StyleWithLevel((
254 style.to_string(),
255 level,
256 )));
257 continue;
258 }
259 }
260 }
261 break;
262 }
263 }
264 }
265 "\\u" => toc = toc.use_applied_paragraph_line_level(),
266 "\\w" => toc = toc.preserve_tab(),
267 "\\x" => toc = toc.preserve_new_line(),
268 "\\z" => toc = toc.hide_tab_and_page_numbers_in_webview(),
269 _ => {}
270 }
271 } else {
272 return Ok(toc);
273 }
274 }
275 }
276}