use tracing::{debug, error};
use crate::documents::{EML, candidate_lists::CandidateLists, election_count::ElectionCount};
#[derive(thiserror::Error, Debug)]
pub enum EMLMatchError {
#[error("EML-510 counts document found, but missing EML-230b candidate lists document")]
NoValidCandidatesDocument,
#[error("EML-230b candidates document found, but missing EML-510 counts document")]
NoValidCountsDocument,
#[error("Missing EML-510 counts document and missing EML-230b candidate lists document")]
NoValidDocuments,
#[error(
"Election id of counts file ('{counts_id}') does not match candidate lists id ('{candidates_id}')"
)]
MismatchedElectionIds {
counts_id: String,
candidates_id: String,
},
#[error("Missing contest in the EML-510 counts document")]
MissingContestInCountsDocument,
#[error("Missing contest element in the EML-230b candidate lists document")]
MissingContestInCandidateDocument,
#[error(
"Contest id of counts file ('{counts_id}') does not match candidate lists id ('{candidates_id}')"
)]
MismatchedContestIds {
counts_id: String,
candidates_id: String,
},
}
pub fn find_matching_documents<'a>(
first_xml: &'a EML,
second_xml: &'a EML,
) -> Result<(&'a ElectionCount, &'a CandidateLists), EMLMatchError> {
let (counts, candidates) = if let Some(election_count) = first_xml.as_count_doc() {
debug!("First file is identified as counts document");
if let Some(candidate_lists) = second_xml.as_candidate_lists_doc() {
debug!("Second file is identified as candidates document");
(election_count, candidate_lists)
} else {
error!("Second file does not contain a valid EML-230b candidates document");
return Err(EMLMatchError::NoValidCandidatesDocument);
}
} else if let Some(candidate_lists) = first_xml.as_candidate_lists_doc() {
debug!("First file is identified as candidates document");
if let Some(election_count) = second_xml.as_count_doc() {
debug!("Second file is identified as counts document");
(election_count, candidate_lists)
} else {
error!("Second file does not contain a valid EML-510 counts document");
return Err(EMLMatchError::NoValidCountsDocument);
}
} else {
error!("Neither file provided contains a valid counts or candidates document");
return Err(EMLMatchError::NoValidDocuments);
};
let counts_election_id = &counts.count.election.identifier.id;
let candidates_election_id = &candidates.candidate_list.election.identifier.id;
if counts_election_id != candidates_election_id {
error!("Failed to match election ids of documents provided");
return Err(EMLMatchError::MismatchedElectionIds {
counts_id: counts_election_id.raw().into_owned(),
candidates_id: candidates_election_id.raw().into_owned(),
});
}
let count_contest = counts
.count
.election
.contests
.first()
.ok_or(EMLMatchError::MissingContestInCountsDocument)?;
let candidates_contest = candidates
.candidate_list
.election
.contests
.first()
.ok_or(EMLMatchError::MissingContestInCandidateDocument)?;
if count_contest.identifier.id.raw() != candidates_contest.identifier.id.raw() {
error!("Failed to match contest ids of documents provided");
return Err(EMLMatchError::MismatchedContestIds {
counts_id: count_contest.identifier.id.raw().into_owned(),
candidates_id: candidates_contest.identifier.id.raw().into_owned(),
});
}
Ok((counts, candidates))
}