1 // This Source Code Form is subject to the terms of the Mozilla Public
2 // License, v. 2.0. If a copy of the MPL was not distributed with this
3 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 //
5 // Copyright © 2019 Corporation for Digital Scholarship
6
7 use pandoc_types::definition::CitationMode;
suppression_from_pandoc_mode(mode: CitationMode) -> Option<Suppression>8 pub fn suppression_from_pandoc_mode(mode: CitationMode) -> Option<Suppression> {
9 match mode {
10 CitationMode::AuthorInText => Some(Suppression::InText),
11 CitationMode::SuppressAuthor => Some(Suppression::Rest),
12 CitationMode::NormalCitation => None,
13 }
14 }
15
16 use pandoc_types::{
17 definition::{Block, Citation, Inline, Pandoc as PandocDocument},
18 walk::MutVisitor,
19 };
20
21 use citeproc::input::{Cite, CiteId, Cluster, ClusterId, Suppression};
22 use citeproc::output::Pandoc;
23 use citeproc::Processor;
24 use csl::StyleClass;
25
26 struct GetClusters {
27 next_cluster_id: ClusterId,
28 next_cite_id: CiteId,
29 clusters: Vec<Cluster<Pandoc>>,
30 }
31
get_clusters(pandoc: &mut PandocDocument) -> Vec<Cluster<Pandoc>>32 pub fn get_clusters(pandoc: &mut PandocDocument) -> Vec<Cluster<Pandoc>> {
33 // pandoc-citeproc starts at 1
34 let mut gc = GetClusters {
35 next_cluster_id: 1,
36 next_cite_id: 1,
37 clusters: vec![],
38 };
39 gc.walk_pandoc(pandoc);
40 gc.clusters
41 }
42
43 impl MutVisitor for GetClusters {
walk_inline(&mut self, inline: &mut Inline)44 fn walk_inline(&mut self, inline: &mut Inline) {
45 match *inline {
46 Inline::Note(_) => {
47 // just trying to mirror the cite hashes of pandoc-citeproc
48 self.next_cite_id += 1;
49 }
50 Inline::Cite(ref p_cites, ref _literal) => {
51 let mut note_number = 0;
52 let cites = p_cites
53 .iter()
54 .map(|p| {
55 note_number = p.citation_note_num;
56 let id = self.next_cite_id;
57 self.next_cite_id += 1;
58 Cite {
59 id,
60 ref_id: p.citation_id.clone().into(),
61 suppression: suppression_from_pandoc_mode(p.citation_mode.clone()),
62 prefix: p.citation_prefix.clone(),
63 suffix: p.citation_suffix.clone(),
64 // XXX: parse these out of the suffix, and drop the rest in "suffix"
65 locators: vec![],
66 locator_extra: None,
67 locator_date: None,
68 }
69 })
70 .collect();
71 let cluster = Cluster {
72 // id == note_number for Pandoc, but not necessarily with user-supplied
73 // ids.
74 id: self.next_cluster_id,
75 note_number: self.next_cluster_id as u32,
76 cites,
77 };
78 self.clusters.push(cluster);
79 self.next_cluster_id += 1;
80 }
81 _ => {}
82 }
83 }
84 }
85
86 struct WriteClusters<'a> {
87 next_cluster_id: ClusterId,
88 next_cite_id: CiteId,
89 db: &'a Processor,
90 }
91
92 /// Only works if you run it on a PandocDocument that hasn't been modified since you ingested the
93 /// clusters into the database. The Inline::Cite-s & Inline::Note-s have to be in the same order.
94 /// If you're adding a bibliography, do it after a get_clusters/write_clusters pair.
write_clusters(pandoc: &mut PandocDocument, db: &Processor)95 pub fn write_clusters(pandoc: &mut PandocDocument, db: &Processor) {
96 let mut wc = WriteClusters {
97 next_cluster_id: 1,
98 next_cite_id: 1,
99 db,
100 };
101 wc.walk_pandoc(pandoc);
102 }
103
104 impl<'a> MutVisitor for WriteClusters<'a> {
walk_inline(&mut self, inline: &mut Inline)105 fn walk_inline(&mut self, inline: &mut Inline) {
106 match *inline {
107 Inline::Note(_) => {
108 // just trying to mirror the cite hashes of pandoc-citeproc
109 self.next_cite_id += 1;
110 }
111 Inline::Cite(ref mut p_cites, ref mut literal) => {
112 let cites = p_cites
113 .iter()
114 .map(|p| {
115 let id = self.next_cite_id;
116 self.next_cite_id += 1;
117 let rust_cite = self.db.get_cite(id);
118 Citation {
119 citation_hash: id as i32,
120 citation_prefix: rust_cite.prefix.clone(),
121 citation_suffix: rust_cite.suffix.clone(),
122 ..p.clone()
123 }
124 })
125 .collect();
126 *p_cites = cites;
127 let built = (*self.db.get_cluster(self.next_cluster_id)).clone();
128 if self.db.get_style().class == StyleClass::Note {
129 *literal = vec![Inline::Note(vec![Block::Para(built)])];
130 } else {
131 *literal = built;
132 }
133 self.next_cluster_id += 1;
134 }
135 _ => {}
136 }
137 }
138 }
139
140 // TODO: parse locators!
141