natter/src/intermediate/registry.rs

209 lines
7.1 KiB
Rust
Raw Normal View History

2023-10-29 18:14:10 +00:00
use crate::error::CustomError;
2023-10-29 16:31:48 +00:00
use organic::types::Element;
2023-10-29 16:06:40 +00:00
use organic::types::Object;
2023-10-30 01:50:51 +00:00
use std::collections::HashMap;
2023-10-29 16:06:40 +00:00
2023-10-29 17:51:32 +00:00
use super::ast_node::IAstNode;
2023-10-30 01:33:43 +00:00
use super::ast_node::IntoIAstNode;
use super::IObject;
use super::IParagraph;
use super::IntermediateContext;
2023-10-29 17:51:32 +00:00
type IdCounter = u16;
#[derive(Debug)]
pub(crate) struct Registry<'orig, 'parse> {
id_counter: IdCounter,
2023-10-27 18:54:54 +00:00
targets: HashMap<&'parse str, String>,
footnote_ids: Vec<(Option<&'parse str>, Vec<IAstNode>)>,
2023-10-30 02:27:47 +00:00
footnote_reference_counts: HashMap<&'parse str, usize>,
2023-10-30 01:50:51 +00:00
on_deck_footnote_ids: HashMap<&'parse str, &'orig Vec<Element<'parse>>>,
}
impl<'orig, 'parse> Registry<'orig, 'parse> {
pub(crate) fn new() -> Registry<'orig, 'parse> {
Registry {
id_counter: 0,
targets: HashMap::new(),
2023-10-29 16:06:40 +00:00
footnote_ids: Vec::new(),
2023-10-30 02:27:47 +00:00
footnote_reference_counts: HashMap::new(),
2023-10-30 01:50:51 +00:00
on_deck_footnote_ids: HashMap::new(),
}
}
pub(crate) fn get_target<'reg>(&'reg mut self, body: &'parse str) -> &'reg String {
self.targets.entry(body).or_insert_with(|| {
self.id_counter += 1;
format!("target_{}", self.id_counter)
})
}
2023-10-29 16:06:40 +00:00
2023-10-29 19:36:15 +00:00
pub(crate) fn get_footnote_ids(&self) -> impl Iterator<Item = (usize, &Vec<IAstNode>)> {
self.footnote_ids
.iter()
.map(|(_label, definition)| definition)
.enumerate()
2023-10-29 19:36:15 +00:00
}
}
2023-10-29 19:36:15 +00:00
/// Get a 0-indexed ID for a footnote.
///
/// This needs to be incremented to be 1-indexed for render.
pub(crate) async fn get_footnote_reference_id<'orig, 'parse>(
intermediate_context: IntermediateContext<'orig, 'parse>,
label: Option<&'parse str>,
2023-12-23 11:38:23 +00:00
definition: &'orig [Object<'parse>],
2023-10-30 02:27:47 +00:00
) -> Result<(usize, usize), CustomError> {
2023-12-23 11:38:23 +00:00
if label.is_none() {
// If it has no label then it must always get a new ID.
let contents = convert_reference_contents(intermediate_context.clone(), definition).await?;
let pos = {
let mut registry = intermediate_context.registry.lock().unwrap();
registry.footnote_ids.push((None, contents));
registry.footnote_ids.len() - 1
};
2023-10-30 02:27:47 +00:00
return Ok((pos, 0));
}
2023-10-29 16:06:40 +00:00
2023-10-30 02:27:47 +00:00
let reference_count = if let Some(label) = label {
promote_footnote_definition(intermediate_context.clone(), label).await?;
let mut registry = intermediate_context.registry.lock().unwrap();
2023-10-30 02:27:47 +00:00
let reference_count = registry
.footnote_reference_counts
.entry(label)
.and_modify(|count| *count += 1)
.or_insert(0);
*reference_count
} else {
0
};
2023-10-30 02:01:42 +00:00
let existing_index = intermediate_context
.registry
.lock()
.unwrap()
.footnote_ids
.iter()
.position(|(id, _definition)| *id == label);
if let Some(existing_id) = existing_index {
if !definition.is_empty() {
let contents =
convert_reference_contents(intermediate_context.clone(), definition).await?;
let mut registry = intermediate_context.registry.lock().unwrap();
let entry = registry
.footnote_ids
.get_mut(existing_id)
.expect("If-statement proves this to be Some.");
entry.1 = contents;
2023-10-29 16:06:40 +00:00
}
2023-10-30 02:27:47 +00:00
Ok((existing_id, reference_count))
} else {
let existing_id = {
let mut registry = intermediate_context.registry.lock().unwrap();
registry.footnote_ids.push((label, Vec::new()));
registry.footnote_ids.len() - 1
};
let contents = convert_reference_contents(intermediate_context.clone(), definition).await?;
{
let mut registry = intermediate_context.registry.lock().unwrap();
let entry = registry
.footnote_ids
.get_mut(existing_id)
.expect("If-statement proves this to be Some.");
entry.1 = contents;
}
2023-10-30 02:27:47 +00:00
Ok((existing_id, reference_count))
2023-10-29 16:06:40 +00:00
}
}
2023-10-29 16:31:48 +00:00
/// Update the definition to a footnote but do not mark it as referenced.
pub(crate) async fn register_footnote_definition<'orig, 'parse>(
intermediate_context: IntermediateContext<'orig, 'parse>,
label: &'parse str,
definition: &'orig Vec<Element<'parse>>,
) -> Result<(), CustomError> {
let has_existing: bool = {
let mut registry = intermediate_context.registry.lock().unwrap();
registry
.footnote_ids
.iter_mut()
.any(|(id, _definition)| *id == Some(label))
};
if !has_existing {
let mut registry = intermediate_context.registry.lock().unwrap();
2023-10-30 01:50:51 +00:00
registry.on_deck_footnote_ids.insert(label, definition);
return Ok(());
}
let contents = convert_definition_contents(intermediate_context.clone(), definition).await?;
let mut registry = intermediate_context.registry.lock().unwrap();
if let Some((_existing_id, existing_definition)) = registry
.footnote_ids
.iter_mut()
.find(|(id, _definition)| *id == Some(label))
{
*existing_definition = contents;
2023-10-29 16:31:48 +00:00
}
Ok(())
2023-10-29 16:31:48 +00:00
}
async fn convert_reference_contents<'orig, 'parse>(
intermediate_context: IntermediateContext<'orig, 'parse>,
2023-12-23 11:38:23 +00:00
contents: &'orig [Object<'parse>],
2023-10-29 18:14:10 +00:00
) -> Result<Vec<IAstNode>, CustomError> {
let children = {
2023-10-29 18:14:10 +00:00
let mut ret = Vec::new();
for obj in contents.iter() {
ret.push(IObject::new(intermediate_context.clone(), obj).await?);
2023-10-29 18:14:10 +00:00
}
ret
};
let containing_paragraph =
IParagraph::artificial(intermediate_context.clone(), children, 0).await?;
2023-12-23 11:38:23 +00:00
let contents = vec![IAstNode::Paragraph(containing_paragraph)];
2023-10-29 18:14:10 +00:00
Ok(contents)
}
async fn convert_definition_contents<'orig, 'parse>(
intermediate_context: IntermediateContext<'orig, 'parse>,
2023-12-23 11:38:23 +00:00
contents: &'orig [Element<'parse>],
2023-10-29 18:14:10 +00:00
) -> Result<Vec<IAstNode>, CustomError> {
let contents = {
let mut ret = Vec::new();
for obj in contents.iter() {
2023-12-23 11:38:23 +00:00
ret.push(obj.as_ast_node(intermediate_context.clone()).await?);
2023-10-29 18:14:10 +00:00
}
ret
};
Ok(contents)
}
2023-10-30 02:01:42 +00:00
/// Take a footnote definition that has not yet received a reference and move it into the active footnotes.
pub(crate) async fn promote_footnote_definition<'orig, 'parse>(
intermediate_context: IntermediateContext<'orig, 'parse>,
2023-10-30 02:01:42 +00:00
label: &'parse str,
) -> Result<(), CustomError> {
let definition = {
let mut registry = intermediate_context.registry.lock().unwrap();
2023-12-23 11:38:23 +00:00
registry.on_deck_footnote_ids.remove(label)
2023-10-30 02:01:42 +00:00
};
if let Some(elements) = definition {
let existing_id = {
let mut registry = intermediate_context.registry.lock().unwrap();
registry.footnote_ids.push((Some(label), Vec::new()));
registry.footnote_ids.len() - 1
};
let contents = convert_definition_contents(intermediate_context.clone(), elements).await?;
{
let mut registry = intermediate_context.registry.lock().unwrap();
let entry = registry
.footnote_ids
.get_mut(existing_id)
.expect("If-statement proves this to be Some.");
entry.1 = contents;
}
2023-10-30 02:01:42 +00:00
}
Ok(())
}