/*
http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#hyperlink-targets
Links can have internal or external targets.
In the source, targets look like:
.. targetname1:
.. targetname2:
some paragraph or list item or so
or:
.. targetname1:
.. targetname2: https://link
There’s also anonymous links and targets without names.
TODO: continue documenting how it’s done via http://svn.code.sf.net/p/docutils/code/trunk/docutils/docutils/transforms/references.py
*/
use std::collections::HashMap;
use crate::target::Target;
use crate::document_tree::{
Document,
HasChildren,
attribute_types::ID,
elements as e,
element_categories as c,
};
enum MaybeDirectTarget {
IndirectTarget(ID),
DirectTarget(Target),
}
trait ResolvableRefs {
fn populate_targets(&self, refs: &mut HashMap<&ID, Target>);
fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self;
}
pub fn resolve_references(mut doc: Document) -> Document {
let mut references = HashMap::new();
for c in doc.children() {
c.populate_targets(&mut references);
}
let new: Vec<_> = doc.children_mut().drain(..).map(|c| c.resolve_refs(&references)).collect();
Document::with_children(new)
}
fn sub_pop
(parent: &P, refs: &mut HashMap<&ID, Target>) where P: HasChildren, C: ResolvableRefs {
for c in parent.children() {
c.populate_targets(refs);
}
}
fn sub_res(mut parent: P, refs: &HashMap<&ID, Target>) -> P where P: e::Element + HasChildren, C: ResolvableRefs {
let new: Vec<_> = parent.children_mut().drain(..).map(|c| c.resolve_refs(&refs)).collect();
parent.children_mut().extend(new);
parent
}
fn sub_sub_pop(parent: &P, refs: &mut HashMap<&ID, Target>) where P: HasChildren, C1: HasChildren, C2: ResolvableRefs {
for c in parent.children() {
sub_pop(c, refs);
}
}
fn sub_sub_res(mut parent: P, refs: &HashMap<&ID, Target>) -> P where P: e::Element + HasChildren, C1: e::Element + HasChildren, C2: ResolvableRefs {
let new: Vec<_> = parent.children_mut().drain(..).map(|c| sub_res(c, refs)).collect();
parent.children_mut().extend(new);
parent
}
impl ResolvableRefs for c::StructuralSubElement {
fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) {
use c::StructuralSubElement::*;
match self {
Title(e) => sub_pop(&**e, refs),
Subtitle(e) => sub_pop(&**e, refs),
Decoration(e) => sub_pop(&**e, refs),
Docinfo(e) => sub_pop(&**e, refs),
SubStructure(e) => e.populate_targets(refs),
}
}
fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self {
use c::StructuralSubElement::*;
match self {
Title(e) => sub_res(*e, refs).into(),
Subtitle(e) => sub_res(*e, refs).into(),
Decoration(e) => sub_res(*e, refs).into(),
Docinfo(e) => sub_res(*e, refs).into(),
SubStructure(e) => e.resolve_refs(refs).into(),
}
}
}
impl ResolvableRefs for c::SubStructure {
fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) {
use c::SubStructure::*;
match self {
Topic(e) => sub_pop(&**e, refs),
Sidebar(e) => sub_pop(&**e, refs),
Transition(e) => {
// TODO
},
Section(e) => sub_pop(&**e, refs),
BodyElement(e) => e.populate_targets(refs),
}
}
fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self {
use c::SubStructure::*;
match self {
Topic(e) => sub_res(*e, refs).into(),
Sidebar(e) => sub_res(*e, refs).into(),
Transition(e) => Transition(e),
Section(e) => sub_res(*e, refs).into(),
BodyElement(e) => e.resolve_refs(refs).into(),
}
}
}
impl ResolvableRefs for c::BodyElement {
fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) {
use c::BodyElement::*;
match self {
Paragraph(e) => sub_pop(&**e, refs),
LiteralBlock(e) => sub_pop(&**e, refs),
DoctestBlock(e) => sub_pop(&**e, refs),
MathBlock(e) => {
// TODO
},
Rubric(e) => sub_pop(&**e, refs),
SubstitutionDefinition(e) => sub_pop(&**e, refs),
Comment(e) => sub_pop(&**e, refs),
Pending(e) => {
// TODO
},
Target(e) => {
// TODO
},
Raw(e) => {
// TODO
},
Image(e) => {
// TODO
},
Compound(e) => sub_pop(&**e, refs),
Container(e) => sub_pop(&**e, refs),
BulletList(e) => sub_sub_pop(&**e, refs),
EnumeratedList(e) => sub_sub_pop(&**e, refs),
DefinitionList(e) => sub_sub_pop(&**e, refs),
FieldList(e) => sub_sub_pop(&**e, refs),
OptionList(e) => sub_sub_pop(&**e, refs),
LineBlock(e) => sub_pop(&**e, refs),
BlockQuote(e) => sub_pop(&**e, refs),
Admonition(e) => sub_pop(&**e, refs),
Attention(e) => sub_pop(&**e, refs),
Hint(e) => sub_pop(&**e, refs),
Note(e) => sub_pop(&**e, refs),
Caution(e) => sub_pop(&**e, refs),
Danger(e) => sub_pop(&**e, refs),
Error(e) => sub_pop(&**e, refs),
Important(e) => sub_pop(&**e, refs),
Tip(e) => sub_pop(&**e, refs),
Warning(e) => sub_pop(&**e, refs),
Footnote(e) => sub_pop(&**e, refs),
Citation(e) => sub_pop(&**e, refs),
SystemMessage(e) => sub_pop(&**e, refs),
Figure(e) => sub_pop(&**e, refs),
Table(e) => sub_pop(&**e, refs)
}
}
fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self {
use c::BodyElement::*;
match self {
Paragraph(e) => sub_res(*e, refs).into(),
LiteralBlock(e) => sub_res(*e, refs).into(),
DoctestBlock(e) => sub_res(*e, refs).into(),
MathBlock(e) => MathBlock(e),
Rubric(e) => sub_res(*e, refs).into(),
SubstitutionDefinition(e) => sub_res(*e, refs).into(),
Comment(e) => sub_res(*e, refs).into(),
Pending(e) => Pending(e),
Target(e) => Target(e),
Raw(e) => Raw(e),
Image(e) => Image(e),
Compound(e) => sub_res(*e, refs).into(),
Container(e) => sub_res(*e, refs).into(),
BulletList(e) => sub_sub_res(*e, refs).into(),
EnumeratedList(e) => sub_sub_res(*e, refs).into(),
DefinitionList(e) => sub_sub_res(*e, refs).into(),
FieldList(e) => sub_sub_res(*e, refs).into(),
OptionList(e) => sub_sub_res(*e, refs).into(),
LineBlock(e) => sub_res(*e, refs).into(),
BlockQuote(e) => sub_res(*e, refs).into(),
Admonition(e) => sub_res(*e, refs).into(),
Attention(e) => sub_res(*e, refs).into(),
Hint(e) => sub_res(*e, refs).into(),
Note(e) => sub_res(*e, refs).into(),
Caution(e) => sub_res(*e, refs).into(),
Danger(e) => sub_res(*e, refs).into(),
Error(e) => sub_res(*e, refs).into(),
Important(e) => sub_res(*e, refs).into(),
Tip(e) => sub_res(*e, refs).into(),
Warning(e) => sub_res(*e, refs).into(),
Footnote(e) => sub_res(*e, refs).into(),
Citation(e) => sub_res(*e, refs).into(),
SystemMessage(e) => sub_res(*e, refs).into(),
Figure(e) => sub_res(*e, refs).into(),
Table(e) => sub_res(*e, refs).into()
}
}
}
impl ResolvableRefs for c::BibliographicElement {
fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) {
use c::BibliographicElement::*;
match self {
Author(e) => sub_pop(&**e, refs),
Authors(e) => sub_pop(&**e, refs),
Organization(e) => sub_pop(&**e, refs),
Address(e) => sub_pop(&**e, refs),
Contact(e) => sub_pop(&**e, refs),
Version(e) => sub_pop(&**e, refs),
Revision(e) => sub_pop(&**e, refs),
Status(e) => sub_pop(&**e, refs),
Date(e) => sub_pop(&**e, refs),
Copyright(e) => sub_pop(&**e, refs),
Field(e) => sub_pop(&**e, refs),
}
}
fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self {
use c::BibliographicElement::*;
match self {
Author(e) => sub_res(*e, refs).into(),
Authors(e) => sub_res(*e, refs).into(),
Organization(e) => sub_res(*e, refs).into(),
Address(e) => sub_res(*e, refs).into(),
Contact(e) => sub_res(*e, refs).into(),
Version(e) => sub_res(*e, refs).into(),
Revision(e) => sub_res(*e, refs).into(),
Status(e) => sub_res(*e, refs).into(),
Date(e) => sub_res(*e, refs).into(),
Copyright(e) => sub_res(*e, refs).into(),
Field(e) => sub_res(*e, refs).into(),
}
}
}
impl ResolvableRefs for c::TextOrInlineElement {
fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) {
use c::TextOrInlineElement::*;
match self {
c::TextOrInlineElement::String(e) => {
// TODO
},
Emphasis(e) => sub_pop(&**e, refs),
Strong(e) => sub_pop(&**e, refs),
Literal(e) => sub_pop(&**e, refs),
Reference(e) => sub_pop(&**e, refs),
FootnoteReference(e) => sub_pop(&**e, refs),
CitationReference(e) => sub_pop(&**e, refs),
SubstitutionReference(e) => sub_pop(&**e, refs),
TitleReference(e) => sub_pop(&**e, refs),
Abbreviation(e) => sub_pop(&**e, refs),
Acronym(e) => sub_pop(&**e, refs),
Superscript(e) => sub_pop(&**e, refs),
Subscript(e) => sub_pop(&**e, refs),
Inline(e) => sub_pop(&**e, refs),
Problematic(e) => sub_pop(&**e, refs),
Generated(e) => sub_pop(&**e, refs),
Math(e) => {
// TODO
},
TargetInline(e) => {
// TODO
},
RawInline(e) => {
// TODO
},
ImageInline(e) => {
// TODO
}
}
}
fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self {
use c::TextOrInlineElement::*;
match self {
c::TextOrInlineElement::String(e) => c::TextOrInlineElement::String(e),
Emphasis(e) => sub_res(*e, refs).into(),
Strong(e) => sub_res(*e, refs).into(),
Literal(e) => sub_res(*e, refs).into(),
Reference(e) => sub_res(*e, refs).into(),
FootnoteReference(e) => sub_res(*e, refs).into(),
CitationReference(e) => sub_res(*e, refs).into(),
SubstitutionReference(e) => sub_res(*e, refs).into(),
TitleReference(e) => sub_res(*e, refs).into(),
Abbreviation(e) => sub_res(*e, refs).into(),
Acronym(e) => sub_res(*e, refs).into(),
Superscript(e) => sub_res(*e, refs).into(),
Subscript(e) => sub_res(*e, refs).into(),
Inline(e) => sub_res(*e, refs).into(),
Problematic(e) => sub_res(*e, refs).into(),
Generated(e) => sub_res(*e, refs).into(),
Math(e) => Math(e),
TargetInline(e) => TargetInline(e),
RawInline(e) => RawInline(e),
ImageInline(e) => ImageInline(e)
}
}
}
impl ResolvableRefs for c::AuthorInfo {
fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) {
use c::AuthorInfo::*;
match self {
Author(e) => sub_pop(&**e, refs),
Organization(e) => sub_pop(&**e, refs),
Address(e) => sub_pop(&**e, refs),
Contact(e) => sub_pop(&**e, refs),
}
}
fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self {
use c::AuthorInfo::*;
match self {
Author(e) => sub_res(*e, refs).into(),
Organization(e) => sub_res(*e, refs).into(),
Address(e) => sub_res(*e, refs).into(),
Contact(e) => sub_res(*e, refs).into(),
}
}
}
impl ResolvableRefs for c::DecorationElement {
fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) {
use c::DecorationElement::*;
match self {
Header(e) => sub_pop(&**e, refs),
Footer(e) => sub_pop(&**e, refs),
}
}
fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self {
use c::DecorationElement::*;
match self {
Header(e) => sub_res(*e, refs).into(),
Footer(e) => sub_res(*e, refs).into(),
}
}
}
impl ResolvableRefs for c::SubTopic {
fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) {
use c::SubTopic::*;
match self {
Title(e) => sub_pop(&**e, refs),
BodyElement(e) => e.populate_targets(refs),
}
}
fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self {
use c::SubTopic::*;
match self {
Title(e) => sub_res(*e, refs).into(),
BodyElement(e) => e.resolve_refs(refs).into(),
}
}
}
impl ResolvableRefs for c::SubSidebar {
fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) {
use c::SubSidebar::*;
match self {
Topic(e) => sub_pop(&**e, refs),
Title(e) => sub_pop(&**e, refs),
Subtitle(e) => sub_pop(&**e, refs),
BodyElement(e) => e.populate_targets(refs),
}
}
fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self {
use c::SubSidebar::*;
match self {
Topic(e) => sub_res(*e, refs).into(),
Title(e) => sub_res(*e, refs).into(),
Subtitle(e) => sub_res(*e, refs).into(),
BodyElement(e) => e.resolve_refs(refs).into(),
}
}
}
impl ResolvableRefs for c::SubDLItem {
fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) {
use c::SubDLItem::*;
match self {
Term(e) => sub_pop(&**e, refs),
Classifier(e) => sub_pop(&**e, refs),
Definition(e) => sub_pop(&**e, refs),
}
}
fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self {
use c::SubDLItem::*;
match self {
Term(e) => sub_res(*e, refs).into(),
Classifier(e) => sub_res(*e, refs).into(),
Definition(e) => sub_res(*e, refs).into(),
}
}
}
impl ResolvableRefs for c::SubField {
fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) {
use c::SubField::*;
match self {
FieldName(e) => sub_pop(&**e, refs),
FieldBody(e) => sub_pop(&**e, refs),
}
}
fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self {
use c::SubField::*;
match self {
FieldName(e) => sub_res(*e, refs).into(),
FieldBody(e) => sub_res(*e, refs).into(),
}
}
}
impl ResolvableRefs for c::SubOptionListItem {
fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) {
use c::SubOptionListItem::*;
match self {
OptionGroup(e) => sub_sub_pop(&**e, refs),
Description(e) => sub_pop(&**e, refs),
}
}
fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self {
use c::SubOptionListItem::*;
match self {
OptionGroup(e) => sub_sub_res(*e, refs).into(),
Description(e) => sub_res(*e, refs).into(),
}
}
}
impl ResolvableRefs for c::SubOption {
fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) {
use c::SubOption::*;
match self {
OptionString(e) => {
// TODO
},
OptionArgument(e) => {
// TODO
},
}
}
fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self {
use c::SubOption::*;
match self {
OptionString(e) => OptionString(e),
OptionArgument(e) => OptionArgument(e),
}
}
}
impl ResolvableRefs for c::SubLineBlock {
fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) {
use c::SubLineBlock::*;
match self {
LineBlock(e) => sub_pop(&**e, refs),
Line(e) => sub_pop(&**e, refs),
}
}
fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self {
use c::SubLineBlock::*;
match self {
LineBlock(e) => sub_res(*e, refs).into(),
Line(e) => sub_res(*e, refs).into(),
}
}
}
impl ResolvableRefs for c::SubBlockQuote {
fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) {
use c::SubBlockQuote::*;
match self {
Attribution(e) => sub_pop(&**e, refs),
BodyElement(e) => e.populate_targets(refs),
}
}
fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self {
use c::SubBlockQuote::*;
match self {
Attribution(e) => sub_res(*e, refs).into(),
BodyElement(e) => e.resolve_refs(refs).into(),
}
}
}
impl ResolvableRefs for c::SubFootnote {
fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) {
use c::SubFootnote::*;
match self {
Label(e) => sub_pop(&**e, refs),
BodyElement(e) => e.populate_targets(refs),
}
}
fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self {
use c::SubFootnote::*;
match self {
Label(e) => sub_res(*e, refs).into(),
BodyElement(e) => e.resolve_refs(refs).into(),
}
}
}
impl ResolvableRefs for c::SubFigure {
fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) {
use c::SubFigure::*;
match self {
Caption(e) => sub_pop(&**e, refs),
Legend(e) => sub_pop(&**e, refs),
BodyElement(e) => e.populate_targets(refs),
}
}
fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self {
use c::SubFigure::*;
match self {
Caption(e) => sub_res(*e, refs).into(),
Legend(e) => sub_res(*e, refs).into(),
BodyElement(e) => e.resolve_refs(refs).into(),
}
}
}
impl ResolvableRefs for c::SubTable {
fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) {
use c::SubTable::*;
match self {
Title(e) => sub_pop(&**e, refs),
TableGroup(e) => sub_pop(&**e, refs),
}
}
fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self {
use c::SubTable::*;
match self {
Title(e) => sub_res(*e, refs).into(),
TableGroup(e) => sub_res(*e, refs).into(),
}
}
}
impl ResolvableRefs for c::SubTableGroup {
fn populate_targets(&self, refs: &mut HashMap<&ID, Target>) {
use c::SubTableGroup::*;
match self {
TableColspec(e) => {
// TODO
},
TableHead(e) => {
for c in e.children() {
sub_sub_pop(c, refs);
}
},
TableBody(e) => {
for c in e.children() {
sub_sub_pop(c, refs);
}
},
}
}
fn resolve_refs(self, refs: &HashMap<&ID, Target>) -> Self {
use c::SubTableGroup::*;
match self {
TableColspec(e) => TableColspec(e),
TableHead(mut e) => {
let new: Vec<_> = e.children_mut().drain(..).map(|c| sub_sub_res(c, refs)).collect();
e.children_mut().extend(new);
TableHead(e)
},
TableBody(mut e) => {
let new: Vec<_> = e.children_mut().drain(..).map(|c| sub_sub_res(c, refs)).collect();
e.children_mut().extend(new);
TableBody(e)
},
}
}
}