diff --git a/default_environment/templates/html/ast_node.dust b/default_environment/templates/html/ast_node.dust new file mode 100644 index 0000000..cb3b7a9 --- /dev/null +++ b/default_environment/templates/html/ast_node.dust @@ -0,0 +1,57 @@ +{@select key=.type} + {@eq value="heading"}{>heading/}{/eq} + {@eq value="section"}{>section/}{/eq} + {@eq value="paragraph"}{>paragraph/}{/eq} + {@eq value="plain_list"}{>plain_list/}{/eq} + {@eq value="center_block"}{>center_block/}{/eq} + {@eq value="quote_block"}{>quote_block/}{/eq} + {@eq value="special_block"}{>special_block/}{/eq} + {@eq value="dynamic_block"}{>dynamic_block/}{/eq} + {@eq value="footnote_definition"}{>footnote_definition/}{/eq} + {@eq value="comment"}{>comment/}{/eq} + {@eq value="drawer"}{>drawer/}{/eq} + {@eq value="property_drawer"}{>property_drawer/}{/eq} + {@eq value="table"}{>table/}{/eq} + {@eq value="verse_block"}{>verse_block/}{/eq} + {@eq value="comment_block"}{>comment_block/}{/eq} + {@eq value="example_block"}{>example_block/}{/eq} + {@eq value="export_block"}{>export_block/}{/eq} + {@eq value="src_block"}{>src_block/}{/eq} + {@eq value="clock"}{>clock/}{/eq} + {@eq value="diary_sexp"}{>diary_sexp/}{/eq} + {@eq value="planning"}{>planning/}{/eq} + {@eq value="fixed_width_area"}{>fixed_width_area/}{/eq} + {@eq value="horizontal_rule"}{>horizontal_rule/}{/eq} + {@eq value="keyword"}{>keyword/}{/eq} + {@eq value="babel_call"}{>babel_call/}{/eq} + {@eq value="latex_environment"}{>latex_environment/}{/eq} + {@eq value="bold"}{>bold/}{/eq} + {@eq value="italic"}{>italic/}{/eq} + {@eq value="underline"}{>underline/}{/eq} + {@eq value="strike_through"}{>strike_through/}{/eq} + {@eq value="code"}{>code/}{/eq} + {@eq value="verbatim"}{>verbatim/}{/eq} + {@eq value="plain_text"}{>plain_text/}{/eq} + {@eq value="regular_link"}{>regular_link/}{/eq} + {@eq value="radio_link"}{>radio_link/}{/eq} + {@eq value="radio_target"}{>radio_target/}{/eq} + {@eq value="plain_link"}{>plain_link/}{/eq} + {@eq value="angle_link"}{>angle_link/}{/eq} + {@eq value="org_macro"}{>org_macro/}{/eq} + {@eq value="entity"}{>entity/}{/eq} + {@eq value="latex_fragment"}{>latex_fragment/}{/eq} + {@eq value="export_snippet"}{>export_snippet/}{/eq} + {@eq value="footnote_reference"}{>footnote_reference/}{/eq} + {@eq value="citation"}{>citation/}{/eq} + {@eq value="citation_reference"}{>citation_reference/}{/eq} + {@eq value="inline_babel_call"}{>inline_babel_call/}{/eq} + {@eq value="inline_source_block"}{>inline_source_block/}{/eq} + {@eq value="line_break"}{>line_break/}{/eq} + {@eq value="target"}{>target/}{/eq} + {@eq value="statistics_cookie"}{>statistics_cookie/}{/eq} + {@eq value="subscript"}{>subscript/}{/eq} + {@eq value="superscript"}{>superscript/}{/eq} + {@eq value="timestamp"}{>timestamp/}{/eq} + {@none}{!TODO: make this panic!}ERROR: Unrecognized type {.type}.{/none} +{/select} +{! TODO: Maybe the final space should be conditional on end blank in the org source !} diff --git a/default_environment/templates/html/blog_post_page.dust b/default_environment/templates/html/blog_post_page.dust index 361823b..067b223 100644 --- a/default_environment/templates/html/blog_post_page.dust +++ b/default_environment/templates/html/blog_post_page.dust @@ -10,5 +10,12 @@ {#.children} {>document_element/} {/.children} + + {?.footnotes} +

Footnotes:

+ {#.footnotes} + {>real_footnote_definition/} + {/.footnotes} + {/.footnotes} diff --git a/default_environment/templates/html/footnote_definition.dust b/default_environment/templates/html/footnote_definition.dust index 037c32c..9cf5738 100644 --- a/default_environment/templates/html/footnote_definition.dust +++ b/default_environment/templates/html/footnote_definition.dust @@ -1 +1 @@ -!!!!!!!! footnote_definition +{! noop !} diff --git a/default_environment/templates/html/footnote_reference.dust b/default_environment/templates/html/footnote_reference.dust index ae1f616..a218064 100644 --- a/default_environment/templates/html/footnote_reference.dust +++ b/default_environment/templates/html/footnote_reference.dust @@ -1 +1 @@ -!!!!!!!! footnote_reference +{.label} diff --git a/default_environment/templates/html/real_footnote_definition.dust b/default_environment/templates/html/real_footnote_definition.dust new file mode 100644 index 0000000..9c6be4b --- /dev/null +++ b/default_environment/templates/html/real_footnote_definition.dust @@ -0,0 +1 @@ +
{.label}
{#.contents}{>ast_node/}{/.contents}
diff --git a/org_test_documents/footnotes/reference_in_definition.org b/org_test_documents/footnotes/reference_in_definition.org new file mode 100644 index 0000000..01f8b5e --- /dev/null +++ b/org_test_documents/footnotes/reference_in_definition.org @@ -0,0 +1,21 @@ +# This test shows that footnote references only count if the definition containing them is rendered. + +foo[fn:a:bar] + +[fn:a] lorem + +[fn:b] ipsum + +[fn:d] fizz + +[fn:c] dolar + +yo[fn:b] + + +hello[fn:c] + + +[fn:e] buzz + +sup[fn:d] diff --git a/org_test_documents/footnotes/simple.org b/org_test_documents/footnotes/simple.org new file mode 100644 index 0000000..8a15e71 --- /dev/null +++ b/org_test_documents/footnotes/simple.org @@ -0,0 +1,25 @@ +# Test proves that: +# +# - Anonymous references with identical content get unique IDs. +# - Unreferenced footnote definitions are dropped. +# - Footnote definitions that come before their first reference are dropped. + +foo[fn:2:something] + +bar[fn::something] + +baz[fn::something] + +cat[fn::something] + +dog[fn:3] + +[fn:3] ipsum + +[fn:4] lorem + +[fn:3] dolar + +[fn:5] not referenced + +stuff[fn:4] and things diff --git a/src/context/ast_node.rs b/src/context/ast_node.rs new file mode 100644 index 0000000..7a2ffcd --- /dev/null +++ b/src/context/ast_node.rs @@ -0,0 +1,407 @@ +use std::path::Path; + +use serde::Serialize; + +use crate::config::Config; +use crate::error::CustomError; +use crate::intermediate::IAstNode; + +use super::angle_link::RenderAngleLink; +use super::babel_call::RenderBabelCall; +use super::bold::RenderBold; +use super::center_block::RenderCenterBlock; +use super::citation::RenderCitation; +use super::citation_reference::RenderCitationReference; +use super::clock::RenderClock; +use super::code::RenderCode; +use super::comment::RenderComment; +use super::comment_block::RenderCommentBlock; +use super::diary_sexp::RenderDiarySexp; +use super::drawer::RenderDrawer; +use super::dynamic_block::RenderDynamicBlock; +use super::entity::RenderEntity; +use super::example_block::RenderExampleBlock; +use super::export_block::RenderExportBlock; +use super::export_snippet::RenderExportSnippet; +use super::fixed_width_area::RenderFixedWidthArea; +use super::footnote_definition::RenderFootnoteDefinition; +use super::footnote_reference::RenderFootnoteReference; +use super::horizontal_rule::RenderHorizontalRule; +use super::inline_babel_call::RenderInlineBabelCall; +use super::inline_source_block::RenderInlineSourceBlock; +use super::italic::RenderItalic; +use super::keyword::RenderKeyword; +use super::latex_environment::RenderLatexEnvironment; +use super::latex_fragment::RenderLatexFragment; +use super::line_break::RenderLineBreak; +use super::org_macro::RenderOrgMacro; +use super::paragraph::RenderParagraph; +use super::plain_link::RenderPlainLink; +use super::plain_list::RenderPlainList; +use super::plain_text::RenderPlainText; +use super::planning::RenderPlanning; +use super::property_drawer::RenderPropertyDrawer; +use super::quote_block::RenderQuoteBlock; +use super::radio_link::RenderRadioLink; +use super::radio_target::RenderRadioTarget; +use super::regular_link::RenderRegularLink; +use super::special_block::RenderSpecialBlock; +use super::src_block::RenderSrcBlock; +use super::statistics_cookie::RenderStatisticsCookie; +use super::strike_through::RenderStrikeThrough; +use super::subscript::RenderSubscript; +use super::superscript::RenderSuperscript; +use super::table::RenderTable; +use super::target::RenderTarget; +use super::timestamp::RenderTimestamp; +use super::underline::RenderUnderline; +use super::verbatim::RenderVerbatim; +use super::verse_block::RenderVerseBlock; +use super::RenderHeading; +use super::RenderSection; + +#[derive(Debug, Serialize)] +#[serde(untagged)] +pub(crate) enum RenderAstNode { + Heading(RenderHeading), + Section(RenderSection), + Paragraph(RenderParagraph), + PlainList(RenderPlainList), + CenterBlock(RenderCenterBlock), + QuoteBlock(RenderQuoteBlock), + SpecialBlock(RenderSpecialBlock), + DynamicBlock(RenderDynamicBlock), + FootnoteDefinition(RenderFootnoteDefinition), + Comment(RenderComment), + Drawer(RenderDrawer), + PropertyDrawer(RenderPropertyDrawer), + Table(RenderTable), + VerseBlock(RenderVerseBlock), + CommentBlock(RenderCommentBlock), + ExampleBlock(RenderExampleBlock), + ExportBlock(RenderExportBlock), + SrcBlock(RenderSrcBlock), + Clock(RenderClock), + DiarySexp(RenderDiarySexp), + Planning(RenderPlanning), + FixedWidthArea(RenderFixedWidthArea), + HorizontalRule(RenderHorizontalRule), + Keyword(RenderKeyword), + BabelCall(RenderBabelCall), + LatexEnvironment(RenderLatexEnvironment), + Bold(RenderBold), + Italic(RenderItalic), + Underline(RenderUnderline), + StrikeThrough(RenderStrikeThrough), + Code(RenderCode), + Verbatim(RenderVerbatim), + PlainText(RenderPlainText), + RegularLink(RenderRegularLink), + RadioLink(RenderRadioLink), + RadioTarget(RenderRadioTarget), + PlainLink(RenderPlainLink), + AngleLink(RenderAngleLink), + OrgMacro(RenderOrgMacro), + Entity(RenderEntity), + LatexFragment(RenderLatexFragment), + ExportSnippet(RenderExportSnippet), + FootnoteReference(RenderFootnoteReference), + Citation(RenderCitation), + CitationReference(RenderCitationReference), + InlineBabelCall(RenderInlineBabelCall), + InlineSourceBlock(RenderInlineSourceBlock), + LineBreak(RenderLineBreak), + Target(RenderTarget), + StatisticsCookie(RenderStatisticsCookie), + Subscript(RenderSubscript), + Superscript(RenderSuperscript), + Timestamp(RenderTimestamp), +} + +pub(crate) trait IntoRenderAstNode { + fn into_render_ast_node( + &self, + config: &Config, + output_directory: &Path, + output_file: &Path, + ) -> Result; +} + +impl IntoRenderAstNode for IAstNode { + fn into_render_ast_node( + &self, + config: &Config, + output_directory: &Path, + output_file: &Path, + ) -> Result { + match self { + IAstNode::Heading(inner) => Ok(RenderAstNode::Heading(RenderHeading::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::Section(inner) => Ok(RenderAstNode::Section(RenderSection::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::Paragraph(inner) => Ok(RenderAstNode::Paragraph(RenderParagraph::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::PlainList(inner) => Ok(RenderAstNode::PlainList(RenderPlainList::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::CenterBlock(inner) => Ok(RenderAstNode::CenterBlock(RenderCenterBlock::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::QuoteBlock(inner) => Ok(RenderAstNode::QuoteBlock(RenderQuoteBlock::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::SpecialBlock(inner) => Ok(RenderAstNode::SpecialBlock( + RenderSpecialBlock::new(config, output_directory, output_file, inner)?, + )), + IAstNode::DynamicBlock(inner) => Ok(RenderAstNode::DynamicBlock( + RenderDynamicBlock::new(config, output_directory, output_file, inner)?, + )), + IAstNode::FootnoteDefinition(inner) => Ok(RenderAstNode::FootnoteDefinition( + RenderFootnoteDefinition::new(config, output_directory, output_file, inner)?, + )), + IAstNode::Comment(inner) => Ok(RenderAstNode::Comment(RenderComment::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::Drawer(inner) => Ok(RenderAstNode::Drawer(RenderDrawer::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::PropertyDrawer(inner) => Ok(RenderAstNode::PropertyDrawer( + RenderPropertyDrawer::new(config, output_directory, output_file, inner)?, + )), + IAstNode::Table(inner) => Ok(RenderAstNode::Table(RenderTable::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::VerseBlock(inner) => Ok(RenderAstNode::VerseBlock(RenderVerseBlock::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::CommentBlock(inner) => Ok(RenderAstNode::CommentBlock( + RenderCommentBlock::new(config, output_directory, output_file, inner)?, + )), + IAstNode::ExampleBlock(inner) => Ok(RenderAstNode::ExampleBlock( + RenderExampleBlock::new(config, output_directory, output_file, inner)?, + )), + IAstNode::ExportBlock(inner) => Ok(RenderAstNode::ExportBlock(RenderExportBlock::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::SrcBlock(inner) => Ok(RenderAstNode::SrcBlock(RenderSrcBlock::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::Clock(inner) => Ok(RenderAstNode::Clock(RenderClock::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::DiarySexp(inner) => Ok(RenderAstNode::DiarySexp(RenderDiarySexp::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::Planning(inner) => Ok(RenderAstNode::Planning(RenderPlanning::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::FixedWidthArea(inner) => Ok(RenderAstNode::FixedWidthArea( + RenderFixedWidthArea::new(config, output_directory, output_file, inner)?, + )), + IAstNode::HorizontalRule(inner) => Ok(RenderAstNode::HorizontalRule( + RenderHorizontalRule::new(config, output_directory, output_file, inner)?, + )), + IAstNode::Keyword(inner) => Ok(RenderAstNode::Keyword(RenderKeyword::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::BabelCall(inner) => Ok(RenderAstNode::BabelCall(RenderBabelCall::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::LatexEnvironment(inner) => Ok(RenderAstNode::LatexEnvironment( + RenderLatexEnvironment::new(config, output_directory, output_file, inner)?, + )), + IAstNode::Bold(inner) => Ok(RenderAstNode::Bold(RenderBold::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::Italic(inner) => Ok(RenderAstNode::Italic(RenderItalic::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::Underline(inner) => Ok(RenderAstNode::Underline(RenderUnderline::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::StrikeThrough(inner) => Ok(RenderAstNode::StrikeThrough( + RenderStrikeThrough::new(config, output_directory, output_file, inner)?, + )), + IAstNode::Code(inner) => Ok(RenderAstNode::Code(RenderCode::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::Verbatim(inner) => Ok(RenderAstNode::Verbatim(RenderVerbatim::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::PlainText(inner) => Ok(RenderAstNode::PlainText(RenderPlainText::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::RegularLink(inner) => Ok(RenderAstNode::RegularLink(RenderRegularLink::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::RadioLink(inner) => Ok(RenderAstNode::RadioLink(RenderRadioLink::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::RadioTarget(inner) => Ok(RenderAstNode::RadioTarget(RenderRadioTarget::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::PlainLink(inner) => Ok(RenderAstNode::PlainLink(RenderPlainLink::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::AngleLink(inner) => Ok(RenderAstNode::AngleLink(RenderAngleLink::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::OrgMacro(inner) => Ok(RenderAstNode::OrgMacro(RenderOrgMacro::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::Entity(inner) => Ok(RenderAstNode::Entity(RenderEntity::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::LatexFragment(inner) => Ok(RenderAstNode::LatexFragment( + RenderLatexFragment::new(config, output_directory, output_file, inner)?, + )), + IAstNode::ExportSnippet(inner) => Ok(RenderAstNode::ExportSnippet( + RenderExportSnippet::new(config, output_directory, output_file, inner)?, + )), + IAstNode::FootnoteReference(inner) => Ok(RenderAstNode::FootnoteReference( + RenderFootnoteReference::new(config, output_directory, output_file, inner)?, + )), + IAstNode::Citation(inner) => Ok(RenderAstNode::Citation(RenderCitation::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::CitationReference(inner) => Ok(RenderAstNode::CitationReference( + RenderCitationReference::new(config, output_directory, output_file, inner)?, + )), + IAstNode::InlineBabelCall(inner) => Ok(RenderAstNode::InlineBabelCall( + RenderInlineBabelCall::new(config, output_directory, output_file, inner)?, + )), + IAstNode::InlineSourceBlock(inner) => Ok(RenderAstNode::InlineSourceBlock( + RenderInlineSourceBlock::new(config, output_directory, output_file, inner)?, + )), + IAstNode::LineBreak(inner) => Ok(RenderAstNode::LineBreak(RenderLineBreak::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::Target(inner) => Ok(RenderAstNode::Target(RenderTarget::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::StatisticsCookie(inner) => Ok(RenderAstNode::StatisticsCookie( + RenderStatisticsCookie::new(config, output_directory, output_file, inner)?, + )), + IAstNode::Subscript(inner) => Ok(RenderAstNode::Subscript(RenderSubscript::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::Superscript(inner) => Ok(RenderAstNode::Superscript(RenderSuperscript::new( + config, + output_directory, + output_file, + inner, + )?)), + IAstNode::Timestamp(inner) => Ok(RenderAstNode::Timestamp(RenderTimestamp::new( + config, + output_directory, + output_file, + inner, + )?)), + } + } +} diff --git a/src/context/blog_post_page.rs b/src/context/blog_post_page.rs index 9f607ce..a8720f7 100644 --- a/src/context/blog_post_page.rs +++ b/src/context/blog_post_page.rs @@ -1,5 +1,6 @@ use serde::Serialize; +use super::footnote_definition::RenderRealFootnoteDefinition; use super::GlobalSettings; use super::RenderDocumentElement; @@ -15,6 +16,8 @@ pub(crate) struct RenderBlogPostPage { self_link: Option, children: Vec, + + footnotes: Vec, } impl RenderBlogPostPage { @@ -23,12 +26,14 @@ impl RenderBlogPostPage { title: Option, self_link: Option, children: Vec, + footnotes: Vec, ) -> RenderBlogPostPage { RenderBlogPostPage { global_settings, title, self_link, children, + footnotes, } } } diff --git a/src/context/footnote_definition.rs b/src/context/footnote_definition.rs index 589658e..877ec42 100644 --- a/src/context/footnote_definition.rs +++ b/src/context/footnote_definition.rs @@ -5,6 +5,10 @@ use serde::Serialize; use crate::config::Config; use crate::error::CustomError; use crate::intermediate::IFootnoteDefinition; +use crate::intermediate::IRealFootnoteDefinition; + +use super::ast_node::IntoRenderAstNode; +use super::ast_node::RenderAstNode; #[derive(Debug, Serialize)] #[serde(tag = "type")] @@ -21,3 +25,37 @@ impl RenderFootnoteDefinition { Ok(RenderFootnoteDefinition {}) } } + +#[derive(Debug, Serialize)] +#[serde(tag = "type")] +#[serde(rename = "footnote_reference")] +pub(crate) struct RenderRealFootnoteDefinition { + definition_id: String, + reference_link: String, + label: String, + contents: Vec, +} + +impl RenderRealFootnoteDefinition { + pub(crate) fn new( + config: &Config, + output_directory: &Path, + output_file: &Path, + original: &IRealFootnoteDefinition, + ) -> Result { + let contents = { + let mut ret = Vec::new(); + for obj in original.contents.iter() { + ret.push(obj.into_render_ast_node(config, output_directory, output_file)?); + } + ret + }; + + Ok(RenderRealFootnoteDefinition { + definition_id: original.get_definition_id(), + reference_link: format!("#{}", original.get_reference_id()), + label: original.get_display_label(), + contents, + }) + } +} diff --git a/src/context/footnote_reference.rs b/src/context/footnote_reference.rs index 300517e..11d1622 100644 --- a/src/context/footnote_reference.rs +++ b/src/context/footnote_reference.rs @@ -9,15 +9,23 @@ use crate::intermediate::IFootnoteReference; #[derive(Debug, Serialize)] #[serde(tag = "type")] #[serde(rename = "footnote_reference")] -pub(crate) struct RenderFootnoteReference {} +pub(crate) struct RenderFootnoteReference { + reference_id: String, + definition_link: String, + label: String, +} impl RenderFootnoteReference { pub(crate) fn new( config: &Config, output_directory: &Path, output_file: &Path, - comment: &IFootnoteReference, + original: &IFootnoteReference, ) -> Result { - Ok(RenderFootnoteReference {}) + Ok(RenderFootnoteReference { + reference_id: original.get_reference_id(), + definition_link: format!("#{}", original.get_definition_id()), + label: original.get_display_label(), + }) } } diff --git a/src/context/heading.rs b/src/context/heading.rs index ec6399f..d69f659 100644 --- a/src/context/heading.rs +++ b/src/context/heading.rs @@ -30,8 +30,8 @@ impl RenderHeading { for obj in heading.title.iter() { ret.push(RenderObject::new( config, - &output_directory, - &output_file, + output_directory, + output_file, obj, )?); } @@ -43,8 +43,8 @@ impl RenderHeading { for obj in heading.children.iter() { ret.push(RenderDocumentElement::new( config, - &output_directory, - &output_file, + output_directory, + output_file, obj, )?); } diff --git a/src/context/mod.rs b/src/context/mod.rs index 25ae64c..129ebca 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -1,4 +1,5 @@ mod angle_link; +mod ast_node; mod babel_call; mod blog_post_page; mod bold; @@ -61,6 +62,7 @@ mod verse_block; pub(crate) use blog_post_page::RenderBlogPostPage; pub(crate) use document_element::RenderDocumentElement; pub(crate) use element::RenderElement; +pub(crate) use footnote_definition::RenderRealFootnoteDefinition; pub(crate) use global_settings::GlobalSettings; pub(crate) use heading::RenderHeading; pub(crate) use object::RenderObject; diff --git a/src/context/paragraph.rs b/src/context/paragraph.rs index 2e0e1c3..c211535 100644 --- a/src/context/paragraph.rs +++ b/src/context/paragraph.rs @@ -27,8 +27,8 @@ impl RenderParagraph { for obj in paragraph.children.iter() { ret.push(RenderObject::new( config, - &output_directory, - &output_file, + output_directory, + output_file, obj, )?); } diff --git a/src/context/plain_list.rs b/src/context/plain_list.rs index b6b61b9..25840b8 100644 --- a/src/context/plain_list.rs +++ b/src/context/plain_list.rs @@ -33,8 +33,8 @@ impl RenderPlainList { for obj in original.children.iter() { ret.push(RenderPlainListItem::new( config, - &output_directory, - &output_file, + output_directory, + output_file, obj, )?); } diff --git a/src/context/plain_list_item.rs b/src/context/plain_list_item.rs index 0cfe106..df18dee 100644 --- a/src/context/plain_list_item.rs +++ b/src/context/plain_list_item.rs @@ -29,8 +29,8 @@ impl RenderPlainListItem { for obj in original.tag.iter() { ret.push(RenderObject::new( config, - &output_directory, - &output_file, + output_directory, + output_file, obj, )?); } @@ -42,8 +42,8 @@ impl RenderPlainListItem { for obj in original.children.iter() { ret.push(RenderElement::new( config, - &output_directory, - &output_file, + output_directory, + output_file, obj, )?); } diff --git a/src/context/quote_block.rs b/src/context/quote_block.rs index 8a5d7f0..5866442 100644 --- a/src/context/quote_block.rs +++ b/src/context/quote_block.rs @@ -27,8 +27,8 @@ impl RenderQuoteBlock { for obj in original.children.iter() { ret.push(RenderElement::new( config, - &output_directory, - &output_file, + output_directory, + output_file, obj, )?); } diff --git a/src/context/regular_link.rs b/src/context/regular_link.rs index 0cecb38..58ccd4a 100644 --- a/src/context/regular_link.rs +++ b/src/context/regular_link.rs @@ -28,8 +28,8 @@ impl RenderRegularLink { for obj in regular_link.children.iter() { ret.push(RenderObject::new( config, - &output_directory, - &output_file, + output_directory, + output_file, obj, )?); } diff --git a/src/context/section.rs b/src/context/section.rs index 45d66bf..87c5a12 100644 --- a/src/context/section.rs +++ b/src/context/section.rs @@ -27,8 +27,8 @@ impl RenderSection { for obj in section.children.iter() { ret.push(RenderElement::new( config, - &output_directory, - &output_file, + output_directory, + output_file, obj, )?); } diff --git a/src/intermediate/angle_link.rs b/src/intermediate/angle_link.rs index bd84243..736def0 100644 --- a/src/intermediate/angle_link.rs +++ b/src/intermediate/angle_link.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct IAngleLink {} - -impl IAngleLink { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::AngleLink<'parse>, - ) -> Result { - Ok(IAngleLink {}) - } -} +inoop!(IAngleLink, AngleLink); diff --git a/src/intermediate/ast_node.rs b/src/intermediate/ast_node.rs new file mode 100644 index 0000000..c205e19 --- /dev/null +++ b/src/intermediate/ast_node.rs @@ -0,0 +1,319 @@ +use super::angle_link::IAngleLink; +use super::bold::IBold; +use super::citation::ICitation; +use super::citation_reference::ICitationReference; +use super::code::ICode; +use super::comment::IComment; +use super::entity::IEntity; +use super::export_snippet::IExportSnippet; +use super::footnote_reference::IFootnoteReference; +use super::inline_babel_call::IInlineBabelCall; +use super::inline_source_block::IInlineSourceBlock; +use super::italic::IItalic; +use super::keyword::IKeyword; +use super::latex_fragment::ILatexFragment; +use super::line_break::ILineBreak; +use super::org_macro::IOrgMacro; +use super::plain_link::IPlainLink; +use super::plain_text::IPlainText; +use super::radio_link::IRadioLink; +use super::radio_target::IRadioTarget; +use super::registry::Registry; +use super::regular_link::IRegularLink; +use super::statistics_cookie::IStatisticsCookie; +use super::strike_through::IStrikeThrough; +use super::subscript::ISubscript; +use super::superscript::ISuperscript; +use super::IBabelCall; +use super::ICenterBlock; +use super::IClock; +use super::ICommentBlock; +use super::IDiarySexp; +use super::IDrawer; +use super::IDynamicBlock; +use super::IExampleBlock; +use super::IExportBlock; +use super::IFixedWidthArea; +use super::IFootnoteDefinition; +use super::IHeading; +use super::IHorizontalRule; +use super::ILatexEnvironment; +use super::IParagraph; +use super::IPlainList; +use super::IPlanning; +use super::IPropertyDrawer; +use super::IQuoteBlock; +use super::ISection; +use super::ISpecialBlock; +use super::ISrcBlock; +use super::ITable; +use super::ITarget; +use super::ITimestamp; +use super::IUnderline; +use super::IVerbatim; +use super::IVerseBlock; +use super::RefRegistry; +use crate::error::CustomError; +use futures::future::{BoxFuture, FutureExt}; + +#[derive(Debug, Clone)] +pub(crate) enum IAstNode { + Heading(IHeading), + Section(ISection), + Paragraph(IParagraph), + PlainList(IPlainList), + CenterBlock(ICenterBlock), + QuoteBlock(IQuoteBlock), + SpecialBlock(ISpecialBlock), + DynamicBlock(IDynamicBlock), + FootnoteDefinition(IFootnoteDefinition), + Comment(IComment), + Drawer(IDrawer), + PropertyDrawer(IPropertyDrawer), + Table(ITable), + VerseBlock(IVerseBlock), + CommentBlock(ICommentBlock), + ExampleBlock(IExampleBlock), + ExportBlock(IExportBlock), + SrcBlock(ISrcBlock), + Clock(IClock), + DiarySexp(IDiarySexp), + Planning(IPlanning), + FixedWidthArea(IFixedWidthArea), + HorizontalRule(IHorizontalRule), + Keyword(IKeyword), + BabelCall(IBabelCall), + LatexEnvironment(ILatexEnvironment), + Bold(IBold), + Italic(IItalic), + Underline(IUnderline), + StrikeThrough(IStrikeThrough), + Code(ICode), + Verbatim(IVerbatim), + PlainText(IPlainText), + RegularLink(IRegularLink), + RadioLink(IRadioLink), + RadioTarget(IRadioTarget), + PlainLink(IPlainLink), + AngleLink(IAngleLink), + OrgMacro(IOrgMacro), + Entity(IEntity), + LatexFragment(ILatexFragment), + ExportSnippet(IExportSnippet), + FootnoteReference(IFootnoteReference), + Citation(ICitation), + CitationReference(ICitationReference), + InlineBabelCall(IInlineBabelCall), + InlineSourceBlock(IInlineSourceBlock), + LineBreak(ILineBreak), + Target(ITarget), + StatisticsCookie(IStatisticsCookie), + Subscript(ISubscript), + Superscript(ISuperscript), + Timestamp(ITimestamp), +} + +pub(crate) trait IntoIAstNode<'parse> { + fn into_ast_node<'orig>( + &'orig self, + registry: RefRegistry<'orig, 'parse>, + ) -> BoxFuture<'orig, Result>; +} + +impl<'parse> IntoIAstNode<'parse> for organic::types::DocumentElement<'parse> { + fn into_ast_node<'orig>( + &'orig self, + registry: RefRegistry<'orig, 'parse>, + ) -> BoxFuture<'orig, Result> { + async move { + match self { + organic::types::DocumentElement::Heading(inner) => { + Ok(IAstNode::Heading(IHeading::new(registry, inner).await?)) + } + organic::types::DocumentElement::Section(inner) => { + Ok(IAstNode::Section(ISection::new(registry, inner).await?)) + } + } + } + .boxed() + } +} + +impl<'parse> IntoIAstNode<'parse> for organic::types::Element<'parse> { + fn into_ast_node<'orig>( + &'orig self, + registry: RefRegistry<'orig, 'parse>, + ) -> BoxFuture<'orig, Result> { + async move { + match self { + organic::types::Element::Paragraph(inner) => { + Ok(IAstNode::Paragraph(IParagraph::new(registry, inner).await?)) + } + organic::types::Element::PlainList(inner) => { + Ok(IAstNode::PlainList(IPlainList::new(registry, inner).await?)) + } + organic::types::Element::CenterBlock(inner) => Ok(IAstNode::CenterBlock( + ICenterBlock::new(registry, inner).await?, + )), + organic::types::Element::QuoteBlock(inner) => Ok(IAstNode::QuoteBlock( + IQuoteBlock::new(registry, inner).await?, + )), + organic::types::Element::SpecialBlock(inner) => Ok(IAstNode::SpecialBlock( + ISpecialBlock::new(registry, inner).await?, + )), + organic::types::Element::DynamicBlock(inner) => Ok(IAstNode::DynamicBlock( + IDynamicBlock::new(registry, inner).await?, + )), + organic::types::Element::FootnoteDefinition(inner) => Ok( + IAstNode::FootnoteDefinition(IFootnoteDefinition::new(registry, inner).await?), + ), + organic::types::Element::Comment(inner) => { + Ok(IAstNode::Comment(IComment::new(registry, inner).await?)) + } + organic::types::Element::Drawer(inner) => { + Ok(IAstNode::Drawer(IDrawer::new(registry, inner).await?)) + } + organic::types::Element::PropertyDrawer(inner) => Ok(IAstNode::PropertyDrawer( + IPropertyDrawer::new(registry, inner).await?, + )), + organic::types::Element::Table(inner) => { + Ok(IAstNode::Table(ITable::new(registry, inner).await?)) + } + organic::types::Element::VerseBlock(inner) => Ok(IAstNode::VerseBlock( + IVerseBlock::new(registry, inner).await?, + )), + organic::types::Element::CommentBlock(inner) => Ok(IAstNode::CommentBlock( + ICommentBlock::new(registry, inner).await?, + )), + organic::types::Element::ExampleBlock(inner) => Ok(IAstNode::ExampleBlock( + IExampleBlock::new(registry, inner).await?, + )), + organic::types::Element::ExportBlock(inner) => Ok(IAstNode::ExportBlock( + IExportBlock::new(registry, inner).await?, + )), + organic::types::Element::SrcBlock(inner) => { + Ok(IAstNode::SrcBlock(ISrcBlock::new(registry, inner).await?)) + } + organic::types::Element::Clock(inner) => { + Ok(IAstNode::Clock(IClock::new(registry, inner).await?)) + } + organic::types::Element::DiarySexp(inner) => { + Ok(IAstNode::DiarySexp(IDiarySexp::new(registry, inner).await?)) + } + organic::types::Element::Planning(inner) => { + Ok(IAstNode::Planning(IPlanning::new(registry, inner).await?)) + } + organic::types::Element::FixedWidthArea(inner) => Ok(IAstNode::FixedWidthArea( + IFixedWidthArea::new(registry, inner).await?, + )), + organic::types::Element::HorizontalRule(inner) => Ok(IAstNode::HorizontalRule( + IHorizontalRule::new(registry, inner).await?, + )), + organic::types::Element::Keyword(inner) => { + Ok(IAstNode::Keyword(IKeyword::new(registry, inner).await?)) + } + organic::types::Element::BabelCall(inner) => { + Ok(IAstNode::BabelCall(IBabelCall::new(registry, inner).await?)) + } + organic::types::Element::LatexEnvironment(inner) => Ok(IAstNode::LatexEnvironment( + ILatexEnvironment::new(registry, inner).await?, + )), + } + } + .boxed() + } +} + +impl<'parse> IntoIAstNode<'parse> for organic::types::Object<'parse> { + fn into_ast_node<'orig>( + &'orig self, + registry: RefRegistry<'orig, 'parse>, + ) -> BoxFuture<'orig, Result> { + async move { + match self { + organic::types::Object::Bold(inner) => { + Ok(IAstNode::Bold(IBold::new(registry, inner).await?)) + } + organic::types::Object::Italic(inner) => { + Ok(IAstNode::Italic(IItalic::new(registry, inner).await?)) + } + organic::types::Object::Underline(inner) => { + Ok(IAstNode::Underline(IUnderline::new(registry, inner).await?)) + } + organic::types::Object::StrikeThrough(inner) => Ok(IAstNode::StrikeThrough( + IStrikeThrough::new(registry, inner).await?, + )), + organic::types::Object::Code(inner) => { + Ok(IAstNode::Code(ICode::new(registry, inner).await?)) + } + organic::types::Object::Verbatim(inner) => { + Ok(IAstNode::Verbatim(IVerbatim::new(registry, inner).await?)) + } + organic::types::Object::PlainText(inner) => { + Ok(IAstNode::PlainText(IPlainText::new(registry, inner).await?)) + } + organic::types::Object::RegularLink(inner) => Ok(IAstNode::RegularLink( + IRegularLink::new(registry, inner).await?, + )), + organic::types::Object::RadioLink(inner) => { + Ok(IAstNode::RadioLink(IRadioLink::new(registry, inner).await?)) + } + organic::types::Object::RadioTarget(inner) => Ok(IAstNode::RadioTarget( + IRadioTarget::new(registry, inner).await?, + )), + organic::types::Object::PlainLink(inner) => { + Ok(IAstNode::PlainLink(IPlainLink::new(registry, inner).await?)) + } + organic::types::Object::AngleLink(inner) => { + Ok(IAstNode::AngleLink(IAngleLink::new(registry, inner).await?)) + } + organic::types::Object::OrgMacro(inner) => { + Ok(IAstNode::OrgMacro(IOrgMacro::new(registry, inner).await?)) + } + organic::types::Object::Entity(inner) => { + Ok(IAstNode::Entity(IEntity::new(registry, inner).await?)) + } + organic::types::Object::LatexFragment(inner) => Ok(IAstNode::LatexFragment( + ILatexFragment::new(registry, inner).await?, + )), + organic::types::Object::ExportSnippet(inner) => Ok(IAstNode::ExportSnippet( + IExportSnippet::new(registry, inner).await?, + )), + organic::types::Object::FootnoteReference(inner) => Ok( + IAstNode::FootnoteReference(IFootnoteReference::new(registry, inner).await?), + ), + organic::types::Object::Citation(inner) => { + Ok(IAstNode::Citation(ICitation::new(registry, inner).await?)) + } + organic::types::Object::CitationReference(inner) => Ok( + IAstNode::CitationReference(ICitationReference::new(registry, inner).await?), + ), + organic::types::Object::InlineBabelCall(inner) => Ok(IAstNode::InlineBabelCall( + IInlineBabelCall::new(registry, inner).await?, + )), + organic::types::Object::InlineSourceBlock(inner) => Ok( + IAstNode::InlineSourceBlock(IInlineSourceBlock::new(registry, inner).await?), + ), + organic::types::Object::LineBreak(inner) => { + Ok(IAstNode::LineBreak(ILineBreak::new(registry, inner).await?)) + } + organic::types::Object::Target(inner) => { + Ok(IAstNode::Target(ITarget::new(registry, inner).await?)) + } + organic::types::Object::StatisticsCookie(inner) => Ok(IAstNode::StatisticsCookie( + IStatisticsCookie::new(registry, inner).await?, + )), + organic::types::Object::Subscript(inner) => { + Ok(IAstNode::Subscript(ISubscript::new(registry, inner).await?)) + } + organic::types::Object::Superscript(inner) => Ok(IAstNode::Superscript( + ISuperscript::new(registry, inner).await?, + )), + organic::types::Object::Timestamp(inner) => { + Ok(IAstNode::Timestamp(ITimestamp::new(registry, inner).await?)) + } + } + } + .boxed() + } +} diff --git a/src/intermediate/babel_call.rs b/src/intermediate/babel_call.rs index a8d542c..cffc2e5 100644 --- a/src/intermediate/babel_call.rs +++ b/src/intermediate/babel_call.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct IBabelCall {} - -impl IBabelCall { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::BabelCall<'parse>, - ) -> Result { - Ok(IBabelCall {}) - } -} +inoop!(IBabelCall, BabelCall); diff --git a/src/intermediate/bold.rs b/src/intermediate/bold.rs index c00f94b..bee42d1 100644 --- a/src/intermediate/bold.rs +++ b/src/intermediate/bold.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct IBold {} - -impl IBold { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::Bold<'parse>, - ) -> Result { - Ok(IBold {}) - } -} +inoop!(IBold, Bold); diff --git a/src/intermediate/center_block.rs b/src/intermediate/center_block.rs index 2e4d777..3b93a29 100644 --- a/src/intermediate/center_block.rs +++ b/src/intermediate/center_block.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct ICenterBlock {} - -impl ICenterBlock { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::CenterBlock<'parse>, - ) -> Result { - Ok(ICenterBlock {}) - } -} +inoop!(ICenterBlock, CenterBlock); diff --git a/src/intermediate/citation.rs b/src/intermediate/citation.rs index 1209241..916ff72 100644 --- a/src/intermediate/citation.rs +++ b/src/intermediate/citation.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct ICitation {} - -impl ICitation { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::Citation<'parse>, - ) -> Result { - Ok(ICitation {}) - } -} +inoop!(ICitation, Citation); diff --git a/src/intermediate/citation_reference.rs b/src/intermediate/citation_reference.rs index 1030158..f7b2e9b 100644 --- a/src/intermediate/citation_reference.rs +++ b/src/intermediate/citation_reference.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct ICitationReference {} - -impl ICitationReference { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::CitationReference<'parse>, - ) -> Result { - Ok(ICitationReference {}) - } -} +inoop!(ICitationReference, CitationReference); diff --git a/src/intermediate/clock.rs b/src/intermediate/clock.rs index 5f3b8eb..056d582 100644 --- a/src/intermediate/clock.rs +++ b/src/intermediate/clock.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct IClock {} - -impl IClock { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::Clock<'parse>, - ) -> Result { - Ok(IClock {}) - } -} +inoop!(IClock, Clock); diff --git a/src/intermediate/code.rs b/src/intermediate/code.rs index 6a137ba..01d7dbe 100644 --- a/src/intermediate/code.rs +++ b/src/intermediate/code.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct ICode {} - -impl ICode { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::Code<'parse>, - ) -> Result { - Ok(ICode {}) - } -} +inoop!(ICode, Code); diff --git a/src/intermediate/comment.rs b/src/intermediate/comment.rs index 70649f2..19c706a 100644 --- a/src/intermediate/comment.rs +++ b/src/intermediate/comment.rs @@ -1,16 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -/// Essentially a no-op since the comment is not rendered. -#[derive(Debug)] -pub(crate) struct IComment {} - -impl IComment { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - comment: &organic::types::Comment<'parse>, - ) -> Result { - Ok(IComment {}) - } -} +inoop!(IComment, Comment); diff --git a/src/intermediate/comment_block.rs b/src/intermediate/comment_block.rs index 71602eb..89f4bc3 100644 --- a/src/intermediate/comment_block.rs +++ b/src/intermediate/comment_block.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct ICommentBlock {} - -impl ICommentBlock { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::CommentBlock<'parse>, - ) -> Result { - Ok(ICommentBlock {}) - } -} +inoop!(ICommentBlock, CommentBlock); diff --git a/src/intermediate/convert.rs b/src/intermediate/convert.rs index ee9bdfa..6bdb654 100644 --- a/src/intermediate/convert.rs +++ b/src/intermediate/convert.rs @@ -6,6 +6,7 @@ use crate::config::Config; use crate::context::GlobalSettings; use crate::context::RenderBlogPostPage; use crate::context::RenderDocumentElement; +use crate::context::RenderRealFootnoteDefinition; use crate::error::CustomError; use super::BlogPost; @@ -55,11 +56,27 @@ pub(crate) fn convert_blog_post_page_to_render_context, F: AsRef< children }; + let footnotes = { + let mut ret = Vec::new(); + + for footnote in page.footnotes.iter() { + ret.push(RenderRealFootnoteDefinition::new( + config, + output_directory, + output_file, + footnote, + )?); + } + + ret + }; + let ret = RenderBlogPostPage::new( global_settings, page.title.clone(), Some(link_to_blog_post), children, + footnotes, ); Ok(ret) } diff --git a/src/intermediate/definition.rs b/src/intermediate/definition.rs index 2f62ca7..4b58d8e 100644 --- a/src/intermediate/definition.rs +++ b/src/intermediate/definition.rs @@ -1,5 +1,7 @@ use std::path::Path; use std::path::PathBuf; +use std::sync::Arc; +use std::sync::Mutex; use tokio::task::JoinHandle; use walkdir::WalkDir; @@ -43,31 +45,26 @@ impl BlogPost { ret }; - let mut registry = Registry::new(); - - // Assign IDs to the targets - for (_real_path, _contents, parsed_document) in parsed_org_files.iter() { - organic::types::AstNode::from(parsed_document) - .iter_all_ast_nodes() - .for_each(|node| match node { - organic::types::AstNode::Target(target) => { - registry.get_target(target.value); - } - _ => {} - }); - } - let pages = { let mut ret = Vec::new(); for (real_path, _contents, parsed_document) in parsed_org_files.iter() { + let mut registry = Registry::new(); + + // Assign IDs to the targets + organic::types::AstNode::from(parsed_document) + .iter_all_ast_nodes() + .for_each(|node| match node { + organic::types::AstNode::Target(target) => { + registry.get_target(target.value); + } + _ => {} + }); + + let registry = Arc::new(Mutex::new(registry)); let relative_to_post_dir_path = real_path.strip_prefix(post_dir)?; ret.push( - BlogPostPage::new( - relative_to_post_dir_path, - &mut registry, - parsed_document, - ) - .await?, + BlogPostPage::new(relative_to_post_dir_path, registry, parsed_document) + .await?, ); } ret diff --git a/src/intermediate/diary_sexp.rs b/src/intermediate/diary_sexp.rs index 5f4d4a4..ebc7b3b 100644 --- a/src/intermediate/diary_sexp.rs +++ b/src/intermediate/diary_sexp.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct IDiarySexp {} - -impl IDiarySexp { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::DiarySexp<'parse>, - ) -> Result { - Ok(IDiarySexp {}) - } -} +inoop!(IDiarySexp, DiarySexp); diff --git a/src/intermediate/document_element.rs b/src/intermediate/document_element.rs index d348269..8f6b608 100644 --- a/src/intermediate/document_element.rs +++ b/src/intermediate/document_element.rs @@ -1,31 +1,30 @@ -use crate::error::CustomError; - +use super::macros::iitem; +use super::macros::iselector; use super::registry::Registry; use super::IHeading; use super::ISection; +use crate::error::CustomError; use futures::future::{BoxFuture, FutureExt}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) enum IDocumentElement { Heading(IHeading), Section(ISection), } -impl IDocumentElement { - pub(crate) fn new<'parse, 'b>( - registry: &'b mut Registry<'parse>, - original: &'b organic::types::DocumentElement<'parse>, - ) -> BoxFuture<'b, Result> { - async move { - match original { - organic::types::DocumentElement::Heading(inner) => Ok(IDocumentElement::Heading( - IHeading::new(registry, inner).await?, - )), - organic::types::DocumentElement::Section(inner) => Ok(IDocumentElement::Section( - ISection::new(registry, inner).await?, - )), - } - } - .boxed() - } -} +iselector!(IDocumentElement, DocumentElement, original, registry, { + iitem!( + registry, + original, + ( + organic::types::DocumentElement::Heading, + IDocumentElement::Heading, + IHeading + ), + ( + organic::types::DocumentElement::Section, + IDocumentElement::Section, + ISection + ), + ) +}); diff --git a/src/intermediate/drawer.rs b/src/intermediate/drawer.rs index b55fd71..4a1c0a3 100644 --- a/src/intermediate/drawer.rs +++ b/src/intermediate/drawer.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct IDrawer {} - -impl IDrawer { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::Drawer<'parse>, - ) -> Result { - Ok(IDrawer {}) - } -} +inoop!(IDrawer, Drawer); diff --git a/src/intermediate/dynamic_block.rs b/src/intermediate/dynamic_block.rs index 6086e9d..fc8cbaf 100644 --- a/src/intermediate/dynamic_block.rs +++ b/src/intermediate/dynamic_block.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct IDynamicBlock {} - -impl IDynamicBlock { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::DynamicBlock<'parse>, - ) -> Result { - Ok(IDynamicBlock {}) - } -} +inoop!(IDynamicBlock, DynamicBlock); diff --git a/src/intermediate/element.rs b/src/intermediate/element.rs index 1fe3360..1e1f2ae 100644 --- a/src/intermediate/element.rs +++ b/src/intermediate/element.rs @@ -1,7 +1,6 @@ -use crate::error::CustomError; - use super::comment::IComment; use super::keyword::IKeyword; +use super::macros::iselector; use super::registry::Registry; use super::IBabelCall; use super::ICenterBlock; @@ -25,9 +24,11 @@ use super::ISpecialBlock; use super::ISrcBlock; use super::ITable; use super::IVerseBlock; +use crate::error::CustomError; +use crate::intermediate::macros::iitem; use futures::future::{BoxFuture, FutureExt}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) enum IElement { Paragraph(IParagraph), PlainList(IPlainList), @@ -55,87 +56,117 @@ pub(crate) enum IElement { LatexEnvironment(ILatexEnvironment), } -impl IElement { - pub(crate) fn new<'parse, 'b>( - registry: &'b mut Registry<'parse>, - elem: &'b organic::types::Element<'parse>, - ) -> BoxFuture<'b, Result> { - async move { - match elem { - organic::types::Element::Paragraph(inner) => { - Ok(IElement::Paragraph(IParagraph::new(registry, inner).await?)) - } - organic::types::Element::PlainList(inner) => { - Ok(IElement::PlainList(IPlainList::new(registry, inner).await?)) - } - organic::types::Element::CenterBlock(inner) => Ok(IElement::CenterBlock( - ICenterBlock::new(registry, inner).await?, - )), - organic::types::Element::QuoteBlock(inner) => Ok(IElement::QuoteBlock( - IQuoteBlock::new(registry, inner).await?, - )), - organic::types::Element::SpecialBlock(inner) => Ok(IElement::SpecialBlock( - ISpecialBlock::new(registry, inner).await?, - )), - organic::types::Element::DynamicBlock(inner) => Ok(IElement::DynamicBlock( - IDynamicBlock::new(registry, inner).await?, - )), - organic::types::Element::FootnoteDefinition(inner) => Ok( - IElement::FootnoteDefinition(IFootnoteDefinition::new(registry, inner).await?), - ), - organic::types::Element::Comment(inner) => { - Ok(IElement::Comment(IComment::new(registry, inner).await?)) - } - organic::types::Element::Drawer(inner) => { - Ok(IElement::Drawer(IDrawer::new(registry, inner).await?)) - } - organic::types::Element::PropertyDrawer(inner) => Ok(IElement::PropertyDrawer( - IPropertyDrawer::new(registry, inner).await?, - )), - organic::types::Element::Table(inner) => { - Ok(IElement::Table(ITable::new(registry, inner).await?)) - } - organic::types::Element::VerseBlock(inner) => Ok(IElement::VerseBlock( - IVerseBlock::new(registry, inner).await?, - )), - organic::types::Element::CommentBlock(inner) => Ok(IElement::CommentBlock( - ICommentBlock::new(registry, inner).await?, - )), - organic::types::Element::ExampleBlock(inner) => Ok(IElement::ExampleBlock( - IExampleBlock::new(registry, inner).await?, - )), - organic::types::Element::ExportBlock(inner) => Ok(IElement::ExportBlock( - IExportBlock::new(registry, inner).await?, - )), - organic::types::Element::SrcBlock(inner) => { - Ok(IElement::SrcBlock(ISrcBlock::new(registry, inner).await?)) - } - organic::types::Element::Clock(inner) => { - Ok(IElement::Clock(IClock::new(registry, inner).await?)) - } - organic::types::Element::DiarySexp(inner) => { - Ok(IElement::DiarySexp(IDiarySexp::new(registry, inner).await?)) - } - organic::types::Element::Planning(inner) => { - Ok(IElement::Planning(IPlanning::new(registry, inner).await?)) - } - organic::types::Element::FixedWidthArea(inner) => Ok(IElement::FixedWidthArea( - IFixedWidthArea::new(registry, inner).await?, - )), - organic::types::Element::HorizontalRule(inner) => Ok(IElement::HorizontalRule( - IHorizontalRule::new(registry, inner).await?, - )), - organic::types::Element::Keyword(inner) => { - Ok(IElement::Keyword(IKeyword::new(registry, inner).await?)) - } - organic::types::Element::BabelCall(inner) => { - Ok(IElement::BabelCall(IBabelCall::new(registry, inner).await?)) - } - organic::types::Element::LatexEnvironment(inner) => Ok(IElement::LatexEnvironment( - ILatexEnvironment::new(registry, inner).await?, - )), - } - } - .boxed() - } -} +iselector!(IElement, Element, original, registry, { + iitem!( + registry, + original, + ( + organic::types::Element::Paragraph, + IElement::Paragraph, + IParagraph + ), + ( + organic::types::Element::PlainList, + IElement::PlainList, + IPlainList + ), + ( + organic::types::Element::CenterBlock, + IElement::CenterBlock, + ICenterBlock + ), + ( + organic::types::Element::QuoteBlock, + IElement::QuoteBlock, + IQuoteBlock + ), + ( + organic::types::Element::SpecialBlock, + IElement::SpecialBlock, + ISpecialBlock + ), + ( + organic::types::Element::DynamicBlock, + IElement::DynamicBlock, + IDynamicBlock + ), + ( + organic::types::Element::FootnoteDefinition, + IElement::FootnoteDefinition, + IFootnoteDefinition + ), + ( + organic::types::Element::Comment, + IElement::Comment, + IComment + ), + (organic::types::Element::Drawer, IElement::Drawer, IDrawer), + ( + organic::types::Element::PropertyDrawer, + IElement::PropertyDrawer, + IPropertyDrawer + ), + (organic::types::Element::Table, IElement::Table, ITable), + ( + organic::types::Element::VerseBlock, + IElement::VerseBlock, + IVerseBlock + ), + ( + organic::types::Element::CommentBlock, + IElement::CommentBlock, + ICommentBlock + ), + ( + organic::types::Element::ExampleBlock, + IElement::ExampleBlock, + IExampleBlock + ), + ( + organic::types::Element::ExportBlock, + IElement::ExportBlock, + IExportBlock + ), + ( + organic::types::Element::SrcBlock, + IElement::SrcBlock, + ISrcBlock + ), + (organic::types::Element::Clock, IElement::Clock, IClock), + ( + organic::types::Element::DiarySexp, + IElement::DiarySexp, + IDiarySexp + ), + ( + organic::types::Element::Planning, + IElement::Planning, + IPlanning + ), + ( + organic::types::Element::FixedWidthArea, + IElement::FixedWidthArea, + IFixedWidthArea + ), + ( + organic::types::Element::HorizontalRule, + IElement::HorizontalRule, + IHorizontalRule + ), + ( + organic::types::Element::Keyword, + IElement::Keyword, + IKeyword + ), + ( + organic::types::Element::BabelCall, + IElement::BabelCall, + IBabelCall + ), + ( + organic::types::Element::LatexEnvironment, + IElement::LatexEnvironment, + ILatexEnvironment + ), + ) +}); diff --git a/src/intermediate/entity.rs b/src/intermediate/entity.rs index 088a8e5..0f47742 100644 --- a/src/intermediate/entity.rs +++ b/src/intermediate/entity.rs @@ -1,19 +1,14 @@ +use super::macros::intermediate; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) struct IEntity { pub(crate) html: String, } -impl IEntity { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::Entity<'parse>, - ) -> Result { - Ok(IEntity { - html: original.html.to_owned(), - }) - } -} +intermediate!(IEntity, Entity, original, registry, { + Ok(IEntity { + html: original.html.to_owned(), + }) +}); diff --git a/src/intermediate/example_block.rs b/src/intermediate/example_block.rs index 2c65752..953c4d5 100644 --- a/src/intermediate/example_block.rs +++ b/src/intermediate/example_block.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct IExampleBlock {} - -impl IExampleBlock { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::ExampleBlock<'parse>, - ) -> Result { - Ok(IExampleBlock {}) - } -} +inoop!(IExampleBlock, ExampleBlock); diff --git a/src/intermediate/export_block.rs b/src/intermediate/export_block.rs index ff24258..2792eda 100644 --- a/src/intermediate/export_block.rs +++ b/src/intermediate/export_block.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct IExportBlock {} - -impl IExportBlock { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::ExportBlock<'parse>, - ) -> Result { - Ok(IExportBlock {}) - } -} +inoop!(IExportBlock, ExportBlock); diff --git a/src/intermediate/export_snippet.rs b/src/intermediate/export_snippet.rs index 6a2c331..7f23280 100644 --- a/src/intermediate/export_snippet.rs +++ b/src/intermediate/export_snippet.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct IExportSnippet {} - -impl IExportSnippet { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::ExportSnippet<'parse>, - ) -> Result { - Ok(IExportSnippet {}) - } -} +inoop!(IExportSnippet, ExportSnippet); diff --git a/src/intermediate/fixed_width_area.rs b/src/intermediate/fixed_width_area.rs index 6cdf376..6bed813 100644 --- a/src/intermediate/fixed_width_area.rs +++ b/src/intermediate/fixed_width_area.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct IFixedWidthArea {} - -impl IFixedWidthArea { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::FixedWidthArea<'parse>, - ) -> Result { - Ok(IFixedWidthArea {}) - } -} +inoop!(IFixedWidthArea, FixedWidthArea); diff --git a/src/intermediate/footnote_definition.rs b/src/intermediate/footnote_definition.rs index 35964bb..4d0ae61 100644 --- a/src/intermediate/footnote_definition.rs +++ b/src/intermediate/footnote_definition.rs @@ -1,15 +1,57 @@ -use crate::error::CustomError; - +use super::macros::intermediate; +use super::registry::register_footnote_definition; use super::registry::Registry; +use super::IAstNode; +use crate::error::CustomError; +use crate::intermediate::RefRegistry; -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) struct IFootnoteDefinition {} -impl IFootnoteDefinition { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::FootnoteDefinition<'parse>, - ) -> Result { +intermediate!( + IFootnoteDefinition, + FootnoteDefinition, + original, + registry, + { + register_footnote_definition(registry, original.label, &original.children).await?; Ok(IFootnoteDefinition {}) } +); + +#[derive(Debug)] +pub(crate) struct IRealFootnoteDefinition { + pub(crate) footnote_id: usize, + pub(crate) contents: Vec, +} + +impl IRealFootnoteDefinition { + pub(crate) async fn new<'orig, 'parse>( + registry: RefRegistry<'orig, 'parse>, + footnote_id: usize, + contents: Vec, + ) -> Result { + Ok(IRealFootnoteDefinition { + footnote_id, + contents, + }) + } + + pub(crate) fn get_display_label(&self) -> String { + format!("{}", self.footnote_id + 1) + } + + /// Get an ID to refer to the first reference to this footnote definition. + /// + /// This ID could, for example, be used for the id attribute in HTML for the reference anchor tag. + pub(crate) fn get_reference_id(&self) -> String { + format!("fnr.{}", self.get_display_label()) + } + + /// Get an ID to refer to the footnote definition. + /// + /// This ID could, for example, be used for the id attribute in HTML for the definition anchor tag. + pub(crate) fn get_definition_id(&self) -> String { + format!("fn.{}", self.get_display_label()) + } } diff --git a/src/intermediate/footnote_reference.rs b/src/intermediate/footnote_reference.rs index cb393c2..aa473d2 100644 --- a/src/intermediate/footnote_reference.rs +++ b/src/intermediate/footnote_reference.rs @@ -1,15 +1,45 @@ +use super::macros::intermediate; +use super::registry::get_footnote_reference_id; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; +#[derive(Debug, Clone)] +pub(crate) struct IFootnoteReference { + footnote_id: usize, + duplicate_offset: usize, +} -#[derive(Debug)] -pub(crate) struct IFootnoteReference {} +intermediate!(IFootnoteReference, FootnoteReference, original, registry, { + let (footnote_id, reference_count) = + get_footnote_reference_id(registry, original.label, &original.definition).await?; + Ok(IFootnoteReference { + footnote_id, + duplicate_offset: reference_count, + }) +}); impl IFootnoteReference { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::FootnoteReference<'parse>, - ) -> Result { - Ok(IFootnoteReference {}) + pub(crate) fn get_display_label(&self) -> String { + format!("{}", self.footnote_id + 1) + } + + /// Get an ID to refer to this footnote reference. + /// + /// This ID could, for example, be used for the id attribute in HTML for the reference anchor tag. + pub(crate) fn get_reference_id(&self) -> String { + if self.duplicate_offset == 0 { + format!("fnr.{}", self.get_display_label()) + } else { + // Org-mode makes all duplicates use "100" but I figure there is no harm in giving each a unique ID. + let append = 100 + self.duplicate_offset - 1; + format!("fnr.{}.{}", self.get_display_label(), append) + } + } + + /// Get an ID to refer to the footnote definition this footnote reference references. + /// + /// This ID could, for example, be used for the id attribute in HTML for the definition anchor tag. + pub(crate) fn get_definition_id(&self) -> String { + format!("fn.{}", self.get_display_label()) } } diff --git a/src/intermediate/heading.rs b/src/intermediate/heading.rs index 4ed510e..716545f 100644 --- a/src/intermediate/heading.rs +++ b/src/intermediate/heading.rs @@ -1,39 +1,34 @@ -use crate::error::CustomError; - +use super::macros::intermediate; use super::registry::Registry; use super::IDocumentElement; use super::IObject; +use crate::error::CustomError; -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) struct IHeading { pub(crate) level: organic::types::HeadlineLevel, pub(crate) title: Vec, pub(crate) children: Vec, } -impl IHeading { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - heading: &organic::types::Heading<'parse>, - ) -> Result { - let title = { - let mut ret = Vec::new(); - for obj in heading.title.iter() { - ret.push(IObject::new(registry, obj).await?); - } - ret - }; - let children = { - let mut ret = Vec::new(); - for obj in heading.children.iter() { - ret.push(IDocumentElement::new(registry, obj).await?); - } - ret - }; - Ok(IHeading { - title, - level: heading.level, - children, - }) - } -} +intermediate!(IHeading, Heading, original, registry, { + let title = { + let mut ret = Vec::new(); + for obj in original.title.iter() { + ret.push(IObject::new(registry.clone(), obj).await?); + } + ret + }; + let children = { + let mut ret = Vec::new(); + for obj in original.children.iter() { + ret.push(IDocumentElement::new(registry.clone(), obj).await?); + } + ret + }; + Ok(IHeading { + title, + level: original.level, + children, + }) +}); diff --git a/src/intermediate/horizontal_rule.rs b/src/intermediate/horizontal_rule.rs index 0be08c6..bc9e323 100644 --- a/src/intermediate/horizontal_rule.rs +++ b/src/intermediate/horizontal_rule.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct IHorizontalRule {} - -impl IHorizontalRule { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::HorizontalRule<'parse>, - ) -> Result { - Ok(IHorizontalRule {}) - } -} +inoop!(IHorizontalRule, HorizontalRule); diff --git a/src/intermediate/inline_babel_call.rs b/src/intermediate/inline_babel_call.rs index 874f5c4..cf1137a 100644 --- a/src/intermediate/inline_babel_call.rs +++ b/src/intermediate/inline_babel_call.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct IInlineBabelCall {} - -impl IInlineBabelCall { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::InlineBabelCall<'parse>, - ) -> Result { - Ok(IInlineBabelCall {}) - } -} +inoop!(IInlineBabelCall, InlineBabelCall); diff --git a/src/intermediate/inline_source_block.rs b/src/intermediate/inline_source_block.rs index 29071b3..700f2fd 100644 --- a/src/intermediate/inline_source_block.rs +++ b/src/intermediate/inline_source_block.rs @@ -1,19 +1,14 @@ +use super::macros::intermediate; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) struct IInlineSourceBlock { pub(crate) value: String, } -impl IInlineSourceBlock { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::InlineSourceBlock<'parse>, - ) -> Result { - Ok(IInlineSourceBlock { - value: original.value.to_owned(), - }) - } -} +intermediate!(IInlineSourceBlock, InlineSourceBlock, original, registry, { + Ok(IInlineSourceBlock { + value: original.value.to_owned(), + }) +}); diff --git a/src/intermediate/italic.rs b/src/intermediate/italic.rs index 79ce541..1c20c55 100644 --- a/src/intermediate/italic.rs +++ b/src/intermediate/italic.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct IItalic {} - -impl IItalic { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::Italic<'parse>, - ) -> Result { - Ok(IItalic {}) - } -} +inoop!(IItalic, Italic); diff --git a/src/intermediate/keyword.rs b/src/intermediate/keyword.rs index bd26939..3f510f3 100644 --- a/src/intermediate/keyword.rs +++ b/src/intermediate/keyword.rs @@ -1,16 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -/// Essentially a no-op since the keyword is not rendered and any relevant impact on other elements is pulled from the parsed form of keyword. -#[derive(Debug)] -pub(crate) struct IKeyword {} - -impl IKeyword { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - keyword: &organic::types::Keyword<'parse>, - ) -> Result { - Ok(IKeyword {}) - } -} +inoop!(IKeyword, Keyword); diff --git a/src/intermediate/latex_environment.rs b/src/intermediate/latex_environment.rs index c6ccaac..361ae3e 100644 --- a/src/intermediate/latex_environment.rs +++ b/src/intermediate/latex_environment.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct ILatexEnvironment {} - -impl ILatexEnvironment { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::LatexEnvironment<'parse>, - ) -> Result { - Ok(ILatexEnvironment {}) - } -} +inoop!(ILatexEnvironment, LatexEnvironment); diff --git a/src/intermediate/latex_fragment.rs b/src/intermediate/latex_fragment.rs index bf8d2af..89aa832 100644 --- a/src/intermediate/latex_fragment.rs +++ b/src/intermediate/latex_fragment.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct ILatexFragment {} - -impl ILatexFragment { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::LatexFragment<'parse>, - ) -> Result { - Ok(ILatexFragment {}) - } -} +inoop!(ILatexFragment, LatexFragment); diff --git a/src/intermediate/line_break.rs b/src/intermediate/line_break.rs index da94610..8436553 100644 --- a/src/intermediate/line_break.rs +++ b/src/intermediate/line_break.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct ILineBreak {} - -impl ILineBreak { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::LineBreak<'parse>, - ) -> Result { - Ok(ILineBreak {}) - } -} +inoop!(ILineBreak, LineBreak); diff --git a/src/intermediate/macros.rs b/src/intermediate/macros.rs new file mode 100644 index 0000000..f7b6236 --- /dev/null +++ b/src/intermediate/macros.rs @@ -0,0 +1,74 @@ +/// Write the implementation for the intermediate ast node for a type that is a noop or is not yet implemented. +/// +/// This exists to make changing the type signature easier. +macro_rules! inoop { + ($istruct:ident, $pstruct:ident) => { + #[derive(Debug, Clone)] + pub(crate) struct $istruct {} + + impl $istruct { + pub(crate) async fn new<'reg, 'orig, 'parse>( + registry: crate::intermediate::RefRegistry<'orig, 'parse>, + original: &'orig organic::types::$pstruct<'parse>, + ) -> Result<$istruct, CustomError> { + Ok($istruct {}) + } + } + }; +} + +pub(crate) use inoop; + +/// Write the implementation for the intermediate ast node. +/// +/// This exists to make changing the type signature easier. +macro_rules! intermediate { + ($istruct:ident, $pstruct:ident, $original:ident, $registry:ident, $fnbody:tt) => { + impl $istruct { + pub(crate) async fn new<'orig, 'parse>( + registry: crate::intermediate::RefRegistry<'orig, 'parse>, + original: &'orig organic::types::$pstruct<'parse>, + ) -> Result<$istruct, CustomError> { + let $original = original; + let $registry = registry; + $fnbody + } + } + }; +} + +pub(crate) use intermediate; + +/// Write the implementation for the intermediate ast node. +/// +/// This exists to make changing the type signature easier. +macro_rules! iselector { + ($istruct:ident, $pstruct:ident, $original:ident, $registry:ident, $fnbody:tt) => { + impl $istruct { + pub(crate) fn new<'orig, 'parse>( + registry: crate::intermediate::RefRegistry<'orig, 'parse>, + original: &'orig organic::types::$pstruct<'parse>, + ) -> BoxFuture<'orig, Result<$istruct, CustomError>> { + let $registry = registry; + let $original = original; + async move { $fnbody }.boxed() + } + } + }; +} + +pub(crate) use iselector; + +macro_rules! iitem { + ($registry:expr, $original:expr, $(($penum:path, $ienum:path, $istruct:ident),)*) => { + match $original { +$( + $penum(inner) => Ok($ienum( + $istruct::new($registry.clone(), inner).await?, + )), +)* + } + }; +} + +pub(crate) use iitem; diff --git a/src/intermediate/mod.rs b/src/intermediate/mod.rs index ac1f411..f228fdb 100644 --- a/src/intermediate/mod.rs +++ b/src/intermediate/mod.rs @@ -1,4 +1,5 @@ mod angle_link; +mod ast_node; mod babel_call; mod bold; mod center_block; @@ -31,6 +32,7 @@ mod keyword; mod latex_environment; mod latex_fragment; mod line_break; +mod macros; mod object; mod org_macro; mod page; @@ -61,6 +63,7 @@ mod util; mod verbatim; mod verse_block; pub(crate) use angle_link::IAngleLink; +pub(crate) use ast_node::IAstNode; pub(crate) use babel_call::IBabelCall; pub(crate) use bold::IBold; pub(crate) use center_block::ICenterBlock; @@ -83,6 +86,7 @@ pub(crate) use export_block::IExportBlock; pub(crate) use export_snippet::IExportSnippet; pub(crate) use fixed_width_area::IFixedWidthArea; pub(crate) use footnote_definition::IFootnoteDefinition; +pub(crate) use footnote_definition::IRealFootnoteDefinition; pub(crate) use footnote_reference::IFootnoteReference; pub(crate) use heading::IHeading; pub(crate) use horizontal_rule::IHorizontalRule; @@ -120,3 +124,6 @@ pub(crate) use timestamp::ITimestamp; pub(crate) use underline::IUnderline; pub(crate) use verbatim::IVerbatim; pub(crate) use verse_block::IVerseBlock; + +pub(crate) type RefRegistry<'orig, 'parse> = + std::sync::Arc>>; diff --git a/src/intermediate/object.rs b/src/intermediate/object.rs index c51584a..a0b05a2 100644 --- a/src/intermediate/object.rs +++ b/src/intermediate/object.rs @@ -1,4 +1,5 @@ use crate::error::CustomError; +use crate::intermediate::macros::iitem; use super::angle_link::IAngleLink; use super::bold::IBold; @@ -13,6 +14,7 @@ use super::inline_source_block::IInlineSourceBlock; use super::italic::IItalic; use super::latex_fragment::ILatexFragment; use super::line_break::ILineBreak; +use super::macros::iselector; use super::org_macro::IOrgMacro; use super::plain_link::IPlainLink; use super::plain_text::IPlainText; @@ -30,7 +32,7 @@ use super::verbatim::IVerbatim; use super::ITarget; use futures::future::{BoxFuture, FutureExt}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) enum IObject { Bold(IBold), Italic(IItalic), @@ -61,96 +63,124 @@ pub(crate) enum IObject { Timestamp(ITimestamp), } -impl IObject { - pub(crate) fn new<'parse, 'b>( - registry: &'b mut Registry<'parse>, - obj: &'b organic::types::Object<'parse>, - ) -> BoxFuture<'b, Result> { - async move { - match obj { - organic::types::Object::Bold(inner) => { - Ok(IObject::Bold(IBold::new(registry, inner).await?)) - } - organic::types::Object::Italic(inner) => { - Ok(IObject::Italic(IItalic::new(registry, inner).await?)) - } - organic::types::Object::Underline(inner) => { - Ok(IObject::Underline(IUnderline::new(registry, inner).await?)) - } - organic::types::Object::StrikeThrough(inner) => Ok(IObject::StrikeThrough( - IStrikeThrough::new(registry, inner).await?, - )), - organic::types::Object::Code(inner) => { - Ok(IObject::Code(ICode::new(registry, inner).await?)) - } - organic::types::Object::Verbatim(inner) => { - Ok(IObject::Verbatim(IVerbatim::new(registry, inner).await?)) - } - organic::types::Object::PlainText(inner) => { - Ok(IObject::PlainText(IPlainText::new(registry, inner).await?)) - } - organic::types::Object::RegularLink(inner) => Ok(IObject::RegularLink( - IRegularLink::new(registry, inner).await?, - )), - organic::types::Object::RadioLink(inner) => { - Ok(IObject::RadioLink(IRadioLink::new(registry, inner).await?)) - } - organic::types::Object::RadioTarget(inner) => Ok(IObject::RadioTarget( - IRadioTarget::new(registry, inner).await?, - )), - organic::types::Object::PlainLink(inner) => { - Ok(IObject::PlainLink(IPlainLink::new(registry, inner).await?)) - } - organic::types::Object::AngleLink(inner) => { - Ok(IObject::AngleLink(IAngleLink::new(registry, inner).await?)) - } - organic::types::Object::OrgMacro(inner) => { - Ok(IObject::OrgMacro(IOrgMacro::new(registry, inner).await?)) - } - organic::types::Object::Entity(inner) => { - Ok(IObject::Entity(IEntity::new(registry, inner).await?)) - } - organic::types::Object::LatexFragment(inner) => Ok(IObject::LatexFragment( - ILatexFragment::new(registry, inner).await?, - )), - organic::types::Object::ExportSnippet(inner) => Ok(IObject::ExportSnippet( - IExportSnippet::new(registry, inner).await?, - )), - organic::types::Object::FootnoteReference(inner) => Ok(IObject::FootnoteReference( - IFootnoteReference::new(registry, inner).await?, - )), - organic::types::Object::Citation(inner) => { - Ok(IObject::Citation(ICitation::new(registry, inner).await?)) - } - organic::types::Object::CitationReference(inner) => Ok(IObject::CitationReference( - ICitationReference::new(registry, inner).await?, - )), - organic::types::Object::InlineBabelCall(inner) => Ok(IObject::InlineBabelCall( - IInlineBabelCall::new(registry, inner).await?, - )), - organic::types::Object::InlineSourceBlock(inner) => Ok(IObject::InlineSourceBlock( - IInlineSourceBlock::new(registry, inner).await?, - )), - organic::types::Object::LineBreak(inner) => { - Ok(IObject::LineBreak(ILineBreak::new(registry, inner).await?)) - } - organic::types::Object::Target(inner) => { - Ok(IObject::Target(ITarget::new(registry, inner).await?)) - } - organic::types::Object::StatisticsCookie(inner) => Ok(IObject::StatisticsCookie( - IStatisticsCookie::new(registry, inner).await?, - )), - organic::types::Object::Subscript(inner) => { - Ok(IObject::Subscript(ISubscript::new(registry, inner).await?)) - } - organic::types::Object::Superscript(inner) => Ok(IObject::Superscript( - ISuperscript::new(registry, inner).await?, - )), - organic::types::Object::Timestamp(inner) => { - Ok(IObject::Timestamp(ITimestamp::new(registry, inner).await?)) - } - } - } - .boxed() - } -} +iselector!(IObject, Object, original, registry, { + iitem!( + registry, + original, + (organic::types::Object::Bold, IObject::Bold, IBold), + (organic::types::Object::Italic, IObject::Italic, IItalic), + ( + organic::types::Object::Underline, + IObject::Underline, + IUnderline + ), + ( + organic::types::Object::StrikeThrough, + IObject::StrikeThrough, + IStrikeThrough + ), + (organic::types::Object::Code, IObject::Code, ICode), + ( + organic::types::Object::Verbatim, + IObject::Verbatim, + IVerbatim + ), + ( + organic::types::Object::PlainText, + IObject::PlainText, + IPlainText + ), + ( + organic::types::Object::RegularLink, + IObject::RegularLink, + IRegularLink + ), + ( + organic::types::Object::RadioLink, + IObject::RadioLink, + IRadioLink + ), + ( + organic::types::Object::RadioTarget, + IObject::RadioTarget, + IRadioTarget + ), + ( + organic::types::Object::PlainLink, + IObject::PlainLink, + IPlainLink + ), + ( + organic::types::Object::AngleLink, + IObject::AngleLink, + IAngleLink + ), + ( + organic::types::Object::OrgMacro, + IObject::OrgMacro, + IOrgMacro + ), + (organic::types::Object::Entity, IObject::Entity, IEntity), + ( + organic::types::Object::LatexFragment, + IObject::LatexFragment, + ILatexFragment + ), + ( + organic::types::Object::ExportSnippet, + IObject::ExportSnippet, + IExportSnippet + ), + ( + organic::types::Object::FootnoteReference, + IObject::FootnoteReference, + IFootnoteReference + ), + ( + organic::types::Object::Citation, + IObject::Citation, + ICitation + ), + ( + organic::types::Object::CitationReference, + IObject::CitationReference, + ICitationReference + ), + ( + organic::types::Object::InlineBabelCall, + IObject::InlineBabelCall, + IInlineBabelCall + ), + ( + organic::types::Object::InlineSourceBlock, + IObject::InlineSourceBlock, + IInlineSourceBlock + ), + ( + organic::types::Object::LineBreak, + IObject::LineBreak, + ILineBreak + ), + (organic::types::Object::Target, IObject::Target, ITarget), + ( + organic::types::Object::StatisticsCookie, + IObject::StatisticsCookie, + IStatisticsCookie + ), + ( + organic::types::Object::Subscript, + IObject::Subscript, + ISubscript + ), + ( + organic::types::Object::Superscript, + IObject::Superscript, + ISuperscript + ), + ( + organic::types::Object::Timestamp, + IObject::Timestamp, + ITimestamp + ), + ) +}); diff --git a/src/intermediate/org_macro.rs b/src/intermediate/org_macro.rs index 328d89a..bae7384 100644 --- a/src/intermediate/org_macro.rs +++ b/src/intermediate/org_macro.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct IOrgMacro {} - -impl IOrgMacro { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::OrgMacro<'parse>, - ) -> Result { - Ok(IOrgMacro {}) - } -} +inoop!(IOrgMacro, OrgMacro); diff --git a/src/intermediate/page.rs b/src/intermediate/page.rs index c512d46..404852e 100644 --- a/src/intermediate/page.rs +++ b/src/intermediate/page.rs @@ -2,10 +2,12 @@ use std::path::PathBuf; use crate::error::CustomError; +use super::footnote_definition::IRealFootnoteDefinition; use super::registry::Registry; use super::IDocumentElement; use super::IHeading; use super::ISection; +use super::RefRegistry; #[derive(Debug)] pub(crate) struct BlogPostPage { @@ -15,31 +17,51 @@ pub(crate) struct BlogPostPage { pub(crate) title: Option, pub(crate) children: Vec, + + pub(crate) footnotes: Vec, } impl BlogPostPage { - pub(crate) async fn new<'parse, P: Into>( + // TODO: Move path into the registry so I can give this a standard interface like the others. + pub(crate) async fn new<'a, 'b, 'parse, P: Into>( path: P, - registry: &mut Registry<'parse>, - document: &organic::types::Document<'parse>, + registry: RefRegistry<'b, 'parse>, + document: &'b organic::types::Document<'parse>, ) -> Result { let path = path.into(); let mut children = Vec::new(); if let Some(section) = document.zeroth_section.as_ref() { children.push(IDocumentElement::Section( - ISection::new(registry, section).await?, + ISection::new(registry.clone(), section).await?, )); } for heading in document.children.iter() { children.push(IDocumentElement::Heading( - IHeading::new(registry, heading).await?, + IHeading::new(registry.clone(), heading).await?, )); } + let footnotes = { + let footnote_definitions: Vec<_> = { + let registry = registry.lock().unwrap(); + let ret = registry + .get_footnote_ids() + .map(|(id, def)| (id, def.clone())) + .collect(); + ret + }; + let mut ret = Vec::new(); + for (id, def) in footnote_definitions.into_iter() { + ret.push(IRealFootnoteDefinition::new(registry.clone(), id, def).await?); + } + ret + }; + Ok(BlogPostPage { path, title: get_title(&document), children, + footnotes, }) } diff --git a/src/intermediate/paragraph.rs b/src/intermediate/paragraph.rs index 09cf1be..675f22e 100644 --- a/src/intermediate/paragraph.rs +++ b/src/intermediate/paragraph.rs @@ -1,26 +1,21 @@ -use crate::error::CustomError; - +use super::macros::intermediate; use super::registry::Registry; use super::IObject; +use crate::error::CustomError; -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) struct IParagraph { pub(crate) children: Vec, } -impl IParagraph { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - paragraph: &organic::types::Paragraph<'parse>, - ) -> Result { - let children = { - let mut ret = Vec::new(); - for obj in paragraph.children.iter() { - ret.push(IObject::new(registry, obj).await?); - } - ret - }; +intermediate!(IParagraph, Paragraph, original, registry, { + let children = { + let mut ret = Vec::new(); + for obj in original.children.iter() { + ret.push(IObject::new(registry.clone(), obj).await?); + } + ret + }; - Ok(IParagraph { children }) - } -} + Ok(IParagraph { children }) +}); diff --git a/src/intermediate/plain_link.rs b/src/intermediate/plain_link.rs index 8e702ed..68d0861 100644 --- a/src/intermediate/plain_link.rs +++ b/src/intermediate/plain_link.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct IPlainLink {} - -impl IPlainLink { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::PlainLink<'parse>, - ) -> Result { - Ok(IPlainLink {}) - } -} +inoop!(IPlainLink, PlainLink); diff --git a/src/intermediate/plain_list.rs b/src/intermediate/plain_list.rs index 2065403..5a4e62b 100644 --- a/src/intermediate/plain_list.rs +++ b/src/intermediate/plain_list.rs @@ -1,30 +1,25 @@ -use crate::error::CustomError; - +use super::macros::intermediate; use super::registry::Registry; use super::IPlainListItem; +use crate::error::CustomError; -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) struct IPlainList { pub(crate) list_type: organic::types::PlainListType, pub(crate) children: Vec, } -impl IPlainList { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - plain_list: &organic::types::PlainList<'parse>, - ) -> Result { - let children = { - let mut ret = Vec::new(); - for obj in plain_list.children.iter() { - ret.push(IPlainListItem::new(registry, obj).await?); - } - ret - }; +intermediate!(IPlainList, PlainList, original, registry, { + let children = { + let mut ret = Vec::new(); + for obj in original.children.iter() { + ret.push(IPlainListItem::new(registry.clone(), obj).await?); + } + ret + }; - Ok(IPlainList { - list_type: plain_list.list_type, - children, - }) - } -} + Ok(IPlainList { + list_type: original.list_type, + children, + }) +}); diff --git a/src/intermediate/plain_list_item.rs b/src/intermediate/plain_list_item.rs index 8a77b9a..8bb03da 100644 --- a/src/intermediate/plain_list_item.rs +++ b/src/intermediate/plain_list_item.rs @@ -1,36 +1,31 @@ -use crate::error::CustomError; - +use super::macros::intermediate; use super::registry::Registry; use super::IElement; use super::IObject; +use crate::error::CustomError; -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) struct IPlainListItem { pub(crate) tag: Vec, pub(crate) children: Vec, } -impl IPlainListItem { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - plain_list_item: &organic::types::PlainListItem<'parse>, - ) -> Result { - let tag = { - let mut ret = Vec::new(); - for obj in plain_list_item.tag.iter() { - ret.push(IObject::new(registry, obj).await?); - } - ret - }; +intermediate!(IPlainListItem, PlainListItem, original, registry, { + let tag = { + let mut ret = Vec::new(); + for obj in original.tag.iter() { + ret.push(IObject::new(registry.clone(), obj).await?); + } + ret + }; - let children = { - let mut ret = Vec::new(); - for elem in plain_list_item.children.iter() { - ret.push(IElement::new(registry, elem).await?); - } - ret - }; + let children = { + let mut ret = Vec::new(); + for elem in original.children.iter() { + ret.push(IElement::new(registry.clone(), elem).await?); + } + ret + }; - Ok(IPlainListItem { tag, children }) - } -} + Ok(IPlainListItem { tag, children }) +}); diff --git a/src/intermediate/plain_text.rs b/src/intermediate/plain_text.rs index d8d5558..1dceef4 100644 --- a/src/intermediate/plain_text.rs +++ b/src/intermediate/plain_text.rs @@ -1,20 +1,15 @@ -use crate::error::CustomError; -use crate::intermediate::util::coalesce_whitespace; - +use super::macros::intermediate; use super::registry::Registry; +use super::util::coalesce_whitespace; +use crate::error::CustomError; -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) struct IPlainText { pub(crate) source: String, } -impl IPlainText { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - plain_text: &organic::types::PlainText<'parse>, - ) -> Result { - Ok(IPlainText { - source: coalesce_whitespace(plain_text.source).into_owned(), - }) - } -} +intermediate!(IPlainText, PlainText, original, registry, { + Ok(IPlainText { + source: coalesce_whitespace(original.source).into_owned(), + }) +}); diff --git a/src/intermediate/planning.rs b/src/intermediate/planning.rs index d34c607..7de9746 100644 --- a/src/intermediate/planning.rs +++ b/src/intermediate/planning.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct IPlanning {} - -impl IPlanning { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::Planning<'parse>, - ) -> Result { - Ok(IPlanning {}) - } -} +inoop!(IPlanning, Planning); diff --git a/src/intermediate/property_drawer.rs b/src/intermediate/property_drawer.rs index 422ab0d..9a42001 100644 --- a/src/intermediate/property_drawer.rs +++ b/src/intermediate/property_drawer.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct IPropertyDrawer {} - -impl IPropertyDrawer { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::PropertyDrawer<'parse>, - ) -> Result { - Ok(IPropertyDrawer {}) - } -} +inoop!(IPropertyDrawer, PropertyDrawer); diff --git a/src/intermediate/quote_block.rs b/src/intermediate/quote_block.rs index 7d471b2..82ad4ed 100644 --- a/src/intermediate/quote_block.rs +++ b/src/intermediate/quote_block.rs @@ -1,26 +1,21 @@ -use crate::error::CustomError; - +use super::macros::intermediate; use super::registry::Registry; use super::IElement; +use crate::error::CustomError; -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) struct IQuoteBlock { pub(crate) children: Vec, } -impl IQuoteBlock { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::QuoteBlock<'parse>, - ) -> Result { - let children = { - let mut ret = Vec::new(); - for obj in original.children.iter() { - ret.push(IElement::new(registry, obj).await?); - } - ret - }; +intermediate!(IQuoteBlock, QuoteBlock, original, registry, { + let children = { + let mut ret = Vec::new(); + for obj in original.children.iter() { + ret.push(IElement::new(registry.clone(), obj).await?); + } + ret + }; - Ok(IQuoteBlock { children }) - } -} + Ok(IQuoteBlock { children }) +}); diff --git a/src/intermediate/radio_link.rs b/src/intermediate/radio_link.rs index 6886169..6dfea17 100644 --- a/src/intermediate/radio_link.rs +++ b/src/intermediate/radio_link.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct IRadioLink {} - -impl IRadioLink { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::RadioLink<'parse>, - ) -> Result { - Ok(IRadioLink {}) - } -} +inoop!(IRadioLink, RadioLink); diff --git a/src/intermediate/radio_target.rs b/src/intermediate/radio_target.rs index 4077c29..01b20e9 100644 --- a/src/intermediate/radio_target.rs +++ b/src/intermediate/radio_target.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct IRadioTarget {} - -impl IRadioTarget { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::RadioTarget<'parse>, - ) -> Result { - Ok(IRadioTarget {}) - } -} +inoop!(IRadioTarget, RadioTarget); diff --git a/src/intermediate/registry.rs b/src/intermediate/registry.rs index d53e2ba..3ae276e 100644 --- a/src/intermediate/registry.rs +++ b/src/intermediate/registry.rs @@ -1,24 +1,201 @@ +use crate::error::CustomError; +use organic::types::Element; +use organic::types::Object; use std::collections::HashMap; +use super::ast_node::IAstNode; +use super::ast_node::IntoIAstNode; +use super::RefRegistry; + type IdCounter = u16; -pub(crate) struct Registry<'parse> { +pub(crate) struct Registry<'orig, 'parse> { id_counter: IdCounter, targets: HashMap<&'parse str, String>, + footnote_ids: Vec<(Option<&'parse str>, Vec)>, + footnote_reference_counts: HashMap<&'parse str, usize>, + on_deck_footnote_ids: HashMap<&'parse str, &'orig Vec>>, } -impl<'parse> Registry<'parse> { - pub(crate) fn new() -> Registry<'parse> { +impl<'orig, 'parse> Registry<'orig, 'parse> { + pub(crate) fn new() -> Registry<'orig, 'parse> { Registry { id_counter: 0, targets: HashMap::new(), + footnote_ids: Vec::new(), + footnote_reference_counts: HashMap::new(), + on_deck_footnote_ids: HashMap::new(), } } - pub(crate) fn get_target<'b>(&'b mut self, body: &'parse str) -> &'b String { + 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) }) } + + pub(crate) fn get_footnote_ids(&self) -> impl Iterator)> { + self.footnote_ids + .iter() + .map(|(_label, definition)| definition) + .enumerate() + } +} + +/// 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>( + registry: RefRegistry<'orig, 'parse>, + label: Option<&'parse str>, + definition: &'orig Vec>, +) -> Result<(usize, usize), CustomError> { + if let None = label { + // If it has no label then it must always get a new ID. + let contents = convert_reference_contents(registry.clone(), definition).await?; + let pos = { + let mut registry = registry.lock().unwrap(); + registry.footnote_ids.push((None, contents)); + registry.footnote_ids.len() - 1 + }; + return Ok((pos, 0)); + } + + let reference_count = if let Some(label) = label { + promote_footnote_definition(registry.clone(), label).await?; + let mut registry = registry.lock().unwrap(); + let reference_count = registry + .footnote_reference_counts + .entry(label) + .and_modify(|count| *count += 1) + .or_insert(0); + *reference_count + } else { + 0 + }; + + let existing_index = 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(registry.clone(), definition).await?; + let mut registry = registry.lock().unwrap(); + let entry = registry + .footnote_ids + .get_mut(existing_id) + .expect("If-statement proves this to be Some."); + entry.1 = contents; + } + Ok((existing_id, reference_count)) + } else { + let existing_id = { + let mut registry = registry.lock().unwrap(); + registry.footnote_ids.push((label, Vec::new())); + registry.footnote_ids.len() - 1 + }; + let contents = convert_reference_contents(registry.clone(), definition).await?; + { + let mut registry = registry.lock().unwrap(); + let entry = registry + .footnote_ids + .get_mut(existing_id) + .expect("If-statement proves this to be Some."); + entry.1 = contents; + } + Ok((existing_id, reference_count)) + } +} + +/// Update the definition to a footnote but do not mark it as referenced. +pub(crate) async fn register_footnote_definition<'orig, 'parse>( + registry: RefRegistry<'orig, 'parse>, + label: &'parse str, + definition: &'orig Vec>, +) -> Result<(), CustomError> { + let has_existing: bool = { + let mut registry = registry.lock().unwrap(); + registry + .footnote_ids + .iter_mut() + .any(|(id, _definition)| *id == Some(label)) + }; + if !has_existing { + let mut registry = registry.lock().unwrap(); + registry.on_deck_footnote_ids.insert(label, definition); + return Ok(()); + } + let contents = convert_definition_contents(registry.clone(), definition).await?; + let mut registry = registry.lock().unwrap(); + if let Some((_existing_id, existing_definition)) = registry + .footnote_ids + .iter_mut() + .find(|(id, _definition)| *id == Some(label)) + { + *existing_definition = contents; + } + Ok(()) +} + +async fn convert_reference_contents<'orig, 'parse>( + registry: RefRegistry<'orig, 'parse>, + contents: &'orig Vec>, +) -> Result, CustomError> { + let contents = { + let mut ret = Vec::new(); + for obj in contents.iter() { + ret.push(obj.into_ast_node(registry.clone()).await?); + } + ret + }; + + Ok(contents) +} + +async fn convert_definition_contents<'orig, 'parse>( + registry: RefRegistry<'orig, 'parse>, + contents: &'orig Vec>, +) -> Result, CustomError> { + let contents = { + let mut ret = Vec::new(); + for obj in contents.iter() { + ret.push(obj.into_ast_node(registry.clone()).await?); + } + ret + }; + + Ok(contents) +} + +/// 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>( + registry: RefRegistry<'orig, 'parse>, + label: &'parse str, +) -> Result<(), CustomError> { + let definition = { + let mut registry = registry.lock().unwrap(); + let definition = registry.on_deck_footnote_ids.remove(label); + definition + }; + if let Some(elements) = definition { + let existing_id = { + let mut registry = registry.lock().unwrap(); + registry.footnote_ids.push((Some(label), Vec::new())); + registry.footnote_ids.len() - 1 + }; + let contents = convert_definition_contents(registry.clone(), elements).await?; + { + let mut registry = registry.lock().unwrap(); + let entry = registry + .footnote_ids + .get_mut(existing_id) + .expect("If-statement proves this to be Some."); + entry.1 = contents; + } + } + Ok(()) } diff --git a/src/intermediate/regular_link.rs b/src/intermediate/regular_link.rs index af1413c..2e85bd7 100644 --- a/src/intermediate/regular_link.rs +++ b/src/intermediate/regular_link.rs @@ -1,29 +1,24 @@ -use crate::error::CustomError; - +use super::macros::intermediate; use super::registry::Registry; use super::IObject; +use crate::error::CustomError; -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) struct IRegularLink { pub(crate) raw_link: String, pub(crate) children: Vec, } -impl IRegularLink { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::RegularLink<'parse>, - ) -> Result { - let children = { - let mut ret = Vec::new(); - for obj in original.children.iter() { - ret.push(IObject::new(registry, obj).await?); - } - ret - }; - Ok(IRegularLink { - raw_link: original.get_raw_link().into_owned(), - children, - }) - } -} +intermediate!(IRegularLink, RegularLink, original, registry, { + let children = { + let mut ret = Vec::new(); + for obj in original.children.iter() { + ret.push(IObject::new(registry.clone(), obj).await?); + } + ret + }; + Ok(IRegularLink { + raw_link: original.get_raw_link().into_owned(), + children, + }) +}); diff --git a/src/intermediate/section.rs b/src/intermediate/section.rs index c9426e6..d095620 100644 --- a/src/intermediate/section.rs +++ b/src/intermediate/section.rs @@ -1,26 +1,21 @@ -use crate::error::CustomError; - +use super::macros::intermediate; use super::registry::Registry; use super::IElement; +use crate::error::CustomError; -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) struct ISection { pub(crate) children: Vec, } -impl ISection { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - section: &organic::types::Section<'parse>, - ) -> Result { - let children = { - let mut ret = Vec::new(); - for elem in section.children.iter() { - ret.push(IElement::new(registry, elem).await?); - } - ret - }; +intermediate!(ISection, Section, original, registry, { + let children = { + let mut ret = Vec::new(); + for elem in original.children.iter() { + ret.push(IElement::new(registry.clone(), elem).await?); + } + ret + }; - Ok(ISection { children }) - } -} + Ok(ISection { children }) +}); diff --git a/src/intermediate/special_block.rs b/src/intermediate/special_block.rs index 252c347..2109242 100644 --- a/src/intermediate/special_block.rs +++ b/src/intermediate/special_block.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct ISpecialBlock {} - -impl ISpecialBlock { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::SpecialBlock<'parse>, - ) -> Result { - Ok(ISpecialBlock {}) - } -} +inoop!(ISpecialBlock, SpecialBlock); diff --git a/src/intermediate/src_block.rs b/src/intermediate/src_block.rs index c54dc4a..e780497 100644 --- a/src/intermediate/src_block.rs +++ b/src/intermediate/src_block.rs @@ -1,22 +1,17 @@ +use super::macros::intermediate; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) struct ISrcBlock { pub(crate) lines: Vec, } -impl ISrcBlock { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::SrcBlock<'parse>, - ) -> Result { - let lines = original - .contents - .split_inclusive('\n') - .map(|s| s.to_owned()) - .collect(); - Ok(ISrcBlock { lines }) - } -} +intermediate!(ISrcBlock, SrcBlock, original, registry, { + let lines = original + .contents + .split_inclusive('\n') + .map(|s| s.to_owned()) + .collect(); + Ok(ISrcBlock { lines }) +}); diff --git a/src/intermediate/statistics_cookie.rs b/src/intermediate/statistics_cookie.rs index e9f8043..ae6ec23 100644 --- a/src/intermediate/statistics_cookie.rs +++ b/src/intermediate/statistics_cookie.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct IStatisticsCookie {} - -impl IStatisticsCookie { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::StatisticsCookie<'parse>, - ) -> Result { - Ok(IStatisticsCookie {}) - } -} +inoop!(IStatisticsCookie, StatisticsCookie); diff --git a/src/intermediate/strike_through.rs b/src/intermediate/strike_through.rs index 5609432..d870835 100644 --- a/src/intermediate/strike_through.rs +++ b/src/intermediate/strike_through.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct IStrikeThrough {} - -impl IStrikeThrough { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::StrikeThrough<'parse>, - ) -> Result { - Ok(IStrikeThrough {}) - } -} +inoop!(IStrikeThrough, StrikeThrough); diff --git a/src/intermediate/subscript.rs b/src/intermediate/subscript.rs index 60f41c5..4ce1f3e 100644 --- a/src/intermediate/subscript.rs +++ b/src/intermediate/subscript.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct ISubscript {} - -impl ISubscript { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::Subscript<'parse>, - ) -> Result { - Ok(ISubscript {}) - } -} +inoop!(ISubscript, Subscript); diff --git a/src/intermediate/superscript.rs b/src/intermediate/superscript.rs index 2293347..9449de3 100644 --- a/src/intermediate/superscript.rs +++ b/src/intermediate/superscript.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct ISuperscript {} - -impl ISuperscript { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::Superscript<'parse>, - ) -> Result { - Ok(ISuperscript {}) - } -} +inoop!(ISuperscript, Superscript); diff --git a/src/intermediate/table.rs b/src/intermediate/table.rs index 2ce2814..c4cb36b 100644 --- a/src/intermediate/table.rs +++ b/src/intermediate/table.rs @@ -1,15 +1,5 @@ +use super::macros::inoop; +use super::registry::Registry; use crate::error::CustomError; -use super::registry::Registry; - -#[derive(Debug)] -pub(crate) struct ITable {} - -impl ITable { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::Table<'parse>, - ) -> Result { - Ok(ITable {}) - } -} +inoop!(ITable, Table); diff --git a/src/intermediate/target.rs b/src/intermediate/target.rs index 1a594de..c27eccc 100644 --- a/src/intermediate/target.rs +++ b/src/intermediate/target.rs @@ -1,23 +1,18 @@ -use crate::error::CustomError; -use crate::intermediate::util::coalesce_whitespace; - +use super::macros::intermediate; use super::registry::Registry; +use crate::error::CustomError; -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) struct ITarget { pub(crate) id: String, value: String, } -impl ITarget { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - target: &organic::types::Target<'parse>, - ) -> Result { - let id = registry.get_target(target.value); - Ok(ITarget { - id: id.clone(), - value: target.value.to_owned(), - }) - } -} +intermediate!(ITarget, Target, original, registry, { + let mut registry = registry.lock().unwrap(); + let id = registry.get_target(original.value); + Ok(ITarget { + id: id.clone(), + value: original.value.to_owned(), + }) +}); diff --git a/src/intermediate/timestamp.rs b/src/intermediate/timestamp.rs index db8d307..d2a2692 100644 --- a/src/intermediate/timestamp.rs +++ b/src/intermediate/timestamp.rs @@ -1,15 +1,6 @@ use crate::error::CustomError; +use super::macros::inoop; use super::registry::Registry; -#[derive(Debug)] -pub(crate) struct ITimestamp {} - -impl ITimestamp { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::Timestamp<'parse>, - ) -> Result { - Ok(ITimestamp {}) - } -} +inoop!(ITimestamp, Timestamp); diff --git a/src/intermediate/underline.rs b/src/intermediate/underline.rs index 4d147a4..2e60589 100644 --- a/src/intermediate/underline.rs +++ b/src/intermediate/underline.rs @@ -1,15 +1,6 @@ use crate::error::CustomError; +use super::macros::inoop; use super::registry::Registry; -#[derive(Debug)] -pub(crate) struct IUnderline {} - -impl IUnderline { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::Underline<'parse>, - ) -> Result { - Ok(IUnderline {}) - } -} +inoop!(IUnderline, Underline); diff --git a/src/intermediate/verbatim.rs b/src/intermediate/verbatim.rs index e9a2d01..7aecb4e 100644 --- a/src/intermediate/verbatim.rs +++ b/src/intermediate/verbatim.rs @@ -1,15 +1,6 @@ use crate::error::CustomError; +use super::macros::inoop; use super::registry::Registry; -#[derive(Debug)] -pub(crate) struct IVerbatim {} - -impl IVerbatim { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::Verbatim<'parse>, - ) -> Result { - Ok(IVerbatim {}) - } -} +inoop!(IVerbatim, Verbatim); diff --git a/src/intermediate/verse_block.rs b/src/intermediate/verse_block.rs index 3c8626d..6a53749 100644 --- a/src/intermediate/verse_block.rs +++ b/src/intermediate/verse_block.rs @@ -1,15 +1,6 @@ use crate::error::CustomError; +use super::macros::inoop; use super::registry::Registry; -#[derive(Debug)] -pub(crate) struct IVerseBlock {} - -impl IVerseBlock { - pub(crate) async fn new<'parse>( - registry: &mut Registry<'parse>, - original: &organic::types::VerseBlock<'parse>, - ) -> Result { - Ok(IVerseBlock {}) - } -} +inoop!(IVerseBlock, VerseBlock);