use crate::{
lint::{
common_error_literals::NO_ROOT_PREFIX,
helpers::{
catch_single_or_multiple_annotations_which_must_exist,
fail_if_contains_inappropriate_word,
},
lint_failure, lint_success, Lint, LintResult,
},
Linter, MultiReaderRdfGraph,
};
use field33_rdftk_iri_temporary_fork::IRI as RDFTK_IRI;
use plow_ontology::constants::REGISTRY_AUTHOR;
use plow_package_management::metadata::get_root_prefix;
use std::str::FromStr;
use std::{any::Any, collections::HashSet};
const RELATED_FIELD: &str = "`registry:author`";
const AUTHOR_NAME_MAX_ALLOWED_CHAR_COUNT: usize = 50;
#[derive(Debug, Default)]
pub struct HasRegistryAuthor;
impl Lint for HasRegistryAuthor {
fn as_any(&self) -> &dyn Any {
self
}
fn short_description(&self) -> &str {
"Check that the field is annotated with a value for `registry:author`"
}
fn run(
&self,
Linter {
document,
graph: MultiReaderRdfGraph { inner: rdf_graph },
..
}: &Linter,
) -> LintResult {
let rdf_factory = field33_rdftk_core_temporary_fork::simple::statement::statement_factory();
if let Some(root_prefix) = get_root_prefix(document) {
let graph_ref = rdf_graph;
let graph = graph_ref.borrow();
#[allow(clippy::unwrap_used)]
let annotations = graph
.statements()
.filter(|statement| {
statement.subject()
== &rdf_factory
.named_subject(RDFTK_IRI::from_str(root_prefix).unwrap().into())
&& statement.predicate()
== &RDFTK_IRI::from_str(REGISTRY_AUTHOR).unwrap().into()
})
.collect::<HashSet<_>>();
if let Some(failure) =
catch_single_or_multiple_annotations_which_must_exist(&annotations, RELATED_FIELD)
{
return failure;
}
let lint_prefix = RELATED_FIELD.to_owned();
let lint_results = annotations.iter().map(|annotation| {
annotation.object().as_literal().map_or_else(
|| lint_failure!(format!("{lint_prefix} is not a literal.")),
|literal| {
if let Some(failure) = fail_if_contains_inappropriate_word(literal,&lint_prefix ) {
return failure;
}
let author_literal = literal.lexical_form();
let name_and_email_raw = author_literal.split('<').collect::<Vec<_>>();
name_and_email_raw.first().map_or_else(|| {
lint_failure!(format!("{lint_prefix} is not in its right form. Example of a right form: \"An Author's Name <email@oftheauthor.com>\"."))
}, |maybe_name| if maybe_name.ends_with(' ') {
let trimmed_maybe_name = maybe_name.trim();
if trimmed_maybe_name.contains('>') || trimmed_maybe_name.contains('<')
{
return lint_failure!(format!("The name field in {lint_prefix} can not contain neither '<' nor '>' characters."));
}
if trimmed_maybe_name.chars().count()
> (AUTHOR_NAME_MAX_ALLOWED_CHAR_COUNT + 1)
{
return lint_failure!(format!("The name field in {lint_prefix} allows a maximum of {AUTHOR_NAME_MAX_ALLOWED_CHAR_COUNT} characters."));
}
name_and_email_raw.get(1).map_or_else(|| lint_failure!(format!("{lint_prefix} is not in its right form. Example of a right form: \"An Author's Name <email@oftheauthor.com>\".")), |maybe_email| {
let maybe_email = maybe_email.split('>').collect::<Vec<_>>();
if maybe_email.len() == 1 {
return lint_failure!(format!("The email field in {lint_prefix} should finish with a '>' character."));
}
maybe_email.first().map_or_else(|| lint_failure!(format!(
"The email field in {lint_prefix} can not be parsed."
)), |maybe_email| {
let email_valid = email_address::EmailAddress::is_valid(maybe_email);
if email_valid {
lint_success!(format!("{lint_prefix} exists and is valid."))
} else {
lint_failure!(format!(
"The email field in {lint_prefix} is invalid."
))
}
})
})
} else {
lint_failure!(format!("The name and email fields in {lint_prefix} is not separated with a white space character."))
})
},
)
}).collect::<Vec<LintResult>>();
for result in lint_results {
if let LintResult::Failure(messages) = result {
return lint_failure!(format!(
"Some {lint_prefix} annotations are invalid. More info: {}",
messages.join(", ")
));
}
}
lint_success!(format!("All {lint_prefix} annotations are valid."))
} else {
lint_failure!(NO_ROOT_PREFIX)
}
}
}