1use std::str::FromStr;
2
3use rustc_hash::FxHashMap;
4use strum::EnumString;
5
6use crate::syntax::bibtex::{Entry, Field, HasName, HasType, HasValue, Value};
7
8use super::field::{
9 author::{AuthorField, AuthorFieldData},
10 date::{DateField, DateFieldData},
11 number::{NumberField, NumberFieldData},
12 text::{TextField, TextFieldData},
13};
14
15#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, EnumString)]
16#[strum(ascii_case_insensitive)]
17pub enum EntryKind {
18 Article,
19 Book,
20 MVBook,
21 InBook,
22 BookInBook,
23 SuppBook,
24 Booklet,
25 Collection,
26 MVCollection,
27 InCollection,
28 SuppCollection,
29 DataSet,
30 Manual,
31 Misc,
32 Online,
33 Electronic,
34 Www,
35 Patent,
36 Periodical,
37 SuppPeriodical,
38 Proceedings,
39 MVProceedings,
40 InProceedings,
41 Conference,
42 Reference,
43 MVReference,
44 InReference,
45 Report,
46 Set,
47 Software,
48 Thesis,
49 MasterThesis,
50 PhdThesis,
51 TechReport,
52 Unknown,
53}
54
55impl Default for EntryKind {
56 fn default() -> Self {
57 Self::Unknown
58 }
59}
60
61#[derive(Debug, Clone, Default)]
62pub struct EntryData {
63 pub kind: EntryKind,
64 pub text: FxHashMap<TextField, TextFieldData>,
65 pub author: FxHashMap<AuthorField, AuthorFieldData>,
66 pub date: FxHashMap<DateField, DateFieldData>,
67 pub number: FxHashMap<NumberField, NumberFieldData>,
68}
69
70impl From<&Entry> for EntryData {
71 fn from(entry: &Entry) -> Self {
72 let mut data = EntryData {
73 kind: entry
74 .type_token()
75 .and_then(|token| EntryKind::from_str(&token.text()[1..]).ok())
76 .unwrap_or(EntryKind::Unknown),
77 ..EntryData::default()
78 };
79
80 for field in entry.fields() {
81 let _ = data.parse_field(&field);
82 }
83
84 data
85 }
86}
87
88impl EntryData {
89 fn parse_field(&mut self, field: &Field) -> Option<()> {
90 let name = field.name_token()?;
91 let name = name.text();
92 let value = field.value()?;
93 self.parse_author_field(name, &value)
94 .or_else(|| self.parse_date_field(name, &value))
95 .or_else(|| self.parse_number_field(name, &value))
96 .or_else(|| self.parse_text_field(name, &value))
97 }
98
99 fn parse_author_field(&mut self, name: &str, value: &Value) -> Option<()> {
100 let name = AuthorField::parse(name)?;
101 let data = AuthorFieldData::parse(value)?;
102 self.author.insert(name, data);
103 Some(())
104 }
105
106 fn parse_date_field(&mut self, name: &str, value: &Value) -> Option<()> {
107 let name = DateField::parse(name)?;
108 let data = DateFieldData::parse(value)?;
109 self.date.insert(name, data);
110 Some(())
111 }
112
113 fn parse_number_field(&mut self, name: &str, value: &Value) -> Option<()> {
114 let name = NumberField::parse(name)?;
115 let data = NumberFieldData::parse(value)?;
116 self.number.insert(name, data);
117 Some(())
118 }
119
120 fn parse_text_field(&mut self, name: &str, value: &Value) -> Option<()> {
121 let name = TextField::parse(name).unwrap_or(TextField::Unknown);
122 let data = TextFieldData::parse(value)?;
123 self.text.insert(name, data);
124 Some(())
125 }
126}