use super::{
add_value, compact_collection_with, compact_iri, compact_iri_with, value_value, Compact,
CompactIndexed, JsonSrc, Options,
};
use crate::{
context::{Inversible, Loader},
object,
syntax::{Container, ContainerType, Keyword, Term},
util::JsonFrom,
ContextMut, Error, ErrorCode, Id, Indexed, Node, Object, Reference,
};
use cc_traits::Len;
use generic_json::{JsonBuild, JsonClone, JsonHash, JsonIntoMut, JsonMut, ValueMut};
async fn compact_property_list<
J: JsonClone + JsonHash,
K: JsonFrom<J>,
T: Sync + Send + Id,
C: ContextMut<T>,
L: Loader,
M,
>(
list: &[Indexed<Object<J, T>>],
expanded_index: Option<&str>,
nest_result: &mut K::Object,
container: Container,
as_array: bool,
item_active_property: &str,
active_context: Inversible<T, &C>,
loader: &mut L,
options: Options,
meta: M,
) -> Result<(), Error>
where
J: JsonSrc,
C: Sync + Send,
C::LocalContext: Send + Sync + From<L::Output>,
L: Sync + Send,
M: Send + Sync + Clone + Fn(Option<&J::MetaData>) -> K::MetaData,
{
let mut compacted_item: K = compact_collection_with(
list.iter(),
active_context.clone(),
active_context.clone(),
Some(item_active_property),
loader,
options,
meta.clone(),
)
.await?;
if !compacted_item.is_array() {
let mut array = K::Array::default();
array.push_back(compacted_item);
compacted_item = K::array(array, meta(None))
}
if !container.contains(ContainerType::List) {
let key = compact_iri::<J, T, C>(
active_context.clone(),
&Term::Keyword(Keyword::List),
true,
false,
options,
)?;
let mut compacted_item_list_object = K::Object::default();
compacted_item_list_object.insert(
K::new_key(key.unwrap().as_str(), meta(None)),
compacted_item,
);
if let Some(index) = expanded_index {
let key = compact_iri::<J, T, C>(
active_context.clone(),
&Term::Keyword(Keyword::Index),
true,
false,
options,
)?;
compacted_item_list_object.insert(
K::new_key(key.unwrap().as_str(), meta(None)),
K::string(index.into(), meta(None)),
);
}
compacted_item = K::object(compacted_item_list_object, meta(None));
add_value(
nest_result,
item_active_property,
compacted_item,
as_array,
|| meta(None),
)
} else {
nest_result.insert(K::new_key(item_active_property, meta(None)), compacted_item);
}
Ok(())
}
async fn compact_property_graph<
J: JsonSrc,
K: JsonFrom<J>,
T: Sync + Send + Id,
C: ContextMut<T>,
L: Loader,
M,
>(
node: &Node<J, T>,
expanded_index: Option<&str>,
nest_result: &mut K::Object,
container: Container,
as_array: bool,
item_active_property: &str,
active_context: Inversible<T, &C>,
loader: &mut L,
options: Options,
meta: M,
) -> Result<(), Error>
where
C: Sync + Send,
C::LocalContext: Send + Sync + From<L::Output>,
L: Sync + Send,
M: Send + Sync + Clone + Fn(Option<&J::MetaData>) -> K::MetaData,
{
let mut compacted_item: K = node
.graph
.as_ref()
.unwrap()
.compact_full(
active_context.clone(),
active_context.clone(),
Some(item_active_property),
loader,
options,
meta.clone(),
)
.await?;
if container.contains(ContainerType::Graph) && container.contains(ContainerType::Id) {
if nest_result.get(item_active_property).is_none() {
nest_result.insert(
K::new_key(item_active_property, meta(None)),
K::object(K::Object::default(), meta(None)),
);
}
let mut map_object = nest_result.get_mut(item_active_property).unwrap();
let map_object = map_object.as_object_mut().unwrap();
let (id_value, vocab): (Term<T>, bool) = match node.id() {
Some(term) => (term.clone().into_term(), false),
None => (Term::Keyword(Keyword::None), true),
};
let map_key =
compact_iri::<J, _, _>(active_context, &id_value, vocab, false, options)?.unwrap();
add_value(
map_object,
map_key.as_str(),
compacted_item,
as_array,
|| meta(None),
)
} else if container.contains(ContainerType::Graph)
&& container.contains(ContainerType::Index)
&& node.is_simple_graph()
{
if nest_result.get(item_active_property).is_none() {
nest_result.insert(
K::new_key(item_active_property, meta(None)),
K::object(K::Object::default(), meta(None)),
);
}
let mut map_object = nest_result.get_mut(item_active_property).unwrap();
let map_object = map_object.as_object_mut().unwrap();
let map_key = expanded_index.unwrap_or("@none");
add_value(map_object, map_key, compacted_item, as_array, || meta(None))
} else if container.contains(ContainerType::Graph) && node.is_simple_graph() {
compacted_item = match compacted_item.into_parts() {
(generic_json::Value::Array(items), items_meta) if items.len() > 1 => {
let key = compact_iri::<J, _, _>(
active_context,
&Term::Keyword(Keyword::Included),
true,
false,
options,
)?
.unwrap();
let mut map = K::Object::default();
map.insert(
K::new_key(key.as_str(), meta(None)),
K::array(items, items_meta),
);
K::object(map, meta(None))
}
(item, item_meta) => K::new(item, item_meta),
};
add_value(
nest_result,
item_active_property,
compacted_item,
as_array,
|| meta(None),
)
} else {
let key = compact_iri::<J, _, _>(
active_context.clone(),
&Term::Keyword(Keyword::Graph),
true,
false,
options,
)?
.unwrap();
let mut map = K::Object::default();
map.insert(K::new_key(key.as_str(), meta(None)), compacted_item);
if let Some(id) = node.id() {
let key = compact_iri::<J, _, _>(
active_context.clone(),
&Term::Keyword(Keyword::Id),
false,
false,
options,
)?
.unwrap();
let value = compact_iri::<J, _, _>(
active_context.clone(),
&id.clone().into_term(),
false,
false,
options,
)?;
map.insert(
K::new_key(key.as_str(), meta(None)),
match value {
Some(s) => K::string(s.as_str().into(), meta(None)),
None => K::null(meta(None)),
},
);
}
if let Some(index) = expanded_index {
let key = compact_iri::<J, _, _>(
active_context.clone(),
&Term::Keyword(Keyword::Index),
true,
false,
options,
)?
.unwrap();
map.insert(
K::new_key(key.as_str(), meta(None)),
K::string(index.into(), meta(None)),
);
}
let compacted_item = K::object(map, meta(None));
add_value(
nest_result,
item_active_property,
compacted_item,
as_array,
|| meta(None),
)
}
Ok(())
}
fn select_nest_result<'a, K: 'a + JsonBuild + JsonMut + JsonIntoMut, T: Id, C: ContextMut<T>, M>(
result: &'a mut K::Object,
active_context: Inversible<T, &C>,
item_active_property: &str,
compact_arrays: bool,
meta: M,
) -> Result<(&'a mut K::Object, Container, bool), Error>
where
M: Fn() -> K::MetaData,
{
let (nest_result, container) = match active_context.get(item_active_property) {
Some(term_definition) => {
let nest_result = match &term_definition.nest {
Some(nest_term) => {
if nest_term != "@nest" {
match active_context.get(nest_term.as_ref()) {
Some(term_def)
if term_def.value == Some(Term::Keyword(Keyword::Nest)) => {}
_ => return Err(ErrorCode::InvalidNestValue.into()),
}
}
if result.get(nest_term).is_none() {
result.insert(
K::new_key(nest_term.as_str(), meta()),
K::object(K::Object::default(), meta()),
);
}
let value: ValueMut<'a, K> = result.get_mut(nest_term).unwrap().into();
let sub_object: &'a mut K::Object = value.into_object_mut().unwrap();
sub_object
}
None => {
result
}
};
(nest_result, term_definition.container)
}
None => (result, Container::None),
};
let as_array = if container.contains(ContainerType::Set)
|| item_active_property == "@graph"
|| item_active_property == "@list"
{
true
} else {
!compact_arrays
};
Ok((nest_result, container, as_array))
}
pub async fn compact_property<
'a,
J: JsonSrc,
K: JsonFrom<J>,
T: 'a + Sync + Send + Id,
N: 'a + object::Any<J, T> + Sync + Send,
O: IntoIterator<Item = &'a Indexed<N>>,
C: ContextMut<T>,
L: Loader,
M: Send + Sync + Clone + Fn(Option<&J::MetaData>) -> K::MetaData,
>(
result: &mut K::Object,
expanded_property: Term<T>,
expanded_value: O,
active_context: Inversible<T, &C>,
loader: &mut L,
inside_reverse: bool,
options: Options,
meta: M,
) -> Result<(), Error>
where
C: Sync + Send,
C::LocalContext: Send + Sync + From<L::Output>,
L: Sync + Send,
{
let mut is_empty = true;
for expanded_item in expanded_value {
is_empty = false;
let item_active_property = compact_iri_with(
active_context.clone(),
&expanded_property,
expanded_item,
true,
inside_reverse,
options,
)?;
if let Some(item_active_property) = item_active_property {
let (nest_result, container, as_array): (&'_ mut K::Object, _, _) =
select_nest_result::<K, _, _, _>(
result,
active_context.clone(),
item_active_property.as_str(),
options.compact_arrays,
|| meta(None),
)?;
match expanded_item.inner().as_ref() {
object::Ref::List(list) => {
compact_property_list::<J, K, _, _, _, _>(
list,
expanded_item.index(),
nest_result,
container,
as_array,
item_active_property.as_str(),
active_context.clone(),
loader,
options,
meta.clone(),
)
.await?
}
object::Ref::Node(node) if node.is_graph() => {
compact_property_graph::<J, K, _, _, _, _>(
node,
expanded_item.index(),
nest_result,
container,
as_array,
item_active_property.as_str(),
active_context.clone(),
loader,
options,
meta.clone(),
)
.await?
}
_ => {
let mut compacted_item: K = expanded_item
.compact_full(
active_context.clone(),
active_context.clone(),
Some(item_active_property.as_str()),
loader,
options,
meta.clone(),
)
.await?;
if !container.contains(ContainerType::Graph)
&& (container.contains(ContainerType::Language)
|| container.contains(ContainerType::Index)
|| container.contains(ContainerType::Id)
|| container.contains(ContainerType::Type))
{
if nest_result.get(item_active_property.as_str()).is_none() {
nest_result.insert(
K::new_key(item_active_property.as_str(), meta(None)),
K::empty_object(meta(None)),
);
}
let mut map_object =
nest_result.get_mut(item_active_property.as_str()).unwrap();
let map_object = map_object.as_object_mut().unwrap();
let container_type = if container.contains(ContainerType::Language) {
ContainerType::Language
} else if container.contains(ContainerType::Index) {
ContainerType::Index
} else if container.contains(ContainerType::Id) {
ContainerType::Id
} else {
ContainerType::Type
};
let mut container_key = compact_iri::<J, _, _>(
active_context.clone(),
&Term::Keyword(container_type.into()),
true,
false,
options,
)?;
let index_key = match active_context.get(item_active_property.as_str()) {
Some(def) if def.index.is_some() => def.index.as_ref().unwrap(),
_ => "@index",
};
let map_key = if container_type == ContainerType::Language
&& expanded_item.is_value()
{
if let object::Ref::Value(value) = expanded_item.inner().as_ref() {
compacted_item = value_value(value, meta.clone())
}
expanded_item.language().map(|lang| lang.to_string())
} else if container_type == ContainerType::Index {
if index_key == "@index" {
expanded_item.index().map(|index| index.to_string())
} else {
container_key = compact_iri::<J, _, _>(
active_context.clone(),
&Term::Ref(Reference::Invalid(index_key.to_string())),
true,
false,
options,
)?;
let (map_key, remaining_values) = match compacted_item
.as_value_mut()
{
generic_json::ValueMut::Object(map) => {
match map.remove(container_key.as_ref().unwrap().as_str()) {
Some(value) => match value.into_parts() {
(generic_json::Value::String(s), _) => {
(Some((*s).to_string()), Vec::new())
}
(generic_json::Value::Array(values), _) => {
let mut values = values.into_iter();
match values.next() {
Some(first_value) => (
first_value
.as_str()
.map(|v| v.to_string()),
values.collect(),
),
None => (None, values.collect()),
}
}
(other_value, meta) => {
(None, vec![K::new(other_value, meta)])
}
},
None => (None, Vec::new()),
}
}
_ => (None, Vec::new()),
};
if !remaining_values.is_empty() {
if let Some(map) = compacted_item.as_object_mut() {
for value in remaining_values {
add_value(
map,
container_key.as_ref().unwrap().as_str(),
value,
false,
|| meta(None),
)
}
}
}
map_key
}
} else if container_type == ContainerType::Id {
compacted_item
.as_object_mut()
.and_then(|map| {
map.remove(container_key.unwrap().as_str())
.map(|value| value.as_str().map(|s| s.to_string()))
})
.flatten()
} else {
let (map_key, remaining_values) = match compacted_item.as_object_mut() {
Some(map) => {
match map.remove(container_key.as_ref().unwrap().as_str()) {
Some(value) => match value.into_parts() {
(generic_json::Value::String(s), _) => {
(Some((*s).to_string()), Vec::new())
}
(generic_json::Value::Array(values), _) => {
let mut values = values.into_iter();
match values.next() {
Some(first_value) => (
first_value.as_str().map(|v| v.to_string()),
values.collect(),
),
None => (None, values.collect()),
}
}
(other_value, meta) => {
(None, vec![K::new(other_value, meta)])
}
},
None => (None, Vec::new()),
}
}
_ => (None, Vec::new()),
};
if !remaining_values.is_empty() {
if let Some(map) = compacted_item.as_object_mut() {
for value in remaining_values {
add_value(
map,
container_key.as_ref().unwrap().as_str(),
value,
false,
|| meta(None),
)
}
}
}
if let Some(map) = compacted_item.as_object() {
if map.len() == 1 && map.get("@id").is_some() {
let obj: Object<J, T> = Object::Node(Node::with_id(
expanded_item.id().unwrap().clone(),
));
compacted_item = obj
.compact_indexed(
None,
active_context.clone(),
active_context.clone(),
Some(item_active_property.as_str()),
loader,
options,
meta.clone(),
)
.await?
}
}
map_key
};
let map_key = match map_key {
Some(key) => key,
None => {
let key = compact_iri::<J, _, _>(
active_context.clone(),
&Term::Keyword(Keyword::None),
true,
false,
options,
)?;
key.unwrap()
}
};
add_value(map_object, &map_key, compacted_item, as_array, || {
meta(None)
})
} else {
add_value(
nest_result,
item_active_property.as_str(),
compacted_item,
as_array,
|| meta(None),
)
}
}
};
}
}
if is_empty {
let item_active_property = compact_iri_with::<J, _, _, _>(
active_context.clone(),
&expanded_property,
&Indexed::new(Object::Node(Node::new()), None),
true,
inside_reverse,
options,
)?;
if let Some(item_active_property) = item_active_property {
let (nest_result, _, _) = select_nest_result::<K, _, _, _>(
result,
active_context.clone(),
item_active_property.as_str(),
options.compact_arrays,
|| meta(None),
)?;
add_value(
nest_result,
item_active_property.as_str(),
K::empty_array(meta(None)),
true,
|| meta(None),
)
}
}
Ok(())
}