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