use std::{borrow::Cow, num::NonZeroU64, str::FromStr};
use crate::{
EML_SCHEMA_VERSION, EMLError, NS_EML, NS_KR, NS_XAL,
common::{
CandidateIdentifier, CanonicalizationMethod, ContestIdentifier, CountryNameCode,
CreationDateTime, ElectionDomain, IssueDate, ListData, ListDataBelongsToCombination,
LocalityName, ManagingAuthority, PersonNameStructure, TransactionId,
},
documents::{
ElectionIdentifierBuilder, accepted_root, validate_category_and_subcategory,
validate_election_and_nomination_dates,
},
error::EMLErrorKind,
io::{
EMLElement, EMLElementReader, EMLElementWriter, EMLReadElement as _, QualifiedName,
collect_struct, write_eml_element,
},
utils::{
AffiliationId, AffiliationType, ElectionCategory, ElectionId, ElectionSubcategory, Gender,
PublicationLanguage, StringValue, XsDate, XsDateOrDateTime, XsDateTime,
},
};
#[derive(Debug, Clone)]
pub struct CandidateLists {
pub lists_type: CandidateListsType,
pub transaction_id: TransactionId,
pub managing_authority: ManagingAuthority,
pub issue_date: IssueDate,
pub creation_date_time: CreationDateTime,
pub canonicalization_method: Option<CanonicalizationMethod>,
pub candidate_list: CandidateListsCandidateList,
}
impl CandidateLists {
pub fn builder() -> CandidateListsBuilder {
CandidateListsBuilder::new()
}
}
impl FromStr for CandidateLists {
type Err = EMLError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
use crate::io::EMLRead as _;
Self::parse_eml(s, crate::io::EMLParsingMode::Strict).ok()
}
}
impl TryFrom<&str> for CandidateLists {
type Error = EMLError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
use crate::io::EMLRead as _;
Self::parse_eml(value, crate::io::EMLParsingMode::Strict).ok()
}
}
impl TryFrom<CandidateLists> for String {
type Error = EMLError;
fn try_from(value: CandidateLists) -> Result<Self, Self::Error> {
use crate::io::EMLWrite as _;
value.write_eml_root_str(true, true)
}
}
#[derive(Debug, Clone)]
pub struct CandidateListsBuilder {
lists_type: Option<CandidateListsType>,
transaction_id: Option<TransactionId>,
managing_authority: Option<ManagingAuthority>,
issue_date: Option<IssueDate>,
creation_date_time: Option<CreationDateTime>,
canonicalization_method: Option<CanonicalizationMethod>,
candidate_list: Option<CandidateListsCandidateList>,
election_identifier: Option<CandidateListsElectionIdentifier>,
list_date: Option<CandidateListsListDate>,
contests: Vec<CandidateListsContest>,
}
impl CandidateListsBuilder {
pub fn new() -> Self {
CandidateListsBuilder {
lists_type: None,
transaction_id: None,
managing_authority: None,
issue_date: None,
creation_date_time: None,
canonicalization_method: None,
candidate_list: None,
election_identifier: None,
list_date: None,
contests: vec![],
}
}
pub fn lists_type(mut self, list_type: impl Into<CandidateListsType>) -> Self {
self.lists_type = Some(list_type.into());
self
}
pub fn transaction_id(mut self, transaction_id: impl Into<TransactionId>) -> Self {
self.transaction_id = Some(transaction_id.into());
self
}
pub fn managing_authority(mut self, managing_authority: impl Into<ManagingAuthority>) -> Self {
self.managing_authority = Some(managing_authority.into());
self
}
pub fn issue_date(mut self, issue_date: impl Into<XsDateOrDateTime>) -> Self {
self.issue_date = Some(IssueDate::new(issue_date.into()));
self
}
pub fn creation_date_time(mut self, creation_date_time: impl Into<XsDateTime>) -> Self {
self.creation_date_time = Some(CreationDateTime::new(creation_date_time.into()));
self
}
pub fn canonicalization_method(
mut self,
canonicalization_method: impl Into<CanonicalizationMethod>,
) -> Self {
self.canonicalization_method = Some(canonicalization_method.into());
self
}
pub fn candidate_list(
mut self,
candidate_list: impl Into<CandidateListsCandidateList>,
) -> Self {
self.candidate_list = Some(candidate_list.into());
self
}
pub fn list_date(mut self, list_date: impl Into<XsDateOrDateTime>) -> Self {
self.list_date = Some(CandidateListsListDate::from(list_date.into()));
self
}
pub fn election_identifier(
mut self,
election_identifier: impl Into<CandidateListsElectionIdentifier>,
) -> Self {
self.election_identifier = Some(election_identifier.into());
self
}
pub fn contests(mut self, contests: impl Into<Vec<CandidateListsContest>>) -> Self {
self.contests = contests.into();
self
}
pub fn push_contest(mut self, contest: impl Into<CandidateListsContest>) -> Self {
self.contests.push(contest.into());
self
}
pub fn build(self) -> Result<CandidateLists, EMLError> {
Ok(CandidateLists {
lists_type: self
.lists_type
.ok_or_else(|| EMLErrorKind::MissingBuildProperty("lists_type").without_span())?,
transaction_id: self
.transaction_id
.ok_or(EMLErrorKind::MissingBuildProperty("transaction_id").without_span())?,
managing_authority: self
.managing_authority
.ok_or(EMLErrorKind::MissingBuildProperty("managing_authority").without_span())?,
issue_date: self
.issue_date
.ok_or(EMLErrorKind::MissingBuildProperty("issue_date").without_span())?,
creation_date_time: self
.creation_date_time
.ok_or(EMLErrorKind::MissingBuildProperty("creation_date_time").without_span())?,
canonicalization_method: self.canonicalization_method,
candidate_list: self.candidate_list.map_or_else(
|| {
if self.contests.is_empty() {
return Err(EMLErrorKind::MissingBuildProperty("contests").without_span());
}
let election = CandidateListsElection::new(self.election_identifier.ok_or(
EMLErrorKind::MissingBuildProperty("election_identifier").without_span(),
)?)
.with_contests(self.contests);
let list = CandidateListsCandidateList::new(election);
let list = if let Some(list_date) = self.list_date {
list.with_list_date(list_date)
} else {
list
};
Ok(list)
},
Ok,
)?,
})
}
}
impl Default for CandidateListsBuilder {
fn default() -> Self {
Self::new()
}
}
impl EMLElement for CandidateLists {
const EML_NAME: QualifiedName<'_, '_> = QualifiedName::from_static("EML", Some(NS_EML));
fn read_eml(elem: &mut EMLElementReader<'_, '_>) -> Result<Self, EMLError> {
accepted_root(elem)?;
let document_id = elem.attribute_value_req(("Id", None))?;
let candidate_lists_type = CandidateListsType::from_eml_id(document_id.as_ref())
.map_err(|e| e.into_kind().with_span(elem.span()))?;
Ok(collect_struct!(elem, CandidateLists {
lists_type: candidate_lists_type,
transaction_id: TransactionId::EML_NAME => |elem| TransactionId::read_eml(elem)?,
managing_authority: ManagingAuthority::EML_NAME => |elem| ManagingAuthority::read_eml(elem)?,
issue_date: IssueDate::EML_NAME => |elem| IssueDate::read_eml(elem)?,
creation_date_time: CreationDateTime::EML_NAME => |elem| CreationDateTime::read_eml(elem)?,
canonicalization_method as Option: CanonicalizationMethod::EML_NAME => |elem| CanonicalizationMethod::read_eml(elem)?,
candidate_list: CandidateListsCandidateList::EML_NAME => |elem| CandidateListsCandidateList::read_eml(elem)?,
}))
}
fn write_eml(&self, writer: EMLElementWriter) -> Result<(), EMLError> {
writer
.attr(("Id", None), self.lists_type.to_eml_id())?
.attr(("SchemaVersion", None), EML_SCHEMA_VERSION)?
.child_elem(TransactionId::EML_NAME, &self.transaction_id)?
.child_elem(ManagingAuthority::EML_NAME, &self.managing_authority)?
.child_elem(IssueDate::EML_NAME, &self.issue_date)?
.child_elem(CreationDateTime::EML_NAME, &self.creation_date_time)?
.child_elem(CandidateListsCandidateList::EML_NAME, &self.candidate_list)?
.finish()
}
}
pub(crate) const EML_CANDIDATE_LISTS_SINGLE_ID: &str = "230b";
pub(crate) const EML_CANDIDATE_LISTS_MULTIPLE_ID: &str = "230c";
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CandidateListsType {
Single,
Multiple,
}
impl CandidateListsType {
pub fn from_eml_id(s: impl AsRef<str>) -> Result<Self, EMLError> {
let data = s.as_ref();
match data {
EML_CANDIDATE_LISTS_SINGLE_ID => Ok(CandidateListsType::Single),
EML_CANDIDATE_LISTS_MULTIPLE_ID => Ok(CandidateListsType::Multiple),
_ => {
Err(EMLErrorKind::InvalidDocumentType("230b/230c", data.to_string()).without_span())
}
}
}
pub fn to_eml_id(&self) -> &'static str {
match self {
CandidateListsType::Single => EML_CANDIDATE_LISTS_SINGLE_ID,
CandidateListsType::Multiple => EML_CANDIDATE_LISTS_MULTIPLE_ID,
}
}
pub fn to_friendly_name(&self) -> &'static str {
match self {
CandidateListsType::Single => "Candidate Lists",
CandidateListsType::Multiple => "Candidate Lists Total",
}
}
pub fn is_valid_eml_id(s: &str) -> bool {
matches!(
s,
EML_CANDIDATE_LISTS_SINGLE_ID | EML_CANDIDATE_LISTS_MULTIPLE_ID
)
}
}
#[derive(Debug, Clone)]
pub struct CandidateListsCandidateList {
pub list_date: Option<CandidateListsListDate>,
pub election: CandidateListsElection,
}
impl CandidateListsCandidateList {
pub fn new(election: CandidateListsElection) -> Self {
Self {
list_date: None,
election,
}
}
pub fn with_list_date(mut self, list_date: CandidateListsListDate) -> Self {
self.list_date = Some(list_date);
self
}
}
impl From<CandidateListsElection> for CandidateListsCandidateList {
fn from(election: CandidateListsElection) -> Self {
Self::new(election)
}
}
impl EMLElement for CandidateListsCandidateList {
const EML_NAME: QualifiedName<'_, '_> =
QualifiedName::from_static("CandidateList", Some(NS_EML));
fn read_eml(elem: &mut EMLElementReader<'_, '_>) -> Result<Self, EMLError> {
Ok(collect_struct!(elem, CandidateListsCandidateList {
list_date as Option: CandidateListsListDate::EML_NAME => |elem| CandidateListsListDate::read_eml(elem)?,
election: CandidateListsElection::EML_NAME => |elem| CandidateListsElection::read_eml(elem)?,
}))
}
fn write_eml(&self, writer: EMLElementWriter) -> Result<(), EMLError> {
writer
.child_elem_option(CandidateListsListDate::EML_NAME, self.list_date.as_ref())?
.child_elem(CandidateListsElection::EML_NAME, &self.election)?
.finish()
}
}
#[derive(Debug, Clone)]
pub struct CandidateListsListDate(pub StringValue<XsDateOrDateTime>);
impl From<XsDateOrDateTime> for CandidateListsListDate {
fn from(value: XsDateOrDateTime) -> Self {
CandidateListsListDate(StringValue::from_value(value))
}
}
impl EMLElement for CandidateListsListDate {
const EML_NAME: QualifiedName<'_, '_> = QualifiedName::from_static("ListDate", Some(NS_EML));
fn read_eml(elem: &mut EMLElementReader<'_, '_>) -> Result<Self, EMLError> {
let value = elem.string_value()?;
Ok(CandidateListsListDate(value))
}
fn write_eml(&self, writer: EMLElementWriter) -> Result<(), EMLError> {
writer.text(self.0.raw().as_ref())?.finish()
}
}
#[derive(Debug, Clone)]
pub struct CandidateListsElection {
pub identifier: CandidateListsElectionIdentifier,
pub contests: Vec<CandidateListsContest>,
}
impl CandidateListsElection {
pub fn new(identifier: impl Into<CandidateListsElectionIdentifier>) -> Self {
Self {
identifier: identifier.into(),
contests: vec![],
}
}
pub fn with_contests(mut self, contests: impl Into<Vec<CandidateListsContest>>) -> Self {
self.contests = contests.into();
self
}
pub fn push_contest(mut self, contest: impl Into<CandidateListsContest>) -> Self {
self.contests.push(contest.into());
self
}
}
impl EMLElement for CandidateListsElection {
const EML_NAME: QualifiedName<'_, '_> = QualifiedName::from_static("Election", Some(NS_EML));
fn read_eml(elem: &mut EMLElementReader<'_, '_>) -> Result<Self, EMLError> {
let data = collect_struct!(elem, CandidateListsElection {
identifier: CandidateListsElectionIdentifier::EML_NAME => |elem| CandidateListsElectionIdentifier::read_eml(elem)?,
contests as Vec: CandidateListsContest::EML_NAME => |elem| CandidateListsContest::read_eml(elem)?,
});
if data.contests.is_empty() {
let err = EMLErrorKind::MissingElement(CandidateListsContest::EML_NAME.as_owned())
.with_span(elem.full_span());
if elem.parsing_mode().is_strict() {
return Err(err);
} else {
elem.push_err(err);
}
}
Ok(data)
}
fn write_eml(&self, writer: EMLElementWriter) -> Result<(), EMLError> {
writer
.child_elem(CandidateListsElectionIdentifier::EML_NAME, &self.identifier)?
.child_elems(CandidateListsContest::EML_NAME, &self.contests)?
.finish()
}
}
#[derive(Debug, Clone)]
pub struct CandidateListsElectionIdentifier {
pub id: StringValue<ElectionId>,
pub name: Option<String>,
pub category: StringValue<ElectionCategory>,
pub subcategory: Option<StringValue<ElectionSubcategory>>,
pub domain: Option<ElectionDomain>,
pub election_date: StringValue<XsDate>,
pub nomination_date: StringValue<XsDate>,
}
impl CandidateListsElectionIdentifier {
pub fn builder() -> ElectionIdentifierBuilder {
ElectionIdentifierBuilder::new()
}
}
impl EMLElement for CandidateListsElectionIdentifier {
const EML_NAME: QualifiedName<'_, '_> =
QualifiedName::from_static("ElectionIdentifier", Some(NS_EML));
fn read_eml(elem: &mut EMLElementReader<'_, '_>) -> Result<Self, EMLError> {
let data = collect_struct!(
elem,
CandidateListsElectionIdentifier {
id: elem.string_value_attr("Id", None)?,
name as Option: ("ElectionName", NS_EML) => |elem| elem.text_without_children()?,
category: ("ElectionCategory", NS_EML) => |elem| elem.string_value()?,
subcategory as Option: ("ElectionSubcategory", NS_KR) => |elem| elem.string_value()?,
domain as Option: ElectionDomain::EML_NAME => |elem| ElectionDomain::read_eml(elem)?,
election_date: ("ElectionDate", NS_KR) => |elem| elem.string_value()?,
nomination_date: ("NominationDate", NS_KR) => |elem| elem.string_value()?,
}
);
if let Err(e) = validate_election_and_nomination_dates(
Some(&data.election_date),
Some(&data.nomination_date),
) {
let e = e.into_kind().with_span(elem.full_span());
if elem.parsing_mode().is_strict() {
return Err(e);
} else {
elem.push_err(e);
}
}
if let Err(e) = validate_category_and_subcategory(&data.category, data.subcategory.as_ref())
{
let e = e.into_kind().with_span(elem.full_span());
if elem.parsing_mode().is_strict() {
return Err(e);
} else {
elem.push_err(e);
}
}
Ok(data)
}
fn write_eml(&self, writer: EMLElementWriter) -> Result<(), EMLError> {
writer
.attr("Id", self.id.raw().as_ref())?
.child_option(
("ElectionName", NS_EML),
self.name.as_ref(),
|elem, value| elem.text(value.as_ref())?.finish(),
)?
.child(("ElectionCategory", NS_EML), |elem| {
elem.text(self.category.raw().as_ref())?.finish()
})?
.child_option(
("ElectionSubcategory", NS_KR),
self.subcategory.as_ref(),
|elem, value| elem.text(value.raw().as_ref())?.finish(),
)?
.child_elem_option(ElectionDomain::EML_NAME, self.domain.as_ref())?
.child(("ElectionDate", NS_KR), |elem| {
elem.text(self.election_date.raw().as_ref())?.finish()
})?
.child(("NominationDate", NS_KR), |elem| {
elem.text(self.nomination_date.raw().as_ref())?.finish()
})?
.finish()
}
}
#[derive(Debug, Clone)]
pub struct CandidateListsContest {
pub identifier: ContestIdentifier,
pub affiliations: Vec<CandidateListsAffiliation>,
}
pub struct CandidateListsContestBuilder {
identifier: Option<ContestIdentifier>,
affiliations: Vec<CandidateListsAffiliation>,
}
impl CandidateListsContestBuilder {
pub fn new() -> Self {
Self {
identifier: None,
affiliations: vec![],
}
}
pub fn identifier(mut self, identifier: impl Into<ContestIdentifier>) -> Self {
self.identifier = Some(identifier.into());
self
}
pub fn affiliations(mut self, affiliations: impl Into<Vec<CandidateListsAffiliation>>) -> Self {
self.affiliations = affiliations.into();
self
}
pub fn push_affiliation(mut self, affiliation: impl Into<CandidateListsAffiliation>) -> Self {
self.affiliations.push(affiliation.into());
self
}
pub fn build(self) -> Result<CandidateListsContest, EMLError> {
if self.affiliations.is_empty() {
return Err(EMLErrorKind::MissingBuildProperty("affiliations").without_span());
}
Ok(CandidateListsContest {
identifier: self
.identifier
.ok_or_else(|| EMLErrorKind::MissingBuildProperty("identifier").without_span())?,
affiliations: self.affiliations,
})
}
}
impl Default for CandidateListsContestBuilder {
fn default() -> Self {
Self::new()
}
}
impl CandidateListsContest {
pub fn builder() -> CandidateListsContestBuilder {
CandidateListsContestBuilder::new()
}
}
impl EMLElement for CandidateListsContest {
const EML_NAME: QualifiedName<'_, '_> = QualifiedName::from_static("Contest", Some(NS_EML));
fn read_eml(elem: &mut EMLElementReader<'_, '_>) -> Result<Self, EMLError> {
let data = collect_struct!(elem, CandidateListsContest {
identifier: ContestIdentifier::EML_NAME => |elem| ContestIdentifier::read_eml(elem)?,
affiliations as Vec: CandidateListsAffiliation::EML_NAME => |elem| CandidateListsAffiliation::read_eml(elem)?,
});
if data.affiliations.is_empty() {
let err = EMLErrorKind::MissingElement(CandidateListsAffiliation::EML_NAME.as_owned())
.with_span(elem.full_span());
if elem.parsing_mode().is_strict() {
return Err(err);
} else {
elem.push_err(err);
}
}
Ok(data)
}
fn write_eml(&self, writer: EMLElementWriter) -> Result<(), EMLError> {
writer
.child_elem(ContestIdentifier::EML_NAME, &self.identifier)?
.child_elems(CandidateListsAffiliation::EML_NAME, &self.affiliations)?
.finish()
}
}
#[derive(Debug, Clone)]
pub struct CandidateListsAffiliation {
pub identifier: AffiliationIdentifier,
pub affiliation_type: StringValue<AffiliationType>,
pub list_data: ListData,
pub candidates: Vec<CandidateListsCandidate>,
}
impl CandidateListsAffiliation {
pub fn builder() -> CandidateListsAffiliationBuilder {
CandidateListsAffiliationBuilder::new()
}
}
pub struct CandidateListsAffiliationBuilder {
id: Option<AffiliationId>,
registered_name: Option<String>,
affiliation_type: Option<AffiliationType>,
publish_gender: Option<bool>,
publication_language: Option<PublicationLanguage>,
belongs_to_set: Option<NonZeroU64>,
belongs_to_combination: Option<ListDataBelongsToCombination>,
candidates: Vec<CandidateListsCandidate>,
}
impl CandidateListsAffiliationBuilder {
pub fn new() -> Self {
CandidateListsAffiliationBuilder {
id: None,
registered_name: None,
affiliation_type: None,
publish_gender: None,
publication_language: None,
belongs_to_set: None,
belongs_to_combination: None,
candidates: vec![],
}
}
pub fn id(mut self, id: impl Into<AffiliationId>) -> Self {
self.id = Some(id.into());
self
}
pub fn registered_name(mut self, registered_name: impl Into<String>) -> Self {
self.registered_name = Some(registered_name.into());
self
}
pub fn affiliation_type(mut self, affiliation_type: impl Into<AffiliationType>) -> Self {
self.affiliation_type = Some(affiliation_type.into());
self
}
pub fn publish_gender(mut self, publish_gender: bool) -> Self {
self.publish_gender = Some(publish_gender);
self
}
pub fn publication_language(
mut self,
publication_language: impl Into<PublicationLanguage>,
) -> Self {
self.publication_language = Some(publication_language.into());
self
}
pub fn belongs_to_set(mut self, belongs_to_set: NonZeroU64) -> Self {
self.belongs_to_set = Some(belongs_to_set);
self
}
pub fn belongs_to_combination(
mut self,
belongs_to_combination: impl Into<ListDataBelongsToCombination>,
) -> Self {
self.belongs_to_combination = Some(belongs_to_combination.into());
self
}
pub fn candidates(mut self, candidates: impl Into<Vec<CandidateListsCandidate>>) -> Self {
self.candidates = candidates.into();
self
}
pub fn push_candidate(mut self, candidate: impl Into<CandidateListsCandidate>) -> Self {
self.candidates.push(candidate.into());
self
}
pub fn build(self) -> Result<CandidateListsAffiliation, EMLError> {
if self.candidates.is_empty() {
return Err(EMLErrorKind::MissingBuildProperty("candidates").without_span());
}
Ok(CandidateListsAffiliation {
identifier: AffiliationIdentifier::new(
self.id
.ok_or_else(|| EMLErrorKind::MissingBuildProperty("id").without_span())?,
self.registered_name,
),
affiliation_type: StringValue::from_value(self.affiliation_type.ok_or_else(|| {
EMLErrorKind::MissingBuildProperty("affiliation_type").without_span()
})?),
list_data: ListData {
publish_gender: StringValue::from_value(self.publish_gender.ok_or_else(|| {
EMLErrorKind::MissingBuildProperty("publish_gender").without_span()
})?),
publication_language: self.publication_language.map(StringValue::from_value),
belongs_to_set: self.belongs_to_set.map(StringValue::from_value),
belongs_to_combination: self.belongs_to_combination.map(StringValue::from_value),
contests: vec![],
},
candidates: self.candidates,
})
}
}
impl Default for CandidateListsAffiliationBuilder {
fn default() -> Self {
Self::new()
}
}
impl EMLElement for CandidateListsAffiliation {
const EML_NAME: QualifiedName<'_, '_> = QualifiedName::from_static("Affiliation", Some(NS_EML));
fn read_eml(elem: &mut EMLElementReader<'_, '_>) -> Result<Self, EMLError> {
let data = collect_struct!(elem, CandidateListsAffiliation {
identifier: AffiliationIdentifier::EML_NAME => |elem| AffiliationIdentifier::read_eml(elem)?,
affiliation_type: ("Type", NS_EML) => |elem| elem.string_value()?,
list_data: ListData::EML_NAME => |elem| ListData::read_eml(elem)?,
candidates as Vec: CandidateListsCandidate::EML_NAME => |elem| CandidateListsCandidate::read_eml(elem)?,
});
if data.candidates.is_empty() {
let err = EMLErrorKind::MissingElement(CandidateListsCandidate::EML_NAME.as_owned())
.with_span(elem.full_span());
if elem.parsing_mode().is_strict() {
return Err(err);
} else {
elem.push_err(err);
}
}
Ok(data)
}
fn write_eml(&self, writer: EMLElementWriter) -> Result<(), EMLError> {
writer
.child_elem(AffiliationIdentifier::EML_NAME, &self.identifier)?
.child(("Type", NS_EML), |elem| {
elem.text(self.affiliation_type.raw().as_ref())?.finish()
})?
.child_elem(ListData::EML_NAME, &self.list_data)?
.child_elems(CandidateListsCandidate::EML_NAME, &self.candidates)?
.finish()
}
}
#[derive(Debug, Clone)]
pub struct AffiliationIdentifier {
pub id: StringValue<AffiliationId>,
pub registered_name: Option<String>,
}
impl AffiliationIdentifier {
pub fn new(id: AffiliationId, registered_name: Option<impl Into<String>>) -> Self {
Self {
id: StringValue::Parsed(id),
registered_name: registered_name.map(|name| name.into()),
}
}
}
impl EMLElement for AffiliationIdentifier {
const EML_NAME: QualifiedName<'_, '_> =
QualifiedName::from_static("AffiliationIdentifier", Some(NS_EML));
fn read_eml(elem: &mut EMLElementReader<'_, '_>) -> Result<Self, EMLError> {
Ok(collect_struct!(
elem,
AffiliationIdentifier {
id: elem.string_value_attr("Id", None)?,
registered_name: ("RegisteredName", NS_EML) => |elem| elem.text_without_children_opt()?,
}
))
}
fn write_eml(&self, writer: EMLElementWriter) -> Result<(), EMLError> {
writer
.attr("Id", self.id.raw().as_ref())?
.child(("RegisteredName", NS_EML), |w| {
if let Some(name) = &self.registered_name {
w.text(name)?.finish()
} else {
w.empty()
}
})?
.finish()?;
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct CandidateListsCandidate {
pub identifier: CandidateIdentifier,
pub full_name: PersonNameStructure,
pub date_of_birth: Option<StringValue<XsDate>>,
pub gender: Option<StringValue<Gender>>,
pub qualifying_address: Option<QualifyingAddress>,
}
impl CandidateListsCandidate {
pub fn builder() -> CandidateListsCandidateBuilder {
CandidateListsCandidateBuilder::new()
}
}
#[derive(Debug, Clone)]
pub struct CandidateListsCandidateBuilder {
identifier: Option<CandidateIdentifier>,
date_of_birth: Option<XsDate>,
gender: Option<Gender>,
full_name: Option<PersonNameStructure>,
qualifying_address: Option<QualifyingAddress>,
}
impl CandidateListsCandidateBuilder {
pub fn new() -> Self {
Self {
identifier: None,
date_of_birth: None,
gender: None,
full_name: None,
qualifying_address: None,
}
}
pub fn identifier(mut self, identifier: impl Into<CandidateIdentifier>) -> Self {
self.identifier = Some(identifier.into());
self
}
pub fn date_of_birth(mut self, date_of_birth: impl Into<XsDate>) -> Self {
self.date_of_birth = Some(date_of_birth.into());
self
}
pub fn gender(mut self, gender: impl Into<Gender>) -> Self {
self.gender = Some(gender.into());
self
}
pub fn full_name(mut self, full_name: impl Into<PersonNameStructure>) -> Self {
self.full_name = Some(full_name.into());
self
}
pub fn qualifying_address(mut self, qualifying_address: impl Into<QualifyingAddress>) -> Self {
self.qualifying_address = Some(qualifying_address.into());
self
}
pub fn build(self) -> Result<CandidateListsCandidate, EMLError> {
Ok(CandidateListsCandidate {
identifier: self
.identifier
.ok_or_else(|| EMLErrorKind::MissingBuildProperty("identifier").without_span())?,
full_name: self
.full_name
.ok_or_else(|| EMLErrorKind::MissingBuildProperty("full_name").without_span())?,
date_of_birth: self.date_of_birth.map(StringValue::from_value),
gender: self.gender.map(StringValue::from_value),
qualifying_address: self.qualifying_address,
})
}
}
impl Default for CandidateListsCandidateBuilder {
fn default() -> Self {
Self::new()
}
}
impl EMLElement for CandidateListsCandidate {
const EML_NAME: QualifiedName<'_, '_> = QualifiedName::from_static("Candidate", Some(NS_EML));
fn read_eml(elem: &mut EMLElementReader<'_, '_>) -> Result<Self, EMLError> {
Ok(collect_struct!(elem, CandidateListsCandidate {
identifier: CandidateIdentifier::EML_NAME => |elem| CandidateIdentifier::read_eml(elem)?,
full_name: ("CandidateFullName", NS_EML) => |elem| PersonNameStructure::read_eml_element(elem)?,
date_of_birth as Option: ("DateOfBirth", NS_EML) => |elem| elem.string_value()?,
gender as Option: ("Gender", NS_EML) => |elem| elem.string_value()?,
qualifying_address as Option: QualifyingAddress::EML_NAME => |elem| QualifyingAddress::read_eml(elem)?,
}))
}
fn write_eml(&self, writer: EMLElementWriter) -> Result<(), EMLError> {
writer
.child_elem(CandidateIdentifier::EML_NAME, &self.identifier)?
.child(
("CandidateFullName", NS_EML),
write_eml_element(&self.full_name),
)?
.child_option(
("DateOfBirth", NS_EML),
self.date_of_birth.as_ref(),
|elem, value| elem.text(value.raw().as_ref())?.finish(),
)?
.child_option(("Gender", NS_EML), self.gender.as_ref(), |elem, value| {
elem.text(value.raw().as_ref())?.finish()
})?
.child_elem_option(
QualifyingAddress::EML_NAME,
self.qualifying_address.as_ref(),
)?
.finish()
}
}
#[derive(Debug, Clone)]
pub enum QualifyingAddress {
Locality(QualifyingAddressLocality),
Country(QualifyingAddressCountry),
}
impl QualifyingAddress {
pub fn new(
locality: impl Into<QualifyingAddressLocality>,
country_name_code: Option<impl Into<CountryNameCode>>,
) -> Self {
match country_name_code {
Some(code) => QualifyingAddress::Country(QualifyingAddressCountry {
locality: locality.into(),
country_name_code: Some(code.into()),
}),
None => QualifyingAddress::Locality(locality.into()),
}
}
pub fn locality(&self) -> &QualifyingAddressLocality {
match self {
QualifyingAddress::Locality(locality) => locality,
QualifyingAddress::Country(country) => &country.locality,
}
}
pub fn country_name_code(&self) -> Option<&CountryNameCode> {
match self {
QualifyingAddress::Locality(_) => None,
QualifyingAddress::Country(country) => country.country_name_code.as_ref(),
}
}
}
impl From<QualifyingAddressLocality> for QualifyingAddress {
fn from(locality: QualifyingAddressLocality) -> Self {
QualifyingAddress::Locality(locality)
}
}
impl From<QualifyingAddressCountry> for QualifyingAddress {
fn from(country: QualifyingAddressCountry) -> Self {
QualifyingAddress::Country(country)
}
}
impl EMLElement for QualifyingAddress {
const EML_NAME: QualifiedName<'_, '_> =
QualifiedName::from_static("QualifyingAddress", Some(NS_EML));
fn read_eml(elem: &mut EMLElementReader<'_, '_>) -> Result<Self, EMLError> {
let parent_name = elem.name()?.as_owned();
let mut found_value = None;
while let Some(mut next_child) = elem.next_child()? {
let name = next_child.name()?;
if found_value.is_some()
|| name != QualifyingAddressLocality::EML_NAME
&& name != QualifyingAddressCountry::EML_NAME
{
let err = EMLErrorKind::UnexpectedElement(name.as_owned(), parent_name.clone())
.with_span(next_child.span());
if next_child.parsing_mode().is_strict() {
return Err(err);
} else {
next_child.push_err(err);
next_child.skip()?;
}
} else {
match name {
name if name == QualifyingAddressLocality::EML_NAME => {
let locality = QualifyingAddressLocality::read_eml(&mut next_child)?;
found_value = Some(QualifyingAddress::Locality(locality));
}
name if name == QualifyingAddressCountry::EML_NAME => {
let country = QualifyingAddressCountry::read_eml(&mut next_child)?;
found_value = Some(QualifyingAddress::Country(country));
}
_ => unreachable!(),
}
}
}
let Some(value) = found_value else {
return Err(EMLErrorKind::MissingChoiceElements(vec![
QualifyingAddressLocality::EML_NAME.as_owned(),
QualifyingAddressCountry::EML_NAME.as_owned(),
])
.with_span(elem.span()));
};
Ok(value)
}
fn write_eml(&self, writer: EMLElementWriter) -> Result<(), EMLError> {
match self {
QualifyingAddress::Locality(locality) => {
writer.child_elem(QualifyingAddressLocality::EML_NAME, locality)?
}
QualifyingAddress::Country(country) => {
writer.child_elem(QualifyingAddressCountry::EML_NAME, country)?
}
}
.finish()
}
}
#[derive(Debug, Clone)]
pub struct QualifyingAddressLocality {
pub address_line: Option<AddressLine>,
pub locality_name: LocalityName,
pub postal_code: Option<PostalCode>,
pub locality_type: Option<String>,
pub usage_type: Option<String>,
pub indicator: Option<String>,
}
impl QualifyingAddressLocality {
pub fn new(locality_name: impl Into<String>) -> Self {
QualifyingAddressLocality {
address_line: None,
locality_name: LocalityName::new(locality_name),
postal_code: None,
locality_type: None,
usage_type: None,
indicator: None,
}
}
pub fn locality_name(&self) -> &str {
&self.locality_name.name
}
pub fn with_address_line(self, address_line: impl Into<AddressLine>) -> Self {
self.with_address_line_option(Some(address_line))
}
pub fn with_address_line_option(
mut self,
address_line: Option<impl Into<AddressLine>>,
) -> Self {
self.address_line = address_line.map(Into::into);
self
}
pub fn with_postal_code(self, postal_code: impl Into<PostalCode>) -> Self {
self.with_postal_code_option(Some(postal_code))
}
pub fn with_postal_code_option(mut self, postal_code: Option<impl Into<PostalCode>>) -> Self {
self.postal_code = postal_code.map(Into::into);
self
}
pub fn with_locality_type(self, locality_type: impl Into<String>) -> Self {
self.with_locality_type_option(Some(locality_type))
}
pub fn with_locality_type_option(mut self, locality_type: Option<impl Into<String>>) -> Self {
self.locality_type = locality_type.map(Into::into);
self
}
pub fn with_usage_type(self, usage_type: impl Into<String>) -> Self {
self.with_usage_type_option(Some(usage_type))
}
pub fn with_usage_type_option(mut self, usage_type: Option<impl Into<String>>) -> Self {
self.usage_type = usage_type.map(Into::into);
self
}
pub fn with_indicator(self, indicator: impl Into<String>) -> Self {
self.with_indicator_option(Some(indicator))
}
pub fn with_indicator_option(mut self, indicator: Option<impl Into<String>>) -> Self {
self.indicator = indicator.map(Into::into);
self
}
}
impl From<&str> for QualifyingAddressLocality {
fn from(value: &str) -> Self {
QualifyingAddressLocality::new(value)
}
}
impl From<String> for QualifyingAddressLocality {
fn from(value: String) -> Self {
QualifyingAddressLocality::new(value)
}
}
impl EMLElement for QualifyingAddressLocality {
const EML_NAME: QualifiedName<'_, '_> = QualifiedName::from_static("Locality", Some(NS_XAL));
fn read_eml(elem: &mut EMLElementReader<'_, '_>) -> Result<Self, EMLError> {
Ok(collect_struct!(elem, QualifyingAddressLocality {
address_line as Option: AddressLine::EML_NAME => |elem| AddressLine::read_eml(elem)?,
locality_name: LocalityName::EML_NAME => |elem| LocalityName::read_eml(elem)?,
postal_code as Option: PostalCode::EML_NAME => |elem| PostalCode::read_eml(elem)?,
locality_type: elem.attribute_value("Type")?.map(Cow::into_owned),
usage_type: elem.attribute_value("UsageType")?.map(Cow::into_owned),
indicator: elem.attribute_value("Indicator")?.map(Cow::into_owned),
}))
}
fn write_eml(&self, writer: EMLElementWriter) -> Result<(), EMLError> {
writer
.attr_opt("Type", self.locality_type.as_ref())?
.attr_opt("UsageType", self.usage_type.as_ref())?
.attr_opt("Indicator", self.indicator.as_ref())?
.child_elem_option(AddressLine::EML_NAME, self.address_line.as_ref())?
.child_elem(LocalityName::EML_NAME, &self.locality_name)?
.child_elem_option(PostalCode::EML_NAME, self.postal_code.as_ref())?
.finish()
}
}
#[derive(Debug, Clone)]
pub struct AddressLine {
pub value: String,
pub address_line_type: Option<String>,
pub code: Option<String>,
}
impl AddressLine {
pub fn new(value: impl Into<String>) -> Self {
AddressLine {
value: value.into(),
address_line_type: None,
code: None,
}
}
pub fn with_type(mut self, address_line_type: impl Into<String>) -> Self {
self.address_line_type = Some(address_line_type.into());
self
}
pub fn with_code(mut self, code: impl Into<String>) -> Self {
self.code = Some(code.into());
self
}
}
impl From<&str> for AddressLine {
fn from(value: &str) -> Self {
AddressLine::new(value)
}
}
impl From<String> for AddressLine {
fn from(value: String) -> Self {
AddressLine::new(value)
}
}
impl EMLElement for AddressLine {
const EML_NAME: QualifiedName<'_, '_> = QualifiedName::from_static("AddressLine", Some(NS_XAL));
fn read_eml(elem: &mut EMLElementReader<'_, '_>) -> Result<Self, EMLError> {
Ok(AddressLine {
value: elem.text_without_children()?,
address_line_type: elem.attribute_value("Type")?.map(Cow::into_owned),
code: elem.attribute_value("Code")?.map(Cow::into_owned),
})
}
fn write_eml(&self, writer: EMLElementWriter) -> Result<(), EMLError> {
writer
.attr_opt("Type", self.address_line_type.as_ref())?
.attr_opt("Code", self.code.as_ref())?
.text(self.value.as_ref())?
.finish()
}
}
#[derive(Debug, Clone)]
pub struct PostalCode {
pub postal_code_number: PostalCodeNumber,
}
impl PostalCode {
pub fn new(postal_code_number: impl Into<PostalCodeNumber>) -> Self {
PostalCode {
postal_code_number: postal_code_number.into(),
}
}
}
impl From<&str> for PostalCode {
fn from(value: &str) -> Self {
PostalCode::new(value)
}
}
impl From<String> for PostalCode {
fn from(value: String) -> Self {
PostalCode::new(value)
}
}
impl From<PostalCodeNumber> for PostalCode {
fn from(postal_code_number: PostalCodeNumber) -> Self {
PostalCode { postal_code_number }
}
}
impl EMLElement for PostalCode {
const EML_NAME: QualifiedName<'_, '_> = QualifiedName::from_static("PostalCode", Some(NS_XAL));
fn read_eml(elem: &mut EMLElementReader<'_, '_>) -> Result<Self, EMLError> {
Ok(collect_struct!(elem, PostalCode {
postal_code_number: PostalCodeNumber::EML_NAME => |elem| PostalCodeNumber::read_eml(elem)?,
}))
}
fn write_eml(&self, writer: EMLElementWriter) -> Result<(), EMLError> {
writer
.child_elem(PostalCodeNumber::EML_NAME, &self.postal_code_number)?
.finish()
}
}
#[derive(Debug, Clone)]
pub struct PostalCodeNumber {
pub value: String,
pub postal_code_number_type: Option<String>,
pub code: Option<String>,
}
impl EMLElement for PostalCodeNumber {
const EML_NAME: QualifiedName<'_, '_> =
QualifiedName::from_static("PostalCodeNumber", Some(NS_XAL));
fn read_eml(elem: &mut EMLElementReader<'_, '_>) -> Result<Self, EMLError> {
Ok(PostalCodeNumber {
value: elem.text_without_children()?,
postal_code_number_type: elem.attribute_value("Type")?.map(Cow::into_owned),
code: elem.attribute_value("Code")?.map(Cow::into_owned),
})
}
fn write_eml(&self, writer: EMLElementWriter) -> Result<(), EMLError> {
writer
.attr_opt("Type", self.postal_code_number_type.as_ref())?
.attr_opt("Code", self.code.as_ref())?
.text(self.value.as_ref())?
.finish()
}
}
impl PostalCodeNumber {
pub fn new(value: impl Into<String>) -> Self {
PostalCodeNumber {
value: value.into(),
postal_code_number_type: None,
code: None,
}
}
pub fn with_type(mut self, postal_code_number_type: impl Into<String>) -> Self {
self.postal_code_number_type = Some(postal_code_number_type.into());
self
}
pub fn with_code(mut self, code: impl Into<String>) -> Self {
self.code = Some(code.into());
self
}
}
impl From<&str> for PostalCodeNumber {
fn from(value: &str) -> Self {
PostalCodeNumber::new(value)
}
}
impl From<String> for PostalCodeNumber {
fn from(value: String) -> Self {
PostalCodeNumber::new(value)
}
}
#[derive(Debug, Clone)]
pub struct QualifyingAddressCountry {
pub country_name_code: Option<CountryNameCode>,
pub locality: QualifyingAddressLocality,
}
impl QualifyingAddressCountry {
pub fn new(
country_code: Option<impl Into<String>>,
locality: impl Into<QualifyingAddressLocality>,
) -> Self {
Self {
country_name_code: country_code.map(|code| CountryNameCode::new(code)),
locality: locality.into(),
}
}
}
impl EMLElement for QualifyingAddressCountry {
const EML_NAME: QualifiedName<'_, '_> = QualifiedName::from_static("Country", Some(NS_XAL));
fn read_eml(elem: &mut EMLElementReader<'_, '_>) -> Result<Self, EMLError> {
Ok(collect_struct!(elem, QualifyingAddressCountry {
country_name_code as Option: CountryNameCode::EML_NAME => |elem| CountryNameCode::read_eml(elem)?,
locality: QualifyingAddressLocality::EML_NAME => |elem| QualifyingAddressLocality::read_eml(elem)?,
}))
}
fn write_eml(&self, writer: EMLElementWriter) -> Result<(), EMLError> {
writer
.child_elem_option(CountryNameCode::EML_NAME, self.country_name_code.as_ref())?
.child_elem(QualifyingAddressLocality::EML_NAME, &self.locality)?
.finish()
}
}
#[cfg(test)]
mod tests {
use chrono::TimeZone as _;
use super::*;
use crate::{
common::PersonName,
io::{
EMLParsingMode, EMLRead as _, EMLWrite as _, test_write_eml_element, test_xml_fragment,
},
utils::{AuthorityId, CandidateId},
};
#[test]
fn test_affiliation_identifier() {
let xml = test_xml_fragment(
r#"
<AffiliationIdentifier xmlns="urn:oasis:names:tc:evs:schema:eml" Id="1">
<RegisteredName>Affiliation 1</RegisteredName>
</AffiliationIdentifier>
"#,
);
let affiliation_identifier = AffiliationIdentifier::parse_eml(&xml, EMLParsingMode::Strict)
.ok()
.unwrap();
assert_eq!(
affiliation_identifier.id,
StringValue::Parsed(AffiliationId::new("1").unwrap())
);
assert_eq!(
affiliation_identifier.registered_name,
Some("Affiliation 1".to_string())
);
let xml_output = test_write_eml_element(&affiliation_identifier, &[NS_EML]).unwrap();
assert_eq!(xml_output, xml);
}
#[test]
fn test_empty_affiliation_identifier() {
let xml = test_xml_fragment(
r#"
<AffiliationIdentifier xmlns="urn:oasis:names:tc:evs:schema:eml" Id="2">
<RegisteredName/>
</AffiliationIdentifier>
"#,
);
let affiliation_identifier = AffiliationIdentifier::parse_eml(&xml, EMLParsingMode::Strict)
.ok()
.unwrap();
assert_eq!(
affiliation_identifier.id,
StringValue::Parsed(AffiliationId::new("2").unwrap())
);
assert_eq!(affiliation_identifier.registered_name, None);
let xml_output = test_write_eml_element(&affiliation_identifier, &[NS_EML]).unwrap();
assert_eq!(xml_output, xml);
}
#[test]
fn test_qualifying_address_full() {
let c = QualifyingAddressCountry::new(
Some("NL"),
QualifyingAddressLocality::new("Amsterdam")
.with_address_line(
AddressLine::new("Test 1")
.with_code("TestCode")
.with_type("TestType"),
)
.with_postal_code(
PostalCodeNumber::new("1234 AB")
.with_code("TestCode")
.with_type("TestType"),
)
.with_indicator("Test")
.with_locality_type("City")
.with_usage_type("Example"),
);
assert_eq!(c.country_name_code, Some(CountryNameCode::new("NL")));
assert_eq!(c.locality.locality_name.name, "Amsterdam");
assert_eq!(c.locality.address_line.as_ref().unwrap().value, "Test 1");
assert_eq!(
c.locality
.address_line
.as_ref()
.unwrap()
.code
.as_ref()
.unwrap(),
"TestCode"
);
assert_eq!(
c.locality
.address_line
.as_ref()
.unwrap()
.address_line_type
.as_ref()
.unwrap(),
"TestType"
);
assert_eq!(
c.locality
.postal_code
.as_ref()
.unwrap()
.postal_code_number
.value,
"1234 AB"
);
assert_eq!(
c.locality
.postal_code
.as_ref()
.unwrap()
.postal_code_number
.code
.as_ref()
.unwrap(),
"TestCode"
);
assert_eq!(
c.locality
.postal_code
.as_ref()
.unwrap()
.postal_code_number
.postal_code_number_type
.as_ref()
.unwrap(),
"TestType"
);
assert_eq!(c.locality.indicator.as_ref().unwrap(), "Test");
assert_eq!(c.locality.locality_type.as_ref().unwrap(), "City");
assert_eq!(c.locality.usage_type.as_ref().unwrap(), "Example");
test_write_eml_element(&c, &[NS_XAL]).unwrap();
}
#[test]
fn test_qualifying_address_parsing() {
let xml = test_xml_fragment(
r#"
<QualifyingAddress xmlns="urn:oasis:names:tc:evs:schema:eml" xmlns:xal="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0">
<xal:Country>
<xal:Locality>
<xal:LocalityName>Amsterdam</xal:LocalityName>
</xal:Locality>
</xal:Country>
</QualifyingAddress>
"#,
);
let qualifying_address = QualifyingAddress::parse_eml(&xml, EMLParsingMode::Strict)
.ok()
.unwrap();
match &qualifying_address {
QualifyingAddress::Country(country) => {
assert_eq!(country.country_name_code, None);
assert_eq!(country.locality.locality_name.name, "Amsterdam");
}
_ => panic!("Expected country qualifying address"),
}
let xml_output = test_write_eml_element(&qualifying_address, &[NS_EML, NS_XAL]).unwrap();
assert_eq!(xml_output, xml);
}
#[test]
fn candidate_lists_construction() {
let cl = CandidateLists::builder()
.lists_type(CandidateListsType::Single)
.transaction_id(TransactionId::new(1))
.managing_authority(ManagingAuthority::new(AuthorityId::new("1234").unwrap()))
.issue_date(XsDate::from_date(2024, 6, 10).unwrap())
.creation_date_time(
chrono::Utc
.with_ymd_and_hms(2014, 11, 28, 12, 0, 9)
.unwrap(),
)
.election_identifier(
CandidateListsElectionIdentifier::builder()
.id(ElectionId::new("GR2026_Test").unwrap())
.category(ElectionCategory::GR)
.election_date(XsDate::from_date(2024, 11, 5).unwrap())
.nomination_date(XsDate::from_date(2024, 10, 1).unwrap())
.build_for_candidate_lists()
.unwrap(),
)
.contests([CandidateListsContest::builder()
.identifier(ContestIdentifier::geen())
.affiliations([CandidateListsAffiliation::builder()
.id(AffiliationId::new("1").unwrap())
.registered_name("Affiliation 1")
.affiliation_type(AffiliationType::StandAloneList)
.publish_gender(true)
.candidates([CandidateListsCandidate::builder()
.identifier(CandidateId::new("1").unwrap())
.full_name(
PersonName::new("Pietersen")
.with_initials("P.")
.with_first_name("Piet"),
)
.qualifying_address(QualifyingAddressCountry::new(Some("NL"), "Amsterdam"))
.build()
.unwrap()])
.build()
.unwrap()])
.build()
.unwrap()])
.build()
.unwrap();
let xml = cl.write_eml_root_str(true, true).unwrap();
assert_eq!(
xml,
include_str!(
"../../test-emls/candidate_lists/eml230b_candidate_lists_construction_output.eml.xml"
)
);
let parsed = CandidateLists::parse_eml(&xml, EMLParsingMode::Strict).unwrap();
let xml2 = parsed.write_eml_root_str(true, true).unwrap();
assert_eq!(xml, xml2);
}
#[test]
fn test_invalid_document_type() {
assert!(
CandidateLists::parse_eml(
include_str!(
"../../test-emls/candidate_lists/eml230b_invalid_document_type.eml.xml"
),
EMLParsingMode::Strict
)
.ok_with_errors()
.is_err()
);
}
#[test]
fn test_invalid_empty_affiliates() {
assert!(
CandidateLists::parse_eml(
include_str!(
"../../test-emls/candidate_lists/eml230b_invalid_empty_affiliates.eml.xml"
),
EMLParsingMode::Strict
)
.ok_with_errors()
.is_err()
);
}
#[test]
fn test_invalid_empty_candidates() {
assert!(
CandidateLists::parse_eml(
include_str!(
"../../test-emls/candidate_lists/eml230b_invalid_empty_candidates.eml.xml"
),
EMLParsingMode::Strict
)
.ok_with_errors()
.is_err()
);
}
#[test]
fn test_invalid_incorrect_election_date() {
assert!(
CandidateLists::parse_eml(
include_str!(
"../../test-emls/candidate_lists/eml230b_invalid_incorrect_election_date.eml.xml"
),
EMLParsingMode::Strict
)
.ok_with_errors()
.is_err()
);
}
#[test]
fn test_incorrect_election_domain() {
assert!(
CandidateLists::parse_eml(
include_str!(
"../../test-emls/candidate_lists/eml230b_invalid_incorrect_election_domain.eml.xml"
),
EMLParsingMode::Strict
)
.ok_with_errors()
.is_err()
);
}
#[test]
fn test_incorrect_election_category() {
assert!(
CandidateLists::parse_eml(
include_str!(
"../../test-emls/candidate_lists/eml230b_invalid_incorrect_election_category.eml.xml"
),
EMLParsingMode::Strict
)
.ok_with_errors()
.is_err()
);
}
#[test]
fn test_incorrect_missing_authority() {
assert!(
CandidateLists::parse_eml(
include_str!(
"../../test-emls/candidate_lists/eml230b_invalid_missing_authority.eml.xml"
),
EMLParsingMode::Strict
)
.ok_with_errors()
.is_err()
);
}
#[test]
fn test_with_missing_addresses() {
assert!(
CandidateLists::parse_eml(
include_str!(
"../../test-emls/candidate_lists/eml230b_test_without_addresses.eml.xml"
),
EMLParsingMode::Strict
)
.ok_with_errors()
.is_ok()
);
}
}