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(())
}
}