use crate::{
parse_subset,
tokenizer::{Token, Tokenizer},
types::{Date, FamilyLink, MultimediaRecord, Note, SourceCitation},
Parser,
};
#[cfg(feature = "json")]
use serde::{Deserialize, Serialize};
use std::{fmt, string::ToString};
#[allow(clippy::module_name_repetitions)]
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "json", derive(Serialize, Deserialize))]
pub enum Event {
Adoption,
AdultChristening,
Annulment,
Baptism,
BarMitzvah,
BasMitzvah,
Birth,
Blessing,
Burial,
Census,
Christening,
Confirmation,
Cremation,
Death,
Divorce,
DivorceFiled,
Emigration,
Engagement,
Event,
FirstCommunion,
Graduation,
Immigration,
Marriage,
MarriageBann,
MarriageContract,
MarriageLicense,
MarriageSettlement,
Naturalization,
Ordination,
Probate,
Residence,
Retired,
Will,
Other,
SourceData(String),
}
impl ToString for Event {
fn to_string(&self) -> String {
format!("{:?}", self)
}
}
#[derive(Clone)]
#[cfg_attr(feature = "json", derive(Serialize, Deserialize))]
pub struct EventDetail {
pub event: Event,
pub value: Option<String>,
pub date: Option<Date>,
pub place: Option<String>,
pub note: Option<Note>,
pub family_link: Option<FamilyLink>,
pub family_event_details: Vec<FamilyEventDetail>,
pub event_type: Option<String>,
pub citations: Vec<SourceCitation>,
pub multimedia: Vec<MultimediaRecord>,
}
impl EventDetail {
#[must_use]
pub fn new(tokenizer: &mut Tokenizer, level: u8, tag: &str) -> EventDetail {
let mut event = EventDetail {
event: Self::from_tag(tag),
value: None,
date: None,
place: None,
note: None,
family_link: None,
family_event_details: Vec::new(),
event_type: None,
citations: Vec::new(),
multimedia: Vec::new(),
};
event.parse(tokenizer, level);
event
}
pub fn with_source_data(&mut self, value: String) {
self.event = Event::SourceData(value);
}
pub fn from_tag(tag: &str) -> Event {
match tag {
"ADOP" => Event::Adoption,
"ANUL" => Event::Annulment,
"BAPM" => Event::Baptism,
"BARM" => Event::BarMitzvah,
"BASM" => Event::BasMitzvah,
"BIRT" => Event::Birth,
"BLES" => Event::Blessing,
"BURI" => Event::Burial,
"CENS" => Event::Census,
"CHR" => Event::Christening,
"CHRA" => Event::AdultChristening,
"CONF" => Event::Confirmation,
"CREM" => Event::Cremation,
"DEAT" => Event::Death,
"DIV" => Event::Divorce,
"DIVF" => Event::DivorceFiled,
"EMIG" => Event::Emigration,
"ENGA" => Event::Engagement,
"EVEN" => Event::Event,
"FCOM" => Event::FirstCommunion,
"GRAD" => Event::Graduation,
"IMMI" => Event::Immigration,
"MARB" => Event::MarriageBann,
"MARC" => Event::MarriageContract,
"MARL" => Event::MarriageLicense,
"MARR" => Event::Marriage,
"MARS" => Event::MarriageSettlement,
"NATU" => Event::Naturalization,
"ORDN" => Event::Ordination,
"OTHER" => Event::Other,
"PROB" => Event::Probate,
"RESI" => Event::Residence,
"RETI" => Event::Retired,
"WILL" => Event::Will,
_ => panic!("Unrecognized EventType tag: {}", tag),
}
}
pub fn add_citation(&mut self, citation: SourceCitation) {
self.citations.push(citation)
}
pub fn add_family_event_detail(&mut self, detail: FamilyEventDetail) {
self.family_event_details.push(detail);
}
pub fn add_multimedia_record(&mut self, m: MultimediaRecord) {
self.multimedia.push(m);
}
#[must_use]
pub fn get_citations(&self) -> Vec<SourceCitation> {
self.citations.clone()
}
}
impl std::fmt::Debug for EventDetail {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let event_type = format!("{:?} Event", &self.event);
let mut debug = f.debug_struct(&event_type);
fmt_optional_value!(debug, "date", &self.date);
fmt_optional_value!(debug, "place", &self.place);
debug.finish()
}
}
pub trait HasEvents {
fn add_event(&mut self, event: EventDetail) -> ();
fn events(&self) -> Vec<EventDetail>;
fn dates(&self) -> Vec<Date> {
let mut dates: Vec<Date> = Vec::new();
for event in self.events() {
if let Some(d) = &event.date {
dates.push(d.clone());
}
}
dates
}
fn places(&self) -> Vec<String> {
let mut places: Vec<String> = Vec::new();
for event in self.events() {
if let Some(p) = &event.place {
places.push(p.clone());
}
}
places
}
}
impl Parser for EventDetail {
fn parse(&mut self, tokenizer: &mut Tokenizer, level: u8) {
tokenizer.next_token();
let mut value = String::new();
if let Token::LineValue(val) = &tokenizer.current_token {
value.push_str(&val);
tokenizer.next_token();
}
let handle_subset = |tag: &str, tokenizer: &mut Tokenizer| {
let mut pointer: Option<String> = None;
if let Token::Pointer(xref) = &tokenizer.current_token {
pointer = Some(xref.to_string());
tokenizer.next_token();
}
match tag {
"DATE" => self.date = Some(Date::new(tokenizer, level + 1)),
"PLAC" => self.place = Some(tokenizer.take_line_value()),
"SOUR" => self.add_citation(SourceCitation::new(tokenizer, level + 1)),
"FAMC" => self.family_link = Some(FamilyLink::new(tokenizer, level + 1, tag)),
"HUSB" | "WIFE" => {
self.add_family_event_detail(FamilyEventDetail::new(tokenizer, level + 1, tag));
}
"NOTE" => self.note = Some(Note::new(tokenizer, level + 1)),
"TYPE" => self.event_type = Some(tokenizer.take_line_value()),
"OBJE" => {
self.add_multimedia_record(MultimediaRecord::new(tokenizer, level + 1, pointer))
}
_ => panic!("{} Unhandled Event Tag: {}", tokenizer.debug(), tag),
}
};
parse_subset(tokenizer, level, handle_subset);
if &value != "" {
self.value = Some(value);
}
}
}
#[derive(Clone, Debug)]
pub enum Spouse {
Spouse1,
Spouse2,
}
impl ToString for Spouse {
fn to_string(&self) -> String {
format!("{:?}", self)
}
}
#[derive(Clone)]
pub struct FamilyEventDetail {
pub member: Spouse,
pub age: Option<String>,
}
impl FamilyEventDetail {
#[must_use]
pub fn new(tokenizer: &mut Tokenizer, level: u8, tag: &str) -> FamilyEventDetail {
let mut fe = FamilyEventDetail {
member: Self::from_tag(tag),
age: None,
};
fe.parse(tokenizer, level);
fe
}
pub fn from_tag(tag: &str) -> Spouse {
match tag {
"HUSB" => Spouse::Spouse1,
"WIFE" => Spouse::Spouse2,
_ => panic!("{:?}, Unrecognized FamilyEventMember", tag),
}
}
}
impl Parser for FamilyEventDetail {
fn parse(&mut self, tokenizer: &mut Tokenizer, level: u8) {
tokenizer.next_token();
let handle_subset = |tag: &str, tokenizer: &mut Tokenizer| match tag {
"AGE" => self.age = Some(tokenizer.take_line_value()),
_ => panic!(
"{}, Unrecognized FamilyEventDetail tag: {}",
tokenizer.debug(),
tag
),
};
parse_subset(tokenizer, level, handle_subset);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::GedcomDocument;
#[test]
fn test_parse_person_event() {
let sample = "\
0 HEAD\n\
1 GEDC\n\
2 VERS 5.5\n\
0 @PERSON1@ INDI
1 CENS\n\
2 DATE 31 DEC 1997\n\
2 PLAC The place\n\
2 SOUR @SOURCE1@\n\
3 PAGE 42\n\
3 DATA\n\
4 DATE 31 DEC 1900\n\
4 TEXT a sample text\n\
5 CONT Sample text continued here. The word TE\n\
5 CONC ST should not be broken!\n\
3 QUAY 3\n\
3 NOTE A note\n\
4 CONT Note continued here. The word TE\n\
4 CONC ST should not be broken!\n\
2 NOTE CENSUS event note (the event of the periodic count of the population for a designated locality, such as a national or state Census)\n\
3 CONT Note continued here. The word TE\n\
3 CONC ST should not be broken!\n\
0 TRLR";
let mut doc = GedcomDocument::new(sample.chars());
let data = doc.parse_document();
let event = data.individuals[0].events[0].event.to_string();
assert_eq!(event, "Census");
}
#[test]
fn test_parse_family_event() {
let sample = "\
0 HEAD\n\
1 GEDC\n\
2 VERS 5.5\n\
0 @FAMILY1@ FAM
1 ANUL
2 DATE 31 DEC 1997
2 PLAC The place
2 SOUR @SOURCE1@
3 PAGE 42
3 DATA
4 DATE 31 DEC 1900
4 TEXT a sample text
5 CONT Sample text continued here. The word TE
5 CONC ST should not be broken!
3 QUAY 3
3 NOTE A note
4 CONT Note continued here. The word TE
4 CONC ST should not be broken!
2 NOTE ANNULMENT event note (declaring a marriage void from the beginning (never existed))
3 CONT Note continued here. The word TE
3 CONC ST should not be broken!
2 HUSB
3 AGE 42y
2 WIFE
3 AGE 42y 6m
0 TRLR";
let mut doc = GedcomDocument::new(sample.chars());
let data = doc.parse_document();
let anul = &data.families[0].events;
assert_eq!(anul.len(), 1);
}
}