texlab/citation/
entry.rs

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}