use crate::{
loader,
syntax::{is_keyword_like, Keyword, Term},
BlankId, Context, Id, Loc, Reference, Warning,
};
use iref::{Iri, IriRef};
use std::convert::TryFrom;
pub fn expand_iri<T: Id, C: Context<T>, M: Clone>(
source: Option<loader::Id>,
active_context: &C,
value: &str,
metadata: &M,
document_relative: bool,
vocab: bool,
warnings: &mut Vec<Loc<Warning, M>>,
) -> Term<T> {
if let Ok(keyword) = Keyword::try_from(value) {
Term::Keyword(keyword)
} else {
if is_keyword_like(value) {
warnings.push(Loc::new(
Warning::KeywordLikeValue(value.to_string()),
source,
metadata.clone(),
));
return Term::Null;
}
if let Some(term_definition) = active_context.get(value) {
if let Some(value) = &term_definition.value {
if value.is_keyword() {
return value.clone();
}
}
if vocab {
if let Some(mapped_value) = &term_definition.value {
return mapped_value.clone();
} else {
return invalid(value.to_string(), source, metadata, warnings);
}
}
}
if let Some(index) = value.find(':') {
if index > 0 {
let (prefix, suffix) = value.split_at(index);
let suffix = &suffix[1..suffix.len()];
if prefix == "_" {
return Term::from(BlankId::new(suffix));
}
if suffix.starts_with("//") {
if let Ok(iri) = Iri::new(value) {
return Term::from(T::from_iri(iri));
} else {
return invalid(value.to_string(), source, metadata, warnings);
}
}
if let Some(term_definition) = active_context.get(prefix) {
if term_definition.prefix {
if let Some(mapping) = &term_definition.value {
let mut result = mapping.as_str().to_string();
result.push_str(suffix);
if let Ok(result) = Iri::new(&result) {
return Term::from(T::from_iri(result));
} else if let Ok(blank) = BlankId::try_from(result.as_ref()) {
return Term::from(blank);
} else {
return Reference::Invalid(result).into();
}
}
}
}
if let Ok(result) = Iri::new(value) {
return Term::from(T::from_iri(result));
}
}
}
if vocab {
match active_context.vocabulary() {
Some(Term::Ref(mapping)) => {
let mut result = mapping.as_str().to_string();
result.push_str(value);
if let Ok(result) = Iri::new(&result) {
return Term::from(T::from_iri(result));
} else if let Ok(blank) = BlankId::try_from(result.as_ref()) {
return Term::from(blank);
} else {
return Reference::Invalid(result).into();
}
}
Some(_) => return invalid(value.to_string(), source, metadata, warnings),
None => (),
}
}
if document_relative {
if let Ok(iri_ref) = IriRef::new(value) {
if let Some(base_iri) = active_context.base_iri() {
let value = iri_ref.resolved(base_iri);
return Term::from(T::from_iri(value.as_iri()));
} else {
return invalid(value.to_string(), source, metadata, warnings);
}
} else {
return invalid(value.to_string(), source, metadata, warnings);
}
}
invalid(value.to_string(), source, metadata, warnings)
}
}
fn invalid<T: Id, M: Clone>(
value: String,
source: Option<loader::Id>,
metadata: &M,
warnings: &mut Vec<Loc<Warning, M>>,
) -> Term<T> {
warnings.push(Loc::new(
Warning::MalformedIri(value.clone()),
source,
metadata.clone(),
));
Reference::Invalid(value).into()
}