use std::collections::HashMap;
mod larst_writer;
mod node_categories;
mod restructuredtext_transforms;
mod tree_zipper;
use tree_zipper::TreeZipper;
pub mod tree_node;
use tree_node::TreeNode;
pub mod tree_node_types;
use tree_node_types::TreeNodeType;
pub mod directives;
mod hyperref_data;
use hyperref_data::{HyperrefData, ANON_REF_LABEL_PREFIX, ANON_REF_LABEL_SUFFIX};
mod class_data;
use class_data::ClassData;
mod section_data;
use section_data::SectionData;
mod walkers;
use crate::common::{
EnumDelims, EnumKind, FootnoteKind, HTMLAlignment, HorizontalAlignment, Length, MetricType,
NodeId, SectionLineStyle, TableColWidths, ToCBacklinks,
};
mod tests;
pub struct DocTree {
filename_stem: String,
file_folder: String,
tree: TreeZipper,
node_count: NodeId,
hyperref_data: HyperrefData,
class_data: ClassData,
section_data: SectionData,
}
use std::path::PathBuf;
impl DocTree {
pub fn new(doc_name: PathBuf) -> Self {
let root_id: NodeId = 0;
let root_data = TreeNodeType::Document;
let root_node = TreeNode::new(root_data, root_id, None, None);
let file_stem: String = if let Some(path_os_str) = doc_name.file_stem() {
if let Some(path_str) = path_os_str.to_str() {
path_str.to_string()
} else {
panic!("Invalid unicode in file path. Computer says no...")
}
} else {
String::new()
};
let file_folder = if let Some(parent) = doc_name.parent() {
if let Some(path_str) = parent.to_str() {
path_str.to_string()
} else {
panic!("Source folder path could not be converted to a string. Computer says no...")
}
} else {
String::new()
};
DocTree {
filename_stem: file_stem,
file_folder: file_folder,
tree: TreeZipper::new(root_node, None, None),
node_count: root_id + 1,
hyperref_data: HyperrefData::new(),
class_data: ClassData::new(),
section_data: SectionData::new(),
}
}
pub fn n_of_nodes(&self) -> NodeId {
self.node_count
}
pub fn print_tree(&self) {
eprintln!("The Document Tree\n=================");
eprintln!("{:#?}", self.tree)
}
fn print_node(&self) {
eprintln!("{:#?}", self.tree.shared_node())
}
fn print_node_id(&self) {
eprintln!("{:#?}", self.tree.node_id())
}
pub fn node_count(&self) -> NodeId {
self.node_count
}
pub fn print_internal_labels(&self) {
eprintln!(
"{:#?}",
self.hyperref_data
.shared_accumulated_internal_target_label()
);
}
pub fn focus_on_parent(mut self) -> Self {
self.tree = match self.tree.focus_on_parent() {
Ok(tree) => tree,
Err(tree) => {
eprintln!("INFO: Tried focusing on node parent but no parent found.\n");
tree
}
};
self
}
pub fn push_data_and_focus(mut self, mut node_data: TreeNodeType) -> Result<Self, Self> {
let target_labels = self.hyperref_actions(&mut node_data);
let classes = self.classes();
match self
.tree
.push_data_and_focus(node_data, self.node_count, target_labels, classes)
{
Ok(tree) => {
self.node_count += 1;
self.tree = tree;
Ok(self)
}
Err(tree) => {
self.tree = tree;
Err(self)
}
}
}
pub fn push_data(mut self, mut node_data: TreeNodeType) -> Result<Self, Self> {
let target_labels = self.hyperref_actions(&mut node_data);
let classes = self.classes();
match self
.tree
.push_data(node_data, self.node_count, target_labels, classes)
{
Ok(tree) => {
self.tree = tree;
self.node_count += 1;
Ok(self)
}
Err(tree) => {
self.tree = tree;
Err(self)
}
}
}
pub fn push_child(&mut self, mut node: TreeNode) -> Result<(), TreeNode> {
let incoming_target_labels = self.hyperref_actions(node.mut_data());
node.set_target_label(incoming_target_labels);
match self.tree.push_child(node) {
Ok(()) => {
self.node_count += 1;
Ok(())
}
Err(node) => Err(node),
}
}
pub fn pop_child(&mut self) -> Option<TreeNode> {
match self.tree.pop_child() {
Some(node) => Some(node),
None => None,
}
}
fn hyperref_actions(&mut self, node_data: &mut TreeNodeType) -> Option<Vec<String>> {
use crate::common::normalize_refname;
let accumulated_target_label = self.hyperref_data.mut_accumulated_internal_target_label();
let mut target_labels: Vec<String> = if accumulated_target_label.is_empty() {
Vec::new()
} else {
match node_data {
TreeNodeType::EmptyLine | TreeNodeType::WhiteSpace { .. } => Vec::new(),
_ => {
let labels = accumulated_target_label.drain(..).collect();
accumulated_target_label.clear();
labels
}
}
};
match node_data {
TreeNodeType::Citation { label, .. } => {
let normalized_refname = normalize_refname(label);
self.add_target(&normalized_refname, self.node_count);
target_labels.push(normalized_refname);
}
TreeNodeType::CitationReference { displayed_text, .. } => {
let normalized_refname = normalize_refname(displayed_text);
self.add_reference(&normalized_refname, self.node_count)
}
TreeNodeType::Footnote { target, label, kind, .. } => {
match kind {
FootnoteKind::Manual => {
let normalized_refname = normalize_refname(label);
target_labels.push(normalized_refname);
}
FootnoteKind::AutoNumbered => {
match self.new_autonumber_footnote_label() {
Some(number) => {
*target = number.clone();
*label = number.clone();
target_labels.push(number)
}
None => ()
}
}
FootnoteKind::SimpleRefName => {
match self.new_autonumber_footnote_label() {
Some(number) => {
*target = number.clone();
*label = number.clone();
target_labels.push(number)
}
None => ()
}
}
FootnoteKind::AutoSymbol => {
match self.new_symbolic_footnote_label() {
Some(symbol) => {
*target = symbol.clone();
*label = symbol.clone();
target_labels.push(symbol)
}
None => ()
}
}
};
for label in target_labels.iter() {
self.add_target(label, self.node_count)
}
if let FootnoteKind::AutoSymbol = kind {
self.increment_symbolic_footnotes();
}
}
TreeNodeType::FootnoteReference { displayed_text, target_label, kind } => {
match kind {
FootnoteKind::Manual => target_labels.push(
crate::common::normalize_refname(displayed_text)
),
FootnoteKind::AutoNumbered => match self.new_autonumber_footnote_ref_label() {
Some(label) => {
*displayed_text = label.clone();
*target_label = label.clone();
target_labels.push(label)
},
None => return None
},
FootnoteKind::SimpleRefName => {
match self.new_autonumber_footnote_ref_label() {
Some(label) => {
*target_label = label.clone();
target_labels.push(label)
},
None => return None
}
target_labels.push(crate::common::normalize_refname(displayed_text));
},
FootnoteKind::AutoSymbol => match self.new_symbolic_footnote_label() {
Some(label) => target_labels.push(label),
None => return None
}
};
for label in target_labels.iter() {
self.add_reference(&label, self.node_count)
}
},
TreeNodeType::ExternalHyperlinkTarget { uri, target, .. } => {
let normalized_refname = normalize_refname(target);
target_labels.push(normalized_refname);
for label in target_labels.iter() {
self.add_target(label,self.node_count);
}
}
TreeNodeType::IndirectHyperlinkTarget {
target,
indirect_target,
..
} => {
let normalized_target_refname = normalize_refname(target);
let normalized_indirect_refname = normalize_refname(target);
for label in target_labels.iter() {
self.add_target(
label,
self.node_count,
);
}
self.add_reference(
&normalize_refname(normalized_indirect_refname.as_str()),
self.node_count,
);
}
TreeNodeType::Section {
title_text,
level,
line_style,
} => {
target_labels.push(normalize_refname(title_text));
for label in target_labels.iter() {
self.add_target(label,self.node_count);
}
self.section_data.add_section_level(*line_style);
if *level > self.section_data.highest_encountered_section_level() {
self.section_data.increment_encountered_section_number();
}
}
_ => for label in target_labels.iter() {
self.add_target(label,self.node_count);
},
};
if target_labels.is_empty() { None } else { Some(target_labels) }
}
fn classes(&mut self) -> Option<Vec<String>> {
let classes = self.class_data.mut_classes();
if classes.is_empty() {
None
} else {
Some(classes.drain(..).collect())
}
}
pub fn shared_node(&self) -> &TreeNode {
self.tree.shared_node()
}
pub fn mut_node(&mut self) -> &mut TreeNode {
self.tree.mut_node()
}
pub fn shared_children(&self) -> Option<&Vec<TreeNode>> {
if let Some(children) = self.tree.shared_children() {
Some(children)
} else {
None
}
}
pub fn mut_children(&mut self) -> Option<&mut Vec<TreeNode>> {
if let Some(children) = self.tree.mut_children() {
Some(children)
} else {
None
}
}
pub fn shared_node_data(&self) -> &TreeNodeType {
self.tree.shared_node().shared_data()
}
pub fn mut_node_data(&mut self) -> &mut TreeNodeType {
self.tree.mut_node().mut_data()
}
pub fn get_child_data(&self, index: usize) -> &TreeNodeType {
if let Some(children) = self.tree.shared_node().shared_children() {
match children.get(index) {
Some(node) => node.shared_data(),
None => {
eprintln!("Focused on node does not have as many children as is implied.\nComputer says no...\n");
panic!()
}
}
} else {
panic!("Cannot retrieve shared child data from a node that cannot have children. Computer says no...")
}
}
pub fn n_of_children(&self) -> usize {
self.tree.n_of_children()
}
pub fn get_mut_child_data(&mut self, index: usize) -> &mut TreeNodeType {
if let Some(children) = self.tree.mut_node().mut_children() {
match children.get_mut(index) {
Some(node) => node.mut_data(),
None => {
eprintln!("Focused on node does not have as many children as is implied.\nComputer says no...\n");
panic!()
}
}
} else {
panic!("Cannot retrieve mutable child data from a node that cannot have children. Computer says no...")
}
}
pub fn shared_child(&self, index: usize) -> Option<&TreeNode> {
if let Some(children) = self.tree.shared_node().shared_children() {
match children.get(index) {
Some(node) => Some(node),
None => {
eprintln!("Focused on node does not have as many children as is implied. Computer says no...");
None
}
}
} else {
eprintln!(
"Cannot retrieve child from a node that cannot have children. Computer says no..."
);
None
}
}
pub fn mut_child(&mut self, index: usize) -> Option<&mut TreeNode> {
if let Some(children) = self.tree.mut_node().mut_children() {
match children.get_mut(index) {
Some(node) => Some(node),
None => {
eprintln!("Focused on node does not have as many children as is implied. Computer says no...");
None
}
}
} else {
eprintln!(
"Cannot retrieve child from a node that cannot have children. Computer says no..."
);
None
}
}
pub fn shared_sibling_data(&self, sibling_index: usize) -> Option<&TreeNodeType> {
if let Some(sibling_data) = self.tree.shared_sibling_data(sibling_index) {
Some(sibling_data)
} else {
eprintln!("Warning: No sibling with index {}...\n", sibling_index);
None
}
}
pub fn index_in_parent(&self) -> Option<usize> {
self.tree.index_in_parent()
}
pub fn append_children(&mut self, nodes: &mut Children) {
let children = nodes.len() as NodeId;
self.tree.append_children(nodes);
self.node_count += children;
}
pub fn has_target_label(&self, label_to_be_inspected_for: &str) -> bool {
self.hyperref_data
.shared_targets()
.contains_key(label_to_be_inspected_for)
}
pub fn has_reference_label(&self, label_to_be_inspected_for: &str) -> bool {
self.hyperref_data
.shared_references()
.contains_key(label_to_be_inspected_for)
}
pub fn current_node_id(&self) -> NodeId {
self.tree.node_id()
}
fn add_target(&mut self, label: &String, id: NodeId) {
match self.hyperref_data.mut_targets().insert(label.clone(), id) {
Some(node_id) => {
eprintln!("Found an existing node with the target label \"{}\".\nReplacing duplicate node id value {} with {}...\n", label, node_id, id);
}
None => {}
};
}
fn add_reference(&mut self, label: &String, id: NodeId) {
match self
.hyperref_data
.mut_references()
.get_mut(label)
{
Some(node_ids) => {
node_ids.push(id);
},
None => {
self.hyperref_data.mut_references().insert(label.clone(), vec![id]);
}
};
}
pub fn push_to_internal_target_stack(&mut self, label: String) {
self.hyperref_data.add_internal_target_label(label);
}
pub fn n_of_symbolic_footnotes(&self) -> u32 {
self.hyperref_data.n_of_symbolic_footnotes()
}
pub fn n_of_symbolic_footnote_refs(&self) -> u32 {
self.hyperref_data.n_of_symbolic_footnote_refs()
}
pub fn increment_symbolic_footnotes(&mut self) {
self.hyperref_data.increment_symbolic_footnote_counter_by(1);
}
pub fn increment_symbolic_footnote_refs(&mut self) {
self.hyperref_data
.increment_symbolic_footnote_ref_counter_by(1);
}
pub fn increment_anon_targets(&mut self) {
self.hyperref_data.increment_anonymous_target_counter_by(1);
}
pub fn increment_anon_references(&mut self) {
self.hyperref_data
.increment_anonymous_target_ref_counter_by(1);
}
pub fn next_anon_target_n(&mut self) -> u32 {
self.increment_anon_targets();
self.hyperref_data.n_of_anon_targets()
}
pub fn next_anon_reference_n(&mut self) -> u32 {
self.increment_anon_references();
self.hyperref_data.n_of_anon_target_refs()
}
pub fn next_anon_target_label(&mut self) -> String {
format!(
"{}{}{}",
ANON_REF_LABEL_PREFIX,
self.next_anon_target_n(),
ANON_REF_LABEL_SUFFIX
)
}
pub fn next_anon_reference_label(&mut self) -> String {
format!(
"{}{}{}",
ANON_REF_LABEL_PREFIX,
self.next_anon_reference_n(),
ANON_REF_LABEL_SUFFIX
)
}
pub fn shared_targets(&self) -> &HashMap<String, NodeId> {
self.hyperref_data.shared_targets()
}
pub fn mut_targets(&mut self) -> &mut HashMap<String, NodeId> {
self.hyperref_data.mut_targets()
}
pub fn shared_references(&self) -> &HashMap<String, Vec<NodeId>> {
self.hyperref_data.shared_references()
}
pub fn mut_references(&mut self) -> &mut HashMap<String, Vec<NodeId>> {
self.hyperref_data.mut_references()
}
pub fn new_section_data(
&self,
title_text: &str,
section_style: SectionLineStyle,
) -> TreeNodeType {
let section_level = self.section_data.line_style_section_level(§ion_style);
TreeNodeType::Section {
level: section_level,
title_text: title_text.to_string(),
line_style: section_style,
}
}
pub fn add_section(mut self, title_text: &str, section_style: SectionLineStyle) -> Self {
let section_data = self.new_section_data(title_text, section_style);
self = match self.push_data(section_data) {
Ok(tree) | Err(tree) => tree,
};
self
}
pub fn walk_to_parent_section_level(mut self, level: usize) -> Self {
self.tree = self.tree.walk_to_parent_section_level(level);
self
}
pub fn shared_parent_ref(&self) -> Option<&TreeZipper> {
self.tree.shared_parent_ref()
}
pub fn shared_data(&self) -> &TreeNodeType {
self.tree.shared_data()
}
pub fn shared_parent_data(&self) -> Option<&TreeNodeType> {
if let Some(parent_ref) = self.shared_parent_ref() {
Some(parent_ref.shared_data())
} else {
None
}
}
pub fn new_symbolic_footnote_label (&self) -> Option<String> {
let n = self.n_of_symbolic_footnotes() as usize;
let n_of_symbols = crate::common::FOOTNOTE_SYMBOLS.len();
let passes = n / n_of_symbols;
let index = n % n_of_symbols;
let symbol: char = match crate::common::FOOTNOTE_SYMBOLS.get(index) {
Some(symb) => *symb,
None => {
eprintln!("No footnote symbol with index {}!", index);
return None;
}
};
let label: String = vec![symbol; passes + 1].iter().collect();
return Some(label)
}
pub fn new_symbolic_footnote_ref_label (&self) -> Option<String> {
let n = self.n_of_symbolic_footnote_refs() as usize;
let n_of_symbols = crate::common::FOOTNOTE_SYMBOLS.len();
let passes = n / n_of_symbols;
let index = n % n_of_symbols;
let symbol: char = match crate::common::FOOTNOTE_SYMBOLS.get(index) {
Some(symb) => *symb,
None => {
eprintln!("No footnote symbol with index {}!", index);
return None;
}
};
let label: String = vec![symbol; passes + 1].iter().collect();
return Some(label)
}
pub fn new_autonumber_footnote_label (&self) -> Option<String>{
for n in 1..=crate::common::EnumAsInt::MAX {
let n_str = n.to_string();
if self.has_target_label(n_str.as_str()) {
continue;
}
return Some(n_str);
}
eprintln!("All possible footnote numbers in use. Computer says no...");
return None;
}
pub fn new_autonumber_footnote_ref_label (&self) -> Option<String>{
for n in 1..=crate::common::EnumAsInt::MAX {
let n_str = n.to_string();
if self.has_reference_label(n_str.as_str()) {
continue;
}
return Some(n_str);
}
eprintln!("All possible footnote numbers in use. Computer says no...");
return None;
}
}
type Children = Vec<TreeNode>;