1use crate::error::BibtexError;
2use crate::parser;
3use crate::parser::{Entry, Span, mkspan};
4use nom_language::error::VerboseError;
5use std::collections::HashMap;
6use std::result;
7use std::str;
8
9type Result<T> = result::Result<T, BibtexError>;
10
11const TABLE_MONTHS: [(&str, &str); 12] = [
12 ("jan", "January"),
13 ("feb", "February"),
14 ("mar", "March"),
15 ("apr", "April"),
16 ("may", "May"),
17 ("jun", "June"),
18 ("jul", "July"),
19 ("aug", "August"),
20 ("sep", "September"),
21 ("oct", "October"),
22 ("nov", "November"),
23 ("dec", "December"),
24];
25
26#[derive(Debug, PartialEq, Eq, Default)]
28pub struct Bibtex {
29 comments: Vec<String>,
30 preambles: Vec<String>,
31 const_map: HashMap<&'static str, &'static str>,
32 variables: HashMap<String, String>,
33 bibliographies: Vec<Bibliography>,
34}
35
36impl Bibtex {
37 pub fn parse(bibtex: &str) -> Result<Self> {
39 let entries = Self::raw_parse(bibtex)?;
40
41 let mut bibtex = Bibtex::default();
42
43 Self::fill_constants(&mut bibtex)?;
44 Self::fill_variables(&mut bibtex, &entries)?;
45
46 for entry in entries {
47 match entry {
48 Entry::Variable(_) => continue, Entry::Comment(v) => bibtex.comments.push(v),
50 Entry::Preamble(v) => {
51 let new_val = Self::expand_str_abbreviations(v, &bibtex)?;
52 bibtex.preambles.push(new_val);
53 }
54 Entry::Bibliography(entry_t, citation_key, tags) => {
55 let new_tags = tags
56 .into_iter()
57 .filter_map(|tag| {
58 let key = tag.key.to_lowercase();
59 let value = Self::expand_str_abbreviations(tag.value, &bibtex).ok()?;
60 Some((key, value))
61 })
62 .collect::<HashMap<_, _>>();
63
64 bibtex
65 .bibliographies
66 .push(Bibliography::new(entry_t, citation_key, new_tags));
67 }
68 }
69 }
70 Ok(bibtex)
71 }
72
73 pub fn raw_parse(bibtex: &str) -> Result<Vec<Entry>> {
75 let span = mkspan(bibtex);
76 match parser::entries::<VerboseError<Span>>(span) {
77 Ok((_, v)) => Ok(v),
78 Err(e) => Err(BibtexError::with_context(bibtex, e)),
79 }
80 }
81
82 pub fn preambles(&self) -> &[String] {
84 &self.preambles
85 }
86
87 pub fn comments(&self) -> &[String] {
89 &self.comments
90 }
91
92 pub fn variables(&self) -> &HashMap<String, String> {
96 &self.variables
97 }
98
99 pub fn bibliographies(&self) -> &Vec<Bibliography> {
101 &self.bibliographies
102 }
103
104 fn fill_constants(bibtex: &mut Bibtex) -> Result<()> {
105 for m in &TABLE_MONTHS {
106 bibtex.const_map.insert(m.0, m.1);
107 }
108 Ok(())
109 }
110
111 fn fill_variables(bibtex: &mut Bibtex, entries: &[Entry]) -> Result<()> {
112 let variables = entries
113 .iter()
114 .filter_map(|v| match v {
115 Entry::Variable(v) => Some(v),
116 _ => None,
117 })
118 .collect::<Vec<_>>();
119
120 bibtex.variables = variables
121 .iter()
122 .filter_map(|var| {
123 let value = Self::expand_variables_value(&var.value, &variables).ok()?;
124 Some((var.key.clone(), value))
125 })
126 .collect();
127
128 Ok(())
129 }
130
131 fn expand_variables_value(
132 var_values: &Vec<StringValueType>,
133 variables: &Vec<&KeyValue>,
134 ) -> Result<String> {
135 let mut result_value = String::new();
136
137 for chunck in var_values {
138 match chunck.clone() {
139 StringValueType::Str(v) => result_value.push_str(&v),
140 StringValueType::Abbreviation(v) => {
141 let var = variables
142 .iter()
143 .find(|&x| *v == x.key)
144 .ok_or(BibtexError::StringVariableNotFound(v))?;
145 result_value.push_str(&Self::expand_variables_value(&var.value, variables)?);
146 }
147 }
148 }
149 Ok(result_value)
150 }
151
152 fn expand_str_abbreviations(value: Vec<StringValueType>, bibtex: &Bibtex) -> Result<String> {
153 let mut result = String::new();
154
155 for chunck in value {
156 match chunck {
157 StringValueType::Str(v) => result.push_str(&v),
158 StringValueType::Abbreviation(v) => {
159 let var = bibtex.variables.iter().find(|&x| &v == x.0);
160 if let Some(res) = var {
161 result.push_str(res.1)
162 } else {
163 match bibtex.const_map.get(v.as_str()) {
164 Some(res) => result.push_str(res),
165 None => return Err(BibtexError::StringVariableNotFound(v)),
166 }
167 }
168 }
169 }
170 }
171 Ok(result)
172 }
173}
174
175#[derive(Debug, PartialEq, Eq)]
177pub struct Bibliography {
178 entry_type: String,
179 citation_key: String,
180 tags: HashMap<String, String>,
181}
182
183impl Bibliography {
184 pub fn new(
186 entry_type: String,
187 citation_key: String,
188 tags: HashMap<String, String>,
189 ) -> Bibliography {
190 Bibliography {
191 entry_type,
192 citation_key,
193 tags,
194 }
195 }
196
197 pub fn entry_type(&self) -> &str {
201 &self.entry_type
202 }
203
204 pub fn citation_key(&self) -> &str {
209 &self.citation_key
210 }
211
212 pub fn tags(&self) -> &HashMap<String, String> {
219 &self.tags
220 }
221}
222
223#[derive(Debug, PartialEq, Eq, Clone)]
228pub enum StringValueType {
229 Str(String),
231 Abbreviation(String),
233}
234
235#[derive(Debug, PartialEq, Eq, Clone)]
239pub struct KeyValue {
240 pub key: String,
241 pub value: Vec<StringValueType>,
242}
243
244impl KeyValue {
245 pub fn new(key: String, value: Vec<StringValueType>) -> KeyValue {
246 Self { key, value }
247 }
248}