use std::collections::HashMap;
use crate::types::{
family::Family, individual::Individual, multimedia::Multimedia, repository::Repository,
source::Source, submitter::Submitter, GedcomData,
};
#[derive(Debug)]
pub struct IndexedGedcomData {
data: GedcomData,
individual_index: HashMap<Box<str>, usize>,
family_index: HashMap<Box<str>, usize>,
source_index: HashMap<Box<str>, usize>,
repository_index: HashMap<Box<str>, usize>,
multimedia_index: HashMap<Box<str>, usize>,
submitter_index: HashMap<Box<str>, usize>,
}
impl IndexedGedcomData {
#[must_use]
pub fn new(data: GedcomData) -> Self {
let mut indexed = Self {
individual_index: HashMap::with_capacity(data.individuals.len()),
family_index: HashMap::with_capacity(data.families.len()),
source_index: HashMap::with_capacity(data.sources.len()),
repository_index: HashMap::with_capacity(data.repositories.len()),
multimedia_index: HashMap::with_capacity(data.multimedia.len()),
submitter_index: HashMap::with_capacity(data.submitters.len()),
data,
};
indexed.build_indexes();
indexed
}
fn build_indexes(&mut self) {
for (i, individual) in self.data.individuals.iter().enumerate() {
if let Some(ref xref) = individual.xref {
self.individual_index.insert(xref.clone().into(), i);
}
}
for (i, family) in self.data.families.iter().enumerate() {
if let Some(ref xref) = family.xref {
self.family_index.insert(xref.clone().into(), i);
}
}
for (i, source) in self.data.sources.iter().enumerate() {
if let Some(ref xref) = source.xref {
self.source_index.insert(xref.clone().into(), i);
}
}
for (i, repo) in self.data.repositories.iter().enumerate() {
if let Some(ref xref) = repo.xref {
self.repository_index.insert(xref.clone().into(), i);
}
}
for (i, media) in self.data.multimedia.iter().enumerate() {
if let Some(ref xref) = media.xref {
self.multimedia_index.insert(xref.clone().into(), i);
}
}
for (i, submitter) in self.data.submitters.iter().enumerate() {
if let Some(ref xref) = submitter.xref {
self.submitter_index.insert(xref.clone().into(), i);
}
}
}
#[inline]
#[must_use]
pub fn data(&self) -> &GedcomData {
&self.data
}
#[must_use]
pub fn into_inner(self) -> GedcomData {
self.data
}
#[inline]
#[must_use]
pub fn find_individual(&self, xref: &str) -> Option<&Individual> {
self.individual_index
.get(xref)
.map(|&idx| &self.data.individuals[idx])
}
#[inline]
#[must_use]
pub fn find_family(&self, xref: &str) -> Option<&Family> {
self.family_index
.get(xref)
.map(|&idx| &self.data.families[idx])
}
#[inline]
#[must_use]
pub fn find_source(&self, xref: &str) -> Option<&Source> {
self.source_index
.get(xref)
.map(|&idx| &self.data.sources[idx])
}
#[inline]
#[must_use]
pub fn find_repository(&self, xref: &str) -> Option<&Repository> {
self.repository_index
.get(xref)
.map(|&idx| &self.data.repositories[idx])
}
#[inline]
#[must_use]
pub fn find_multimedia(&self, xref: &str) -> Option<&Multimedia> {
self.multimedia_index
.get(xref)
.map(|&idx| &self.data.multimedia[idx])
}
#[inline]
#[must_use]
pub fn find_submitter(&self, xref: &str) -> Option<&Submitter> {
self.submitter_index
.get(xref)
.map(|&idx| &self.data.submitters[idx])
}
#[must_use]
pub fn get_families_as_spouse(&self, individual_xref: &str) -> Vec<&Family> {
self.data.get_families_as_spouse(individual_xref)
}
#[must_use]
pub fn get_families_as_child(&self, individual_xref: &str) -> Vec<&Family> {
self.data.get_families_as_child(individual_xref)
}
#[must_use]
pub fn get_children(&self, family: &Family) -> Vec<&Individual> {
family
.children
.iter()
.filter_map(|xref| self.find_individual(xref))
.collect()
}
#[must_use]
pub fn get_parents(&self, family: &Family) -> Vec<&Individual> {
let mut parents = Vec::with_capacity(2);
if let Some(ref xref) = family.individual1 {
if let Some(ind) = self.find_individual(xref) {
parents.push(ind);
}
}
if let Some(ref xref) = family.individual2 {
if let Some(ind) = self.find_individual(xref) {
parents.push(ind);
}
}
parents
}
#[must_use]
pub fn get_spouse(&self, individual_xref: &str, family: &Family) -> Option<&Individual> {
if family
.individual1
.as_ref()
.is_some_and(|x| x == individual_xref)
{
family
.individual2
.as_ref()
.and_then(|x| self.find_individual(x))
} else if family
.individual2
.as_ref()
.is_some_and(|x| x == individual_xref)
{
family
.individual1
.as_ref()
.and_then(|x| self.find_individual(x))
} else {
None
}
}
#[must_use]
pub fn search_individuals_by_name(&self, query: &str) -> Vec<&Individual> {
self.data.search_individuals_by_name(query)
}
#[must_use]
pub fn total_records(&self) -> usize {
self.data.total_records()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
#[must_use]
pub fn individual_count(&self) -> usize {
self.data.individuals.len()
}
#[must_use]
pub fn family_count(&self) -> usize {
self.data.families.len()
}
#[must_use]
pub fn index_stats(&self) -> IndexStats {
IndexStats {
individual_index_size: self.individual_index.len(),
family_index_size: self.family_index.len(),
source_index_size: self.source_index.len(),
repository_index_size: self.repository_index.len(),
multimedia_index_size: self.multimedia_index.len(),
submitter_index_size: self.submitter_index.len(),
}
}
}
impl From<GedcomData> for IndexedGedcomData {
fn from(data: GedcomData) -> Self {
Self::new(data)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IndexStats {
pub individual_index_size: usize,
pub family_index_size: usize,
pub source_index_size: usize,
pub repository_index_size: usize,
pub multimedia_index_size: usize,
pub submitter_index_size: usize,
}
impl IndexStats {
#[must_use]
pub fn total(&self) -> usize {
self.individual_index_size
+ self.family_index_size
+ self.source_index_size
+ self.repository_index_size
+ self.multimedia_index_size
+ self.submitter_index_size
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::GedcomBuilder;
fn create_test_data() -> GedcomData {
let source = "0 HEAD\n\
1 GEDC\n\
2 VERS 5.5\n\
0 @I1@ INDI\n\
1 NAME John /Doe/\n\
1 SEX M\n\
0 @I2@ INDI\n\
1 NAME Jane /Doe/\n\
1 SEX F\n\
0 @I3@ INDI\n\
1 NAME Jimmy /Doe/\n\
0 @F1@ FAM\n\
1 HUSB @I1@\n\
1 WIFE @I2@\n\
1 CHIL @I3@\n\
0 @S1@ SOUR\n\
1 TITL Birth Records\n\
0 @R1@ REPO\n\
1 NAME Library\n\
0 TRLR";
GedcomBuilder::new().build_from_str(source).unwrap()
}
#[test]
fn test_indexed_creation() {
let data = create_test_data();
let indexed = IndexedGedcomData::from(data);
assert_eq!(indexed.individual_count(), 3);
assert_eq!(indexed.family_count(), 1);
}
#[test]
fn test_find_individual() {
let data = create_test_data();
let indexed = IndexedGedcomData::from(data);
let john = indexed.find_individual("@I1@");
assert!(john.is_some());
assert_eq!(john.unwrap().full_name(), Some("John Doe".to_string()));
let none = indexed.find_individual("@I999@");
assert!(none.is_none());
}
#[test]
fn test_find_family() {
let data = create_test_data();
let indexed = IndexedGedcomData::from(data);
let family = indexed.find_family("@F1@");
assert!(family.is_some());
assert_eq!(family.unwrap().individual1, Some("@I1@".to_string()));
}
#[test]
fn test_find_source() {
let data = create_test_data();
let indexed = IndexedGedcomData::from(data);
let source = indexed.find_source("@S1@");
assert!(source.is_some());
}
#[test]
fn test_find_repository() {
let data = create_test_data();
let indexed = IndexedGedcomData::from(data);
let repo = indexed.find_repository("@R1@");
assert!(repo.is_some());
}
#[test]
fn test_get_children() {
let data = create_test_data();
let indexed = IndexedGedcomData::from(data);
let family = indexed.find_family("@F1@").unwrap();
let children = indexed.get_children(family);
assert_eq!(children.len(), 1);
assert_eq!(children[0].full_name(), Some("Jimmy Doe".to_string()));
}
#[test]
fn test_get_parents() {
let data = create_test_data();
let indexed = IndexedGedcomData::from(data);
let family = indexed.find_family("@F1@").unwrap();
let parents = indexed.get_parents(family);
assert_eq!(parents.len(), 2);
}
#[test]
fn test_get_spouse() {
let data = create_test_data();
let indexed = IndexedGedcomData::from(data);
let family = indexed.find_family("@F1@").unwrap();
let spouse = indexed.get_spouse("@I1@", family);
assert!(spouse.is_some());
assert_eq!(spouse.unwrap().full_name(), Some("Jane Doe".to_string()));
}
#[test]
fn test_index_stats() {
let data = create_test_data();
let indexed = IndexedGedcomData::from(data);
let stats = indexed.index_stats();
assert_eq!(stats.individual_index_size, 3);
assert_eq!(stats.family_index_size, 1);
assert_eq!(stats.source_index_size, 1);
assert_eq!(stats.repository_index_size, 1);
assert_eq!(stats.total(), 6);
}
#[test]
fn test_into_inner() {
let data = create_test_data();
let indexed = IndexedGedcomData::from(data);
let recovered = indexed.into_inner();
assert_eq!(recovered.individuals.len(), 3);
}
#[test]
fn test_data_reference() {
let data = create_test_data();
let indexed = IndexedGedcomData::from(data);
let data_ref = indexed.data();
assert_eq!(data_ref.individuals.len(), 3);
}
}