use std::borrow::Cow;
use json_syntax::{JsonNumber, JsonNumberBuf, JsonValue};
use crate::{
JsonLdBacktraceBuilder, JsonLdError, JsonLdIndexedObject, JsonLdLangString, JsonLdLenient,
JsonLdLocatedError, JsonLdNode, JsonLdNullable, JsonLdObject, JsonLdType, JsonLdValue,
JsonLdWarning,
object::{JsonLdLiteralValue, value::JsonLdLiteralType},
};
use super::{Expander, node_id_of_term};
pub enum ExpandableLiteralValue<'a> {
Boolean(bool),
Number(&'a JsonNumber),
String(Cow<'a, str>),
}
impl<'a> ExpandableLiteralValue<'a> {
pub fn new(value: &'a JsonValue) -> Self {
match value {
JsonValue::Boolean(b) => Self::Boolean(*b),
JsonValue::Number(n) => Self::Number(n),
JsonValue::String(s) => Self::String(Cow::Borrowed(s)),
_ => panic!("not a literal value"),
}
}
pub fn is_string(&self) -> bool {
matches!(self, Self::String(_))
}
pub fn as_str(&self) -> Option<&str> {
match self {
Self::String(s) => Some(s),
_ => None,
}
}
}
impl<'a> From<&'a str> for ExpandableLiteralValue<'a> {
fn from(value: &'a str) -> Self {
Self::String(Cow::Borrowed(value))
}
}
impl<'a> Expander<'a> {
pub fn expand_literal(
&self,
warn: impl FnOnce(JsonLdWarning),
value: ExpandableLiteralValue,
location: JsonLdBacktraceBuilder<'_>,
) -> Result<JsonLdIndexedObject, JsonLdLocatedError> {
let active_property_definition = self.active_property_definition();
let active_property_type =
if let Some(active_property_definition) = active_property_definition {
active_property_definition.typ().cloned()
} else {
None
};
match active_property_type {
Some(JsonLdType::Id) if value.is_string() => {
let mut node = JsonLdNode::new();
let id = node_id_of_term(self.active_context.expand_iri_with(
JsonLdNullable::Some(value.as_str().unwrap().into()),
true,
false,
warn,
));
node.id = id;
Ok(JsonLdObject::node(node).into())
}
Some(JsonLdType::Vocab) if value.is_string() => {
let mut node = JsonLdNode::new();
let id = node_id_of_term(self.active_context.expand_iri_with(
JsonLdNullable::Some(value.as_str().unwrap().into()),
true,
true,
warn,
));
node.id = id;
Ok(JsonLdObject::node(node).into())
}
_ => {
let result_value: JsonValue = match value {
ExpandableLiteralValue::Boolean(b) => JsonValue::Boolean(b),
ExpandableLiteralValue::Number(n) => JsonValue::Number(unsafe {
JsonNumberBuf::new_unchecked(n.as_bytes().into())
}),
ExpandableLiteralValue::String(s) => JsonValue::String(s.into_owned().into()),
};
let mut ty = None;
match active_property_type {
None
| Some(JsonLdType::Id)
| Some(JsonLdType::Vocab)
| Some(JsonLdType::None) => {
if let JsonValue::String(s) = result_value {
let language = if let Some(active_property_definition) =
active_property_definition
{
if let Some(language) = active_property_definition.language() {
language.cloned().option()
} else {
self.active_context
.default_language()
.map(JsonLdLenient::into_owned)
}
} else {
self.active_context
.default_language()
.map(JsonLdLenient::into_owned)
};
let direction = if let Some(active_property_definition) =
active_property_definition
{
if let Some(direction) = active_property_definition.direction() {
direction.option()
} else {
self.active_context.default_base_direction()
}
} else {
self.active_context.default_base_direction()
};
return match JsonLdLangString::new(s, language, direction) {
Ok(lang_str) => {
Ok(JsonLdObject::Value(JsonLdValue::LangString(lang_str))
.into())
}
Err(s) => Ok(JsonLdObject::Value(JsonLdValue::Literal(
JsonLdLiteralValue::new(JsonValue::String(s), None),
))
.into()),
};
}
}
Some(t) => {
if let Ok(t) = t.into_iri() {
ty = Some(t)
} else {
return Err(JsonLdError::InvalidTypeValue.at(location));
}
}
}
Ok(
JsonLdObject::Value(JsonLdValue::Literal(JsonLdLiteralValue::new(
result_value,
ty.map(JsonLdLiteralType::Iri),
)))
.into(),
)
}
}
}
}