rustla/doctree/
mod.rs

1/*!
2This module defines the document tree and its nodes.
3The implementation is in the form of a zipper:
4the `DocTree` is a container for the metadata (such as hyperlinks) related to the parsing of the tree
5and store the tree in the form a a `TreeZipper` in its field `DocTree.tree`.
6The `TreeZipper` type is specified in the submodule `crate::parser::tree_zipper`.
7
8Copyright © 2020 Santtu Söderholm
9*/
10use std::collections::HashMap;
11
12mod larst_writer;
13mod node_categories;
14mod restructuredtext_transforms;
15mod tree_zipper;
16use tree_zipper::TreeZipper;
17pub mod tree_node;
18use tree_node::TreeNode;
19pub mod tree_node_types;
20use tree_node_types::TreeNodeType;
21pub mod directives;
22mod hyperref_data;
23use hyperref_data::{HyperrefData, ANON_REF_LABEL_PREFIX, ANON_REF_LABEL_SUFFIX};
24mod class_data;
25use class_data::ClassData;
26mod section_data;
27use section_data::SectionData;
28mod walkers;
29
30use crate::common::{
31    EnumDelims, EnumKind, FootnoteKind, HTMLAlignment, HorizontalAlignment, Length, MetricType,
32    NodeId, SectionLineStyle, TableColWidths, ToCBacklinks,
33};
34
35mod tests;
36
37/// A container for the document tree.
38/// In addition to holding ownership of the
39/// tree (stored in a zipper), also contains
40/// metadata about the tree.
41pub struct DocTree {
42
43    /// The canonicalized file path without the file suffix.
44    filename_stem: String,
45
46    /// The path to the folder the source file is stored in.
47    /// The object file will be stored in the same folder with a different suffix.
48    file_folder: String,
49
50    /// Holds the tree focused on a specific node.
51    tree: TreeZipper,
52
53    /// Keeps track of how many nodes have been added to the tree thus far
54    /// besides the root node, that gets an ID of `0`. Some nodes might differ
55    /// in their behaviour depending on their insertion order into the tree.
56    /// For example, a field list will be transformed into bibliographic data,
57    /// if it is the first non-(whitespace|comment) node in the tree.
58    node_count: NodeId,
59
60    /// The container for hyperref data related to the doctree.
61    hyperref_data: HyperrefData,
62
63    /// A container that holds on to the possibly generated HTML classes.
64    class_data: ClassData,
65
66    /// A container that keeps track of known section styles and section levels corresponding to them.
67    section_data: SectionData,
68}
69
70use std::path::PathBuf;
71
72/// Document tree container methods
73impl DocTree {
74
75    /// A `DocTree` constructor.
76    pub fn new(doc_name: PathBuf) -> Self {
77        let root_id: NodeId = 0;
78        let root_data = TreeNodeType::Document;
79        let root_node = TreeNode::new(root_data, root_id, None, None);
80
81        let file_stem: String = if let Some(path_os_str) = doc_name.file_stem() {
82            if let Some(path_str) = path_os_str.to_str() {
83                path_str.to_string()
84            } else {
85                panic!("Invalid unicode in file path. Computer says no...")
86            }
87        } else {
88            // eprintln!("No recognizable source file name to be found. Computer says no...");
89            String::new()
90        };
91
92        let file_folder = if let Some(parent) = doc_name.parent() {
93            if let Some(path_str) = parent.to_str() {
94                path_str.to_string()
95            } else {
96                panic!("Source folder path could not be converted to a string. Computer says no...")
97            }
98        } else {
99            // eprintln!("Source is not in any folder (even root). Computer says no...");
100            String::new()
101        };
102
103        DocTree {
104            filename_stem: file_stem,
105            file_folder: file_folder,
106            tree: TreeZipper::new(root_node, None, None),
107            node_count: root_id + 1,
108            hyperref_data: HyperrefData::new(),
109            class_data: ClassData::new(),
110            section_data: SectionData::new(),
111        }
112    }
113
114    /// Returns the value of the contnained node counter.
115    pub fn n_of_nodes(&self) -> NodeId {
116        self.node_count
117    }
118
119    /// Mainly for debugging purposes.
120    /// Prints the contaiend tree, focused on the current node.
121    pub fn print_tree(&self) {
122        eprintln!("The Document Tree\n=================");
123        eprintln!("{:#?}", self.tree)
124    }
125
126    /// Prints the currently focused on node.
127    fn print_node(&self) {
128        eprintln!("{:#?}", self.tree.shared_node())
129    }
130
131    /// Prints the id of the currently focused on node.
132    fn print_node_id(&self) {
133        eprintln!("{:#?}", self.tree.node_id())
134    }
135
136    /// Returns a copy of the current node count in the DocTree.
137    pub fn node_count(&self) -> NodeId {
138        self.node_count
139    }
140
141    /// mainly for debugging purposes
142    /// Prints out the internal targe labels stored in `self.hyperref_data` currently being worked on.
143    pub fn print_internal_labels(&self) {
144        eprintln!(
145            "{:#?}",
146            self.hyperref_data
147                .shared_accumulated_internal_target_label()
148        );
149    }
150
151    /// Focuses `self.tree` on its parent node if there is one.
152    pub fn focus_on_parent(mut self) -> Self {
153        self.tree = match self.tree.focus_on_parent() {
154            Ok(tree) => tree,
155            Err(tree) => {
156                eprintln!("INFO: Tried focusing on node parent but no parent found.\n");
157                tree
158            }
159        };
160
161        self
162    }
163
164    /// Creates a new node from given data, pushes it to the children of currently focused on node and focuses on the new node.
165    /// If this succeeds, also increments `self.node_count`.
166    /// Returns `Result::{Ok(self), Err(self)}`, depending on the success of this operation.
167    pub fn push_data_and_focus(mut self, mut node_data: TreeNodeType) -> Result<Self, Self> {
168        let target_labels = self.hyperref_actions(&mut node_data);
169        let classes = self.classes();
170        match self
171            .tree
172            .push_data_and_focus(node_data, self.node_count, target_labels, classes)
173        {
174            Ok(tree) => {
175                self.node_count += 1;
176                self.tree = tree;
177                Ok(self)
178            }
179            Err(tree) => {
180                self.tree = tree;
181                Err(self)
182            }
183        }
184    }
185
186    /// Creates a new node from given data and pushes it to the children of currently focused on node.
187    /// If this succeeds, also increments `self.node_count`.
188    /// Returns self in either `Ok` or an `Err`.
189    pub fn push_data(mut self, mut node_data: TreeNodeType) -> Result<Self, Self> {
190        let target_labels = self.hyperref_actions(&mut node_data);
191        let classes = self.classes();
192        match self
193            .tree
194            .push_data(node_data, self.node_count, target_labels, classes)
195        {
196            Ok(tree) => {
197                self.tree = tree;
198                self.node_count += 1;
199                Ok(self)
200            }
201            Err(tree) => {
202                self.tree = tree;
203                Err(self)
204            }
205        }
206    }
207
208    /// Pushes a new node to the children of the node currently focused on.
209    /// If the addition was successful, returns `Ok(())`, else returns the given node wrapped in an `Err`.
210    pub fn push_child(&mut self, mut node: TreeNode) -> Result<(), TreeNode> {
211
212        let incoming_target_labels = self.hyperref_actions(node.mut_data());
213        node.set_target_label(incoming_target_labels);
214        match self.tree.push_child(node) {
215            Ok(()) => {
216                self.node_count += 1;
217                Ok(())
218            }
219            Err(node) => Err(node),
220        }
221    }
222    /// Removes the last child of the  current node and returns in an `Option`.
223    pub fn pop_child(&mut self) -> Option<TreeNode> {
224        match self.tree.pop_child() {
225            Some(node) => Some(node),
226            None => None,
227        }
228    }
229
230    /// Performs any node specific hyperref label additions to the doctree based on given node data.
231    /// Returns an optional internal target label.
232    fn hyperref_actions(&mut self, node_data: &mut TreeNodeType) -> Option<Vec<String>> {
233
234        use crate::common::normalize_refname;
235
236        // Check if there is an incoming internal target label
237        let accumulated_target_label = self.hyperref_data.mut_accumulated_internal_target_label();
238        let mut target_labels: Vec<String> = if accumulated_target_label.is_empty() {
239            Vec::new()
240        } else {
241            match node_data {
242                TreeNodeType::EmptyLine | TreeNodeType::WhiteSpace { .. } => Vec::new(),
243                _ => {
244                    let labels = accumulated_target_label.drain(..).collect();
245                    accumulated_target_label.clear();
246                    labels
247                }
248            }
249        };
250
251        // Check for targetable or referential nodes. If one is encountered,
252        // add it to the known targets or references.
253        match node_data {
254            TreeNodeType::Citation { label, .. } => {
255                let normalized_refname = normalize_refname(label);
256                self.add_target(&normalized_refname, self.node_count);
257                target_labels.push(normalized_refname);
258            }
259            TreeNodeType::CitationReference { displayed_text, .. } => {
260                let normalized_refname = normalize_refname(displayed_text);
261                self.add_reference(&normalized_refname, self.node_count)
262            }
263            TreeNodeType::Footnote { target, label, kind, .. } => {
264                match kind {
265                    FootnoteKind::Manual => {
266                        let normalized_refname = normalize_refname(label);
267                        target_labels.push(normalized_refname);
268                    }
269                    FootnoteKind::AutoNumbered => {
270                        match self.new_autonumber_footnote_label() {
271                            Some(number) => {
272                                *target = number.clone();
273                                *label = number.clone();
274                                target_labels.push(number)
275                            }
276                            None => ()
277                        }
278                    }
279                    FootnoteKind::SimpleRefName => {
280                        match self.new_autonumber_footnote_label() {
281                            Some(number) => {
282                                *target = number.clone();
283                                *label = number.clone();
284                                target_labels.push(number)
285                            }
286                            None => ()
287                        }
288                    }
289                    FootnoteKind::AutoSymbol => {
290                        match self.new_symbolic_footnote_label() {
291                            Some(symbol) => {
292                                *target = symbol.clone();
293                                *label = symbol.clone();
294                                target_labels.push(symbol)
295                            }
296                            None => ()
297                        }
298                    }
299                };
300                for label in target_labels.iter() {
301                    self.add_target(label, self.node_count)
302                }
303                if let FootnoteKind::AutoSymbol = kind {
304                    self.increment_symbolic_footnotes();
305                }
306            }
307            TreeNodeType::FootnoteReference { displayed_text, target_label, kind } => {
308                match kind {
309                    FootnoteKind::Manual => target_labels.push(
310                        crate::common::normalize_refname(displayed_text)
311                    ),
312                    FootnoteKind::AutoNumbered => match self.new_autonumber_footnote_ref_label() {
313                        Some(label) => {
314                            *displayed_text = label.clone();
315                            *target_label = label.clone();
316                            target_labels.push(label)
317                        },
318                        None => return None
319                    },
320                    FootnoteKind::SimpleRefName => {
321                        match self.new_autonumber_footnote_ref_label() {
322                            Some(label) => {
323                                *target_label = label.clone();
324                                target_labels.push(label)
325                            },
326                            None => return None
327                        }
328                        target_labels.push(crate::common::normalize_refname(displayed_text));
329                    },
330                    FootnoteKind::AutoSymbol => match self.new_symbolic_footnote_label() {
331                        Some(label) => target_labels.push(label),
332                        None => return None
333                    }
334                };
335                for label in target_labels.iter() {
336                    self.add_reference(&label, self.node_count)
337                }
338            },
339            TreeNodeType::ExternalHyperlinkTarget { uri, target, .. } => {
340                let normalized_refname = normalize_refname(target);
341                target_labels.push(normalized_refname);
342                for label in target_labels.iter() {
343                    self.add_target(label,self.node_count);
344                }
345            }
346            TreeNodeType::IndirectHyperlinkTarget {
347                target,
348                indirect_target,
349                ..
350            } => {
351                let normalized_target_refname = normalize_refname(target);
352                let normalized_indirect_refname = normalize_refname(target);
353                for label in target_labels.iter() {
354                    self.add_target(
355                        label,
356                        self.node_count,
357                    );
358                }
359                self.add_reference(
360                    &normalize_refname(normalized_indirect_refname.as_str()),
361                    self.node_count,
362                );
363            }
364            TreeNodeType::Section {
365                title_text,
366                level,
367                line_style,
368            } => {
369                target_labels.push(normalize_refname(title_text));
370                for label in target_labels.iter() {
371                    self.add_target(label,self.node_count);
372                }
373                self.section_data.add_section_level(*line_style);
374                if *level > self.section_data.highest_encountered_section_level() {
375                    self.section_data.increment_encountered_section_number();
376                }
377            }
378            _ => for label in target_labels.iter() {
379                self.add_target(label,self.node_count);
380            },
381        };
382        if target_labels.is_empty() { None } else { Some(target_labels) }
383    }
384
385
386    /// Returns the stack of incoming classes, if there are any.
387    fn classes(&mut self) -> Option<Vec<String>> {
388        let classes = self.class_data.mut_classes();
389        if classes.is_empty() {
390            None
391        } else {
392            Some(classes.drain(..).collect())
393        }
394    }
395
396    /// Returns a shared reference to the current node .
397    pub fn shared_node(&self) -> &TreeNode {
398        self.tree.shared_node()
399    }
400
401    /// Returns a shared reference to the current node .
402    pub fn mut_node(&mut self) -> &mut TreeNode {
403        self.tree.mut_node()
404    }
405
406    /// Returns an optional shared reference to the current node's children, if the exist.
407    pub fn shared_children(&self) -> Option<&Vec<TreeNode>> {
408        if let Some(children) = self.tree.shared_children() {
409            Some(children)
410        } else {
411            None
412        }
413    }
414
415    /// Returns an optional mutable reference to the current node's children, if the exist.
416    pub fn mut_children(&mut self) -> Option<&mut Vec<TreeNode>> {
417        if let Some(children) = self.tree.mut_children() {
418            Some(children)
419        } else {
420            None
421        }
422    }
423
424    /// Retrieves a shared reference to the data of the
425    /// currently focused on node.
426    pub fn shared_node_data(&self) -> &TreeNodeType {
427        self.tree.shared_node().shared_data()
428    }
429
430    /// Retrieves a shared reference to the data of the
431    /// currently focused on node.
432    pub fn mut_node_data(&mut self) -> &mut TreeNodeType {
433        self.tree.mut_node().mut_data()
434    }
435
436    /// Retrieves a shared reference to the data of the given child of the current node.
437    pub fn get_child_data(&self, index: usize) -> &TreeNodeType {
438        if let Some(children) = self.tree.shared_node().shared_children() {
439            match children.get(index) {
440                Some(node) => node.shared_data(),
441                None => {
442                    eprintln!("Focused on node does not have as many children as is implied.\nComputer says no...\n");
443                    panic!()
444                }
445            }
446        } else {
447            panic!("Cannot retrieve shared child data from a node that cannot have children. Computer says no...")
448        }
449    }
450
451    pub fn n_of_children(&self) -> usize {
452        self.tree.n_of_children()
453    }
454
455    /// Retrieves a mutable reference to the data of the given child of the current node.
456    pub fn get_mut_child_data(&mut self, index: usize) -> &mut TreeNodeType {
457        if let Some(children) = self.tree.mut_node().mut_children() {
458            match children.get_mut(index) {
459                Some(node) => node.mut_data(),
460                None => {
461                    eprintln!("Focused on node does not have as many children as is implied.\nComputer says no...\n");
462                    panic!()
463                }
464            }
465        } else {
466            panic!("Cannot retrieve mutable child data from a node that cannot have children. Computer says no...")
467        }
468    }
469
470    /// Retrieves a shared reference to a given child.
471    pub fn shared_child(&self, index: usize) -> Option<&TreeNode> {
472        if let Some(children) = self.tree.shared_node().shared_children() {
473            match children.get(index) {
474                Some(node) => Some(node),
475                None => {
476                    eprintln!("Focused on node does not have as many children as is implied. Computer says no...");
477                    None
478                }
479            }
480        } else {
481            eprintln!(
482                "Cannot retrieve child from a node that cannot have children. Computer says no..."
483            );
484            None
485        }
486    }
487
488    /// Retrieves a shared reference to a given child.
489    pub fn mut_child(&mut self, index: usize) -> Option<&mut TreeNode> {
490        if let Some(children) = self.tree.mut_node().mut_children() {
491            match children.get_mut(index) {
492                Some(node) => Some(node),
493                None => {
494                    eprintln!("Focused on node does not have as many children as is implied. Computer says no...");
495                    None
496                }
497            }
498        } else {
499            eprintln!(
500                "Cannot retrieve child from a node that cannot have children. Computer says no..."
501            );
502            None
503        }
504    }
505
506    /// Retrieves the node data of a sibling of the currently focused-on node with the given index.
507    pub fn shared_sibling_data(&self, sibling_index: usize) -> Option<&TreeNodeType> {
508        if let Some(sibling_data) = self.tree.shared_sibling_data(sibling_index) {
509            Some(sibling_data)
510        } else {
511            eprintln!("Warning: No sibling with index {}...\n", sibling_index);
512            None
513        }
514    }
515
516    /// Retrieves the index of the current node with respect to its parent.
517    pub fn index_in_parent(&self) -> Option<usize> {
518        self.tree.index_in_parent()
519    }
520
521    /// Appends the nodes given in a given vector of nodes to the currently
522    /// focused on node in `self.tree`.
523    pub fn append_children(&mut self, nodes: &mut Children) {
524        let children = nodes.len() as NodeId; // No overflow checks...
525        self.tree.append_children(nodes);
526        self.node_count += children;
527    }
528
529    /// Checks whether the doctree already contains a hyperlink target with the given label.
530    pub fn has_target_label(&self, label_to_be_inspected_for: &str) -> bool {
531        self.hyperref_data
532            .shared_targets()
533            .contains_key(label_to_be_inspected_for)
534    }
535
536    /// Checks whether the doctree already contains a hyperlink reference with the given label.
537    pub fn has_reference_label(&self, label_to_be_inspected_for: &str) -> bool {
538        self.hyperref_data
539            .shared_references()
540            .contains_key(label_to_be_inspected_for)
541    }
542
543    /// Retrieves a copy of the node id currently focused on.
544    pub fn current_node_id(&self) -> NodeId {
545        self.tree.node_id()
546    }
547
548    /// Adds a given label to the known hyperref targets or updates the actual targe node id
549    /// if a label is already in the known labels.
550    fn add_target(&mut self, label: &String, id: NodeId) {
551        match self.hyperref_data.mut_targets().insert(label.clone(), id) {
552            Some(node_id) => {
553                eprintln!("Found an existing node with the target label \"{}\".\nReplacing duplicate node id value {} with {}...\n", label, node_id, id);
554            }
555            None => {}
556        };
557    }
558
559    /// Adds a given label to the known hyperref targets or updates the actual targe node id
560    /// if a label is already in the known labels.
561    fn add_reference(&mut self, label: &String, id: NodeId) {
562        match self
563            .hyperref_data
564            .mut_references()
565            .get_mut(label)
566        {
567            Some(node_ids) => {
568                node_ids.push(id);
569            },
570            None => {
571                self.hyperref_data.mut_references().insert(label.clone(), vec![id]);
572            }
573        };
574    }
575
576    /// Pushes a given label to the chain of detected internal target labels.
577    /// Once a non-internal target is encountered, this array of labels will be
578    /// made to point to the newly detected node and cleared.
579    pub fn push_to_internal_target_stack(&mut self, label: String) {
580        self.hyperref_data.add_internal_target_label(label);
581    }
582
583    /// Returns the number of symbolic footnotes that have been entered into the doctree.
584    pub fn n_of_symbolic_footnotes(&self) -> u32 {
585        self.hyperref_data.n_of_symbolic_footnotes()
586    }
587
588    /// Returns the number of symbolic footnote references that have been entered into the doctree.
589    pub fn n_of_symbolic_footnote_refs(&self) -> u32 {
590        self.hyperref_data.n_of_symbolic_footnote_refs()
591    }
592
593    /// Increments symbolic footnote counter of the doctree by 1.
594    pub fn increment_symbolic_footnotes(&mut self) {
595        self.hyperref_data.increment_symbolic_footnote_counter_by(1);
596    }
597
598    /// Increments symbolic footnote reference counter of the doctree by 1.
599    pub fn increment_symbolic_footnote_refs(&mut self) {
600        self.hyperref_data
601            .increment_symbolic_footnote_ref_counter_by(1);
602    }
603
604    /// Increases the counter for anonymous targets entered into the doctree thus far by one.
605    pub fn increment_anon_targets(&mut self) {
606        self.hyperref_data.increment_anonymous_target_counter_by(1);
607    }
608
609    /// Increases the counter for anonymous targets entered into the doctree thus far by one.
610    pub fn increment_anon_references(&mut self) {
611        self.hyperref_data
612            .increment_anonymous_target_ref_counter_by(1);
613    }
614
615    /// Increments the anon target counter and returns a copy of the result.
616    pub fn next_anon_target_n(&mut self) -> u32 {
617        self.increment_anon_targets();
618        self.hyperref_data.n_of_anon_targets()
619    }
620
621    /// Increments the anon reference counter and returns a copy of the result.
622    pub fn next_anon_reference_n(&mut self) -> u32 {
623        self.increment_anon_references();
624        self.hyperref_data.n_of_anon_target_refs()
625    }
626
627    /// Returns an allocated String representation of the next anonymous target label.
628    pub fn next_anon_target_label(&mut self) -> String {
629        format!(
630            "{}{}{}",
631            ANON_REF_LABEL_PREFIX,
632            self.next_anon_target_n(),
633            ANON_REF_LABEL_SUFFIX
634        )
635    }
636
637    /// Returns an allocated String representation of the next anonymous reference label.
638    pub fn next_anon_reference_label(&mut self) -> String {
639        format!(
640            "{}{}{}",
641            ANON_REF_LABEL_PREFIX,
642            self.next_anon_reference_n(),
643            ANON_REF_LABEL_SUFFIX
644        )
645    }
646
647    /// Returns a shared reference to `self.targets`.
648    pub fn shared_targets(&self) -> &HashMap<String, NodeId> {
649        self.hyperref_data.shared_targets()
650    }
651
652    /// Returns a mutable reference to `self.targets`.
653    pub fn mut_targets(&mut self) -> &mut HashMap<String, NodeId> {
654        self.hyperref_data.mut_targets()
655    }
656
657    /// Returns a shared reference to `self.references`.
658    pub fn shared_references(&self) -> &HashMap<String, Vec<NodeId>> {
659        self.hyperref_data.shared_references()
660    }
661
662    /// Returns a mutable reference to `self.references`.
663    pub fn mut_references(&mut self) -> &mut HashMap<String, Vec<NodeId>> {
664        self.hyperref_data.mut_references()
665    }
666
667    /// Generates a new section node data container by comparing the given `section_style` to known styles
668    /// and corresponding levels via `self.section_levels`. If a section of such style already exists, the level of the section
669    /// is simply set to the level matching it. If not, the maximum known level is plus 1
670    /// is assigned to the section data.
671
672    /// Note that this function does not yet modify known section data or hyperref targets.
673    /// This is donw only if pushing the node data to the tree succeeds, and is handled
674    /// by the related methods.
675    pub fn new_section_data(
676        &self,
677        title_text: &str,
678        section_style: SectionLineStyle,
679    ) -> TreeNodeType {
680        let section_level = self.section_data.line_style_section_level(&section_style);
681        TreeNodeType::Section {
682            level: section_level,
683            title_text: title_text.to_string(),
684            line_style: section_style,
685        }
686    }
687
688    /// Adds a new section to the doctree, also taking care of adding the section title
689    /// to the hyperref data of the tree, updating the section counter and mapping
690    /// the section type to the appropriate section level.
691    pub fn add_section(mut self, title_text: &str, section_style: SectionLineStyle) -> Self {
692        let section_data = self.new_section_data(title_text, section_style);
693
694        self = match self.push_data(section_data) {
695            Ok(tree) | Err(tree) => tree,
696        };
697        self
698    }
699
700    /// Walks up the tree to a given section level.
701    pub fn walk_to_parent_section_level(mut self, level: usize) -> Self {
702        self.tree = self.tree.walk_to_parent_section_level(level);
703        self
704    }
705
706    /// Returns an `Option`al shared reference to the parent node.
707    pub fn shared_parent_ref(&self) -> Option<&TreeZipper> {
708        self.tree.shared_parent_ref()
709    }
710
711    /// Returns a shared reference to the data of the current node.
712    pub fn shared_data(&self) -> &TreeNodeType {
713        self.tree.shared_data()
714    }
715
716    /// Returns an `Option`al shared reference to parent node data.
717    pub fn shared_parent_data(&self) -> Option<&TreeNodeType> {
718        if let Some(parent_ref) = self.shared_parent_ref() {
719            Some(parent_ref.shared_data())
720        } else {
721            None
722        }
723    }
724
725    /// Generates a new symbolic footnote label, wrapped in an `Option`.
726    /// Does not increment the symbolic footnote counter,
727    /// as at the point of calling this function we have no information about whether the
728    /// insertion of the respective node into the doctree succeeded.
729    pub fn new_symbolic_footnote_label (&self) -> Option<String> {
730            // Generate a label from crate::common::FOONOTE_SYMBOLS based on the number of autosymbol footnotes
731            // entered into the document thus far.
732
733            let n = self.n_of_symbolic_footnotes() as usize; // No overflow checks with as...
734
735            let n_of_symbols = crate::common::FOOTNOTE_SYMBOLS.len();
736
737            let passes = n / n_of_symbols;
738            let index = n % n_of_symbols;
739            let symbol: char = match crate::common::FOOTNOTE_SYMBOLS.get(index) {
740                Some(symb) => *symb,
741                None => {
742                    eprintln!("No footnote symbol with index {}!", index);
743                    return None;
744                }
745            };
746            let label: String = vec![symbol; passes + 1].iter().collect();
747            return Some(label)
748    }
749
750    /// Generates a new symbolic footnote reference label, wrapped in an `Option`.
751    /// Does not increment the symbolic footnote counter,
752    /// as at the point of calling this function we have no information about whether the
753    /// insertion of the respective node into the doctree succeeded.
754    pub fn new_symbolic_footnote_ref_label (&self) -> Option<String> {
755        // Generate a label from crate::common::FOONOTE_SYMBOLS based on the number of autosymbol footnotes
756        // entered into the document thus far.
757
758        let n = self.n_of_symbolic_footnote_refs() as usize; // No overflow checks with as...
759
760        let n_of_symbols = crate::common::FOOTNOTE_SYMBOLS.len();
761
762        let passes = n / n_of_symbols;
763        let index = n % n_of_symbols;
764        let symbol: char = match crate::common::FOOTNOTE_SYMBOLS.get(index) {
765            Some(symb) => *symb,
766            None => {
767                eprintln!("No footnote symbol with index {}!", index);
768                return None;
769            }
770        };
771        let label: String = vec![symbol; passes + 1].iter().collect();
772        return Some(label)
773    }
774
775    /// Generates a new footnote number based on existing footnote labels.
776    /// Again, does not modify the doctree, as knowledge about
777    /// node insertion success is nil at this point.
778    pub fn new_autonumber_footnote_label (&self) -> Option<String>{
779        for n in 1..=crate::common::EnumAsInt::MAX {
780            let n_str = n.to_string();
781            if self.has_target_label(n_str.as_str()) {
782                continue;
783            }
784            return Some(n_str);
785        }
786        eprintln!("All possible footnote numbers in use. Computer says no...");
787        return None;
788    }
789
790    /// Generates a new footnote reference number based on existing footnote reference labels.
791    /// Again, does not modify the doctree, as knowledge about
792    /// node insertion success is nil at this point.
793    pub fn new_autonumber_footnote_ref_label (&self) -> Option<String>{
794        for n in 1..=crate::common::EnumAsInt::MAX {
795            let n_str = n.to_string();
796            if self.has_reference_label(n_str.as_str()) {
797                continue;
798            }
799            return Some(n_str);
800        }
801        eprintln!("All possible footnote numbers in use. Computer says no...");
802        return None;
803    }
804}
805
806/// Shorthand for a vector of owned child nodes.
807/// Empty vector indicates no children.
808type Children = Vec<TreeNode>;