use super::{expand_iri, node_id_of_term, ActiveProperty};
use crate::{
loader, object::*, syntax::Type, Context, Error, ErrorCode, Id, Indexed, LangString, Loc,
Warning,
};
use generic_json::{Json, JsonClone, JsonHash, ValueRef};
pub enum LiteralValue<'a, J: Json> {
Given(&'a J),
Inferred(String, J::MetaData),
}
impl<'a, J: Json> LiteralValue<'a, J> {
pub fn is_string(&self) -> bool {
match self {
Self::Given(v) => v.is_string(),
Self::Inferred(_, _) => true,
}
}
pub fn as_str(&self) -> Option<&str> {
match self {
Self::Given(v) => v.as_str(),
Self::Inferred(s, _) => Some(s.as_str()),
}
}
pub fn metadata(&self) -> &J::MetaData {
match self {
Self::Given(v) => v.metadata(),
Self::Inferred(_, meta) => meta,
}
}
}
pub fn expand_literal<J: JsonHash + JsonClone, T: Id, C: Context<T>>(
source: Option<loader::Id>,
active_context: &C,
active_property: ActiveProperty<J>,
value: LiteralValue<J>,
warnings: &mut Vec<Loc<Warning, J::MetaData>>,
) -> Result<Indexed<Object<J, T>>, Error> {
let active_property_definition = active_context.get_opt(active_property.id());
let active_property_type = if let Some(active_property_definition) = active_property_definition
{
active_property_definition.typ.clone()
} else {
None
};
match active_property_type {
Some(Type::Id) if value.is_string() => {
let mut node = Node::new();
node.id = node_id_of_term(expand_iri(
source,
active_context,
value.as_str().unwrap(),
value.metadata(),
true,
false,
warnings,
));
Ok(Object::Node(node).into())
}
Some(Type::Vocab) if value.is_string() => {
let mut node = Node::new();
node.id = node_id_of_term(expand_iri(
source,
active_context,
value.as_str().unwrap(),
value.metadata(),
true,
true,
warnings,
));
Ok(Object::Node(node).into())
}
_ => {
let result: Literal<J> = match value {
LiteralValue::Given(v) => match v.as_value_ref() {
ValueRef::Null => Literal::Null,
ValueRef::Boolean(b) => Literal::Boolean(b),
ValueRef::Number(n) => Literal::Number(n.clone()),
ValueRef::String(s) => Literal::String(LiteralString::Expanded(s.clone())),
_ => panic!("expand_literal must be called with a literal JSON value"),
},
LiteralValue::Inferred(s, _) => Literal::String(LiteralString::Inferred(s)),
};
let mut ty = None;
match active_property_type {
None | Some(Type::Id) | Some(Type::Vocab) | Some(Type::None) => {
if let Literal::String(str) = result {
let language =
if let Some(active_property_definition) = active_property_definition {
if let Some(language) = &active_property_definition.language {
language.as_ref().cloned().option()
} else {
active_context.default_language().map(|lang| lang.cloned())
}
} else {
active_context.default_language().map(|lang| lang.cloned())
};
let direction =
if let Some(active_property_definition) = active_property_definition {
if let Some(direction) = &active_property_definition.direction {
(*direction).option()
} else {
active_context.default_base_direction()
}
} else {
active_context.default_base_direction()
};
return match LangString::new(str, language, direction) {
Ok(lang_str) => Ok(Object::Value(Value::LangString(lang_str)).into()),
Err(str) => Ok(Object::Value(Value::Literal(
Literal::String(str),
None,
))
.into()),
};
}
}
Some(t) => {
if let Ok(t) = t.into_ref() {
ty = Some(t)
} else {
return Err(ErrorCode::InvalidTypeValue.into());
}
}
}
Ok(Object::Value(Value::Literal(result, ty)).into())
}
}
}