use super::{expand_iri, ExpandedEntry};
use crate::{
loader,
object::*,
syntax::{Keyword, Term},
ContextMut, Direction, Error, ErrorCode, Id, Indexed, LangString, Loc, Reference, Warning,
};
use generic_json::{JsonClone, JsonHash, ValueRef};
use langtag::LanguageTagBuf;
use std::convert::TryFrom;
pub(crate) fn expand_value<'e, J: JsonHash + JsonClone, T: Id, C: ContextMut<T>>(
source: Option<loader::Id>,
input_type: Option<Term<T>>,
type_scoped_context: &C,
expanded_entries: Vec<ExpandedEntry<'e, J, Term<T>>>,
value_entry: &J,
warnings: &mut Vec<Loc<Warning, J::MetaData>>,
) -> Result<Option<Indexed<Object<J, T>>>, Error>
where
J::Object: 'e,
{
let mut is_json = input_type == Some(Term::Keyword(Keyword::Json));
let mut ty = None;
let mut index = None;
let mut language = None;
let mut direction = None;
for ExpandedEntry(_, expanded_key, value) in expanded_entries {
match expanded_key {
Term::Keyword(Keyword::Language) => {
let value_metadata = value.metadata();
if let Some(value) = value.as_str() {
if value != "@none" {
language = Some((value.to_string(), value_metadata.clone()));
}
} else {
return Err(ErrorCode::InvalidLanguageTaggedString.into());
}
}
Term::Keyword(Keyword::Direction) => {
if let Some(value) = value.as_str() {
if let Ok(value) = Direction::try_from(value) {
direction = Some(value);
} else {
return Err(ErrorCode::InvalidBaseDirection.into());
}
} else {
return Err(ErrorCode::InvalidBaseDirection.into());
}
}
Term::Keyword(Keyword::Index) => {
if let Some(value) = value.as_str() {
index = Some(value.to_string())
} else {
return Err(ErrorCode::InvalidIndexValue.into());
}
}
Term::Keyword(Keyword::Type) => {
if let Some(ty_value) = value.as_str() {
let expanded_ty = expand_iri(
source,
type_scoped_context,
ty_value,
value.metadata(),
true,
true,
warnings,
);
match expanded_ty {
Term::Keyword(Keyword::Json) => {
is_json = true;
}
Term::Ref(Reference::Id(expanded_ty)) => {
is_json = false;
ty = Some(expanded_ty)
}
_ => return Err(ErrorCode::InvalidTypedValue.into()),
}
} else {
return Err(ErrorCode::InvalidTypedValue.into());
}
}
Term::Keyword(Keyword::Value) => (),
_ => {
return Err(ErrorCode::InvalidValueObject.into());
}
}
}
if is_json {
if language.is_some() || direction.is_some() {
return Err(ErrorCode::InvalidValueObject.into());
}
return Ok(Some(Indexed::new(
Object::Value(Value::Json(value_entry.clone())),
index,
)));
}
let result = match value_entry.as_value_ref() {
ValueRef::Null => Literal::Null,
ValueRef::String(s) => Literal::String(LiteralString::Expanded(s.clone())),
ValueRef::Number(n) => Literal::Number(n.clone()),
ValueRef::Boolean(b) => Literal::Boolean(b),
_ => {
return Err(ErrorCode::InvalidValueObjectValue.into());
}
};
if matches!(result, Literal::Null) {
return Ok(None);
}
if language.is_some() || direction.is_some() {
if ty.is_some() {
return Err(ErrorCode::InvalidValueObject.into());
}
if let Literal::String(str) = result {
let lang = match language {
Some(language) => {
let (language, language_metadata) = language;
match LanguageTagBuf::parse_copy(language.as_str()) {
Ok(lang) => Some(lang.into()),
Err(err) => {
warnings.push(Loc::new(
Warning::MalformedLanguageTag(language.to_string(), err),
source,
language_metadata,
));
Some(language.to_string().into())
}
}
}
None => None,
};
return match LangString::new(str, lang, direction) {
Ok(result) => Ok(Some(Indexed::new(
Object::Value(Value::LangString(result)),
index,
))),
Err(_) => Err(ErrorCode::InvalidLanguageTaggedValue.into()),
};
} else {
return Err(ErrorCode::InvalidLanguageTaggedValue.into());
}
}
Ok(Some(Indexed::new(
Object::Value(Value::Literal(result, ty)),
index,
)))
}