summaryrefslogtreecommitdiff
path: root/support/texlab/src/citation/entry.rs
blob: 1fee03248826cb52dde4e868e6b25843a06360d3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
use std::str::FromStr;

use rustc_hash::FxHashMap;
use strum::EnumString;

use crate::syntax::bibtex::{Entry, Field, HasName, HasType, HasValue, Value};

use super::field::{
    author::{AuthorField, AuthorFieldData},
    date::{DateField, DateFieldData},
    number::{NumberField, NumberFieldData},
    text::{TextField, TextFieldData},
};

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, EnumString)]
#[strum(ascii_case_insensitive)]
pub enum EntryKind {
    Article,
    Book,
    MVBook,
    InBook,
    BookInBook,
    SuppBook,
    Booklet,
    Collection,
    MVCollection,
    InCollection,
    SuppCollection,
    DataSet,
    Manual,
    Misc,
    Online,
    Electronic,
    Www,
    Patent,
    Periodical,
    SuppPeriodical,
    Proceedings,
    MVProceedings,
    InProceedings,
    Conference,
    Reference,
    MVReference,
    InReference,
    Report,
    Set,
    Software,
    Thesis,
    MasterThesis,
    PhdThesis,
    TechReport,
    Unknown,
}

impl Default for EntryKind {
    fn default() -> Self {
        Self::Unknown
    }
}

#[derive(Debug, Clone, Default)]
pub struct EntryData {
    pub kind: EntryKind,
    pub text: FxHashMap<TextField, TextFieldData>,
    pub author: FxHashMap<AuthorField, AuthorFieldData>,
    pub date: FxHashMap<DateField, DateFieldData>,
    pub number: FxHashMap<NumberField, NumberFieldData>,
}

impl From<&Entry> for EntryData {
    fn from(entry: &Entry) -> Self {
        let mut data = EntryData {
            kind: entry
                .type_token()
                .and_then(|token| EntryKind::from_str(&token.text()[1..]).ok())
                .unwrap_or(EntryKind::Unknown),
            ..EntryData::default()
        };

        for field in entry.fields() {
            let _ = data.parse_field(&field);
        }

        data
    }
}

impl EntryData {
    fn parse_field(&mut self, field: &Field) -> Option<()> {
        let name = field.name_token()?;
        let name = name.text();
        let value = field.value()?;
        self.parse_author_field(name, &value)
            .or_else(|| self.parse_date_field(name, &value))
            .or_else(|| self.parse_number_field(name, &value))
            .or_else(|| self.parse_text_field(name, &value))
    }

    fn parse_author_field(&mut self, name: &str, value: &Value) -> Option<()> {
        let name = AuthorField::parse(name)?;
        let data = AuthorFieldData::parse(value)?;
        self.author.insert(name, data);
        Some(())
    }

    fn parse_date_field(&mut self, name: &str, value: &Value) -> Option<()> {
        let name = DateField::parse(name)?;
        let data = DateFieldData::parse(value)?;
        self.date.insert(name, data);
        Some(())
    }

    fn parse_number_field(&mut self, name: &str, value: &Value) -> Option<()> {
        let name = NumberField::parse(name)?;
        let data = NumberFieldData::parse(value)?;
        self.number.insert(name, data);
        Some(())
    }

    fn parse_text_field(&mut self, name: &str, value: &Value) -> Option<()> {
        let name = TextField::parse(name).unwrap_or(TextField::Unknown);
        let data = TextFieldData::parse(value)?;
        self.text.insert(name, data);
        Some(())
    }
}