use std::collections::HashMap;
use std::env::current_dir;
use serde::Serialize;
use crate::errors::{AsciidocrError, AsgError};
use crate::parser::Parser;
use crate::scanner::Scanner;
use super::blocks::{Block, ParentBlock};
use super::inlines::Inline;
use super::nodes::{Header, Location, NodeTypes};
#[derive(Serialize, Debug)]
pub struct Asg {
pub name: String,
#[serde(rename = "type")]
pub node_type: NodeTypes,
#[serde(skip_serializing_if = "Option::is_none")]
pub attributes: Option<HashMap<String, String>>, #[serde(skip_serializing_if = "Option::is_none")]
pub header: Option<Header>,
#[serde(skip)]
document_id: String,
#[serde(skip)]
document_id_hash: HashMap<String, Vec<Inline>>,
pub blocks: Vec<Block>,
pub location: Vec<Location>,
}
impl Default for Asg {
fn default() -> Self {
Self::new()
}
}
impl Asg {
pub fn new() -> Self {
Asg {
name: "document".to_string(),
node_type: NodeTypes::Block,
attributes: None,
header: None,
document_id: "".to_string(),
document_id_hash: HashMap::new(),
blocks: vec![],
location: vec![Location::default()],
}
}
pub fn from_str(s: &str) -> Result<Self, AsciidocrError> {
Ok(Parser::new(current_dir()?).parse(Scanner::new(s))?)
}
pub fn consolidate(&mut self) {
self.consolidate_locations();
self.consolidate_xrefs();
}
pub fn add_header(&mut self, header: Header, doc_attributes: HashMap<String, String>) {
self.document_id = header.document_id();
self.header = Some(header);
self.attributes = Some(doc_attributes);
}
pub fn push_block(&mut self, mut block: Block) -> Result<(), AsgError> {
block.consolidate_locations();
self.document_id_hash.extend(block.id_hashes());
if block.is_section() {
if let Some(possible_section) = self.blocks.last_mut() {
if possible_section.takes_block_of_type(&block) {
possible_section.push_block(block)?;
} else {
self.blocks.push(block);
}
} else {
self.blocks.push(block);
}
} else {
self.blocks.push(block)
}
Ok(())
}
pub fn consolidate_locations(&mut self) {
if let Some(last_block) = self.blocks.last_mut() {
self.location = Location::reconcile(self.location.clone(), last_block.locations())
} else {
if let Some(header) = &self.header {
self.location = Location::reconcile(self.location.clone(), header.location.clone())
}
}
}
fn consolidate_xrefs(&mut self) {
for block in self.blocks.iter_mut() {
for inline in block.inlines_mut() {
inline.attempt_xref_standardization(&self.document_id_hash);
}
}
}
pub fn standardize_footnotes(&mut self) -> Result<(), AsgError> {
let mut footnote_defs: Vec<Block> = vec![];
for block in self.blocks.iter_mut() {
footnote_defs.extend(
block.extract_footnote_definitions(footnote_defs.len(), &self.document_id)?,
);
}
self.push_block(Block::ParentBlock(ParentBlock::new_footnotes_container(
footnote_defs,
)))
}
pub fn all_text(&self) -> String {
let mut graph_text = String::new();
for block in self.blocks.iter() {
graph_text.push_str(&block.block_text())
}
graph_text
}
pub fn word_count(&self) -> usize {
self.all_text().split_whitespace().count()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::graph::blocks::*;
use crate::graph::inlines::*;
#[test]
fn consolidate_footnotes() {
let mut footnote = Inline::InlineSpan(InlineSpan::new(
InlineSpanVariant::Footnote,
InlineSpanForm::Constrained,
vec![],
));
footnote.push_inline(Inline::InlineLiteral(InlineLiteral::new(
InlineLiteralName::Text,
"Foonote text".to_string(),
vec![],
)));
let some_leaf = Block::LeafBlock(LeafBlock::new(
LeafBlockName::Paragraph,
LeafBlockForm::Paragraph,
None,
vec![],
vec![footnote],
));
let mut graph = Asg::new();
graph.document_id = "test".into();
let _ = graph.push_block(some_leaf);
assert_eq!(graph.blocks.len(), 1);
let _ = graph.standardize_footnotes();
assert_eq!(graph.blocks.len(), 2);
let Some(Block::LeafBlock(leaf)) = graph.blocks.first() else {
panic!("Destroyed the block we were only supposed to modify")
};
let inlines = leaf.inlines();
let Some(Inline::InlineSpan(footnoteref)) = inlines.first() else {
panic!("Missing footnote ref in leaf block")
};
let Some(Inline::InlineRef(iref)) = footnoteref.inlines.first() else {
panic!("Missing footnote ref link")
};
assert_eq!(iref.target, "test_footnotedef_1");
}
}