use rdf_syntax::BlankId;
use rdf_syntax::{Iri, IriBuf, IriRef};
use crate::{
JsonLdBacktraceBuilder, JsonLdLenient, JsonLdLocatedError, JsonLdNullable, JsonLdTerm,
JsonLdWarning,
algorithms::{
AsyncJsonLdEnvironment,
context_processing::{ContextProcessor, TargetProcessedContext, merged::Merged},
},
context::RawJsonLdProcessedContext,
syntax::{
JsonLdCompactIri, JsonLdExpandableRef,
context::{JsonLdContextTerm, JsonLdKeyOrKeywordRef},
is_keyword_like,
},
};
pub fn resolve_iri(iri_ref: &IriRef, base_iri: Option<&Iri>) -> Option<IriBuf> {
match base_iri {
Some(base_iri) => Some(iri_ref.resolved(base_iri)),
None => iri_ref.as_iri().map(ToOwned::to_owned),
}
}
pub type ExpandIriResult = Result<JsonLdTerm, JsonLdLocatedError>;
impl<'a> ContextProcessor<'a> {
pub async fn expand_iri_recursive(
&self,
env: &impl AsyncJsonLdEnvironment,
result: &mut TargetProcessedContext<'_>,
local_context: &Merged<'_>,
value: JsonLdNullable<JsonLdExpandableRef<'_>>,
context_loc: JsonLdBacktraceBuilder<'_>,
) -> ExpandIriResult {
match value {
JsonLdNullable::Null => Ok(JsonLdTerm::Null),
JsonLdNullable::Some(JsonLdExpandableRef::Keyword(k)) => Ok(JsonLdTerm::Keyword(k)),
JsonLdNullable::Some(JsonLdExpandableRef::String(value)) => {
if is_keyword_like(value) {
return Ok(JsonLdTerm::Null);
}
Box::pin(self.for_recursive_definition().define(
env,
result,
local_context,
value.into(),
false,
context_loc,
))
.await?;
if let Some(term_definition) = result.value.get(value) {
if let Some(value) = term_definition.value()
&& value.is_keyword()
{
return Ok(value.clone());
}
return match term_definition.value() {
Some(value) => Ok(value.clone()),
None => Ok(JsonLdTerm::Null),
};
}
if value.find(':').map(|i| i > 0).unwrap_or(false) {
if let Ok(blank_id) = BlankId::new(value) {
return Ok(JsonLdTerm::Id(JsonLdLenient::blank(blank_id.to_owned())));
}
if value == "_:" {
return Ok(JsonLdTerm::Id(JsonLdLenient::Invalid("_:".to_string())));
}
if let Ok(compact_iri) = JsonLdCompactIri::new(value) {
Box::pin(self.for_recursive_definition().define(
env,
result,
local_context,
JsonLdKeyOrKeywordRef::Key(compact_iri.prefix().into()),
false,
context_loc,
))
.await?;
let prefix_key = JsonLdContextTerm::from(compact_iri.prefix().to_string());
if let Some(term_definition) = result.value.get_normal(&prefix_key)
&& term_definition.prefix
&& let Some(mapping) = &term_definition.value
{
let mut result = mapping.as_str().to_owned();
result.push_str(compact_iri.suffix());
return Ok(JsonLdTerm::Id(JsonLdLenient::from_string(result).0));
}
}
if let Ok(iri) = Iri::new(value) {
return Ok(JsonLdTerm::Id(JsonLdLenient::iri(iri.to_owned())));
}
}
match result.value.vocabulary() {
Some(JsonLdTerm::Id(mapping)) => {
let mut result = mapping.as_str().to_owned();
result.push_str(value);
return Ok(JsonLdTerm::Id(JsonLdLenient::from_string(result).0));
}
Some(_) => return Ok(invalid_iri(value.to_owned(), |w| env.warn(w))),
None => (),
}
Ok(invalid_iri(value.to_owned(), |w| env.warn(w)))
}
}
}
}
impl RawJsonLdProcessedContext {
pub fn expand_iri(
&self,
value: JsonLdNullable<JsonLdExpandableRef<'_>>,
document_relative: bool,
vocab: bool,
) -> JsonLdTerm {
self.expand_iri_with(value, document_relative, vocab, |_| {})
}
pub fn expand_iri_with(
&self,
value: JsonLdNullable<JsonLdExpandableRef<'_>>,
document_relative: bool,
vocab: bool,
on_warning: impl FnOnce(JsonLdWarning),
) -> JsonLdTerm {
match value {
JsonLdNullable::Null => JsonLdTerm::Null,
JsonLdNullable::Some(JsonLdExpandableRef::Keyword(k)) => JsonLdTerm::Keyword(k),
JsonLdNullable::Some(JsonLdExpandableRef::String(value)) => {
if is_keyword_like(value) {
return JsonLdTerm::Null;
}
if let Some(term_definition) = self.get(value) {
if let Some(value) = term_definition.value()
&& value.is_keyword()
{
return value.clone();
}
if vocab {
return match term_definition.value() {
Some(value) => value.clone(),
None => JsonLdTerm::Null,
};
}
}
if value.find(':').map(|i| i > 0).unwrap_or(false) {
if let Ok(blank_id) = BlankId::new(value) {
return JsonLdTerm::Id(JsonLdLenient::blank(blank_id.to_owned()));
}
if value == "_:" {
return JsonLdTerm::Id(JsonLdLenient::Invalid("_:".to_string()));
}
if let Ok(compact_iri) = JsonLdCompactIri::new(value) {
let prefix_key = JsonLdContextTerm::from(compact_iri.prefix().to_string());
if let Some(term_definition) = self.get_normal(&prefix_key)
&& term_definition.prefix
&& let Some(mapping) = &term_definition.value
{
let mut result = mapping.as_str().to_owned();
result.push_str(compact_iri.suffix());
return JsonLdTerm::Id(JsonLdLenient::from_string(result).0);
}
}
if let Ok(iri) = Iri::new(value) {
return JsonLdTerm::Id(JsonLdLenient::iri(iri.to_owned()));
}
}
if vocab {
match self.vocabulary() {
Some(JsonLdTerm::Id(mapping)) => {
let mut result = mapping.as_str().to_owned();
result.push_str(value);
return JsonLdTerm::Id(JsonLdLenient::from_string(result).0);
}
Some(_) => return invalid_iri(value.to_string(), on_warning),
None => (),
}
}
if document_relative
&& let Ok(iri_ref) = IriRef::new(value)
&& let Some(iri) = resolve_iri(iri_ref, self.base_iri())
{
return JsonLdTerm::from(iri);
}
invalid_iri(value.to_string(), on_warning)
}
}
}
}
fn invalid_iri(value: String, on_warning: impl FnOnce(JsonLdWarning)) -> JsonLdTerm {
(on_warning)(JsonLdWarning::MalformedIri(value.clone()));
JsonLdTerm::Id(JsonLdLenient::Invalid(value))
}