use std::cmp::min;
use data::{Flow, LinkReference, LinksConstants};
use crate::{data::WriteHandler, Doublet, Doublets, DoubletsExt, Error, Fuse, Link};
#[inline]
pub(crate) fn part<T: LinkReference>(query: &[T], part: T) -> T {
query
.get(part.as_())
.copied()
.unwrap_or_else(|| T::from_byte(0))
}
#[inline]
pub(crate) fn ensure_does_not_exist<T, L>(links: &L, source: T, target: T) -> Result<(), Error<T>>
where
T: LinkReference,
L: Doublets<T>,
{
if links.search(source, target).is_some() {
Err(Error::AlreadyExists(Doublet::new(source, target)))
} else {
Ok(())
}
}
#[inline]
pub(crate) fn ensure_no_usages<T, L>(links: &L, link: T) -> Result<(), Error<T>>
where
T: LinkReference,
L: Doublets<T>,
{
if links.has_usages(link) {
Err(Error::HasUsages(usages_of(links, link)))
} else {
Ok(())
}
}
#[inline]
pub(crate) fn usages_of<T, L>(links: &L, link: T) -> Vec<Link<T>>
where
T: LinkReference,
L: Doublets<T>,
{
let any = links.constants().any;
let mut usages: Vec<_> = links.each_iter([any, link, any]).collect();
usages.extend(
links
.each_iter([any, any, link])
.filter(|usage| usage.source != link),
);
usages.retain(|usage| usage.index != link);
usages
}
#[inline]
pub(crate) fn ensure_inner_reference_exists<T, L>(links: &L, query: &[T]) -> Result<(), Error<T>>
where
T: LinkReference,
L: Doublets<T>,
{
for &reference in query {
if links.constants().is_internal(reference) && !links.exist(reference) {
return Err(Error::NotExists(reference));
}
}
Ok(())
}
#[inline]
pub(crate) fn ensure_link_exists<T, L>(links: &L, link: T) -> Result<(), Error<T>>
where
T: LinkReference,
L: Doublets<T>,
{
if links.exist(link) {
Ok(())
} else {
Err(Error::NotExists(link))
}
}
#[inline]
pub(crate) fn resolve_constant_as_self_reference<T: LinkReference>(
constant: T,
restriction: &[T],
substitution: &[T],
constants: &LinksConstants<T>,
) -> [T; 3] {
let null = T::from_byte(0);
let mut index = part(substitution, constants.index_part);
if index == null {
index = part(restriction, constants.index_part);
}
let mut source = part(substitution, constants.source_part);
let mut target = part(substitution, constants.target_part);
if source == constant {
source = index;
}
if target == constant {
target = index;
}
[index, source, target]
}
#[inline]
pub(crate) fn create_point_with<T, L, F>(links: &mut L, handler: F) -> Result<Flow, Error<T>>
where
T: LinkReference,
L: Doublets<T>,
F: FnMut(Link<T>, Link<T>) -> Flow,
{
let mut point = T::from_byte(0);
let mut handler = Fuse::new(handler);
links.create_links(&[], &mut |before, after| {
point = after.index;
handler.call(before, after)
})?;
links.update_links(&[point], &[point, point, point], &mut |before, after| {
handler.call(before, after)
})
}
#[inline]
pub(crate) fn are_values_reset<T, L>(links: &L, index: T) -> Result<bool, Error<T>>
where
T: LinkReference,
L: Doublets<T>,
{
let null = T::from_byte(0);
let link = links.try_get_link(index)?;
Ok(link.source == null && link.target == null)
}
#[inline]
pub(crate) fn enforce_reset_values<T, L>(
links: &mut L,
index: T,
handler: WriteHandler<'_, T>,
) -> Result<Flow, Error<T>>
where
T: LinkReference,
L: Doublets<T>,
{
if are_values_reset(links, index)? {
return Ok(Flow::Continue);
}
let null = T::from_byte(0);
links.update_links(&[index], &[index, null, null], handler)
}
#[inline]
pub(crate) fn merge_usages<T, L, F>(
links: &mut L,
old: T,
new: T,
handler: F,
) -> Result<Flow, Error<T>>
where
T: LinkReference,
L: Doublets<T>,
F: FnMut(Link<T>, Link<T>) -> Flow,
{
if old == new {
return Ok(Flow::Continue);
}
let mut handler = Fuse::new(handler);
for usage in usages_of(links, old) {
let source = if usage.source == old {
new
} else {
usage.source
};
let target = if usage.target == old {
new
} else {
usage.target
};
links.update_links(
&[usage.index],
&[usage.index, source, target],
&mut |before, after| handler.call(before, after),
)?;
}
Ok(Flow::Continue)
}
#[inline]
pub(crate) fn ensure_created<T, L>(links: &mut L, addresses: &[T]) -> Result<(), Error<T>>
where
T: LinkReference,
L: Doublets<T>,
{
let null = T::from_byte(0);
let non_existent: Vec<_> = addresses
.iter()
.copied()
.filter(|&address| address != null && !links.exist(address))
.collect();
let Some(&requested) = non_existent.iter().max() else {
return Ok(());
};
let max = min(requested, *links.constants().internal_range.end());
let mut created = Vec::new();
loop {
let link = links.create_by([])?;
created.push(link);
if link >= max {
break;
}
}
for link in created {
if !non_existent.contains(&link) {
links.delete_by([link])?;
}
}
Ok(())
}
#[inline]
pub(crate) fn format_link<T: LinkReference>(link: &Link<T>) -> String {
if link.index == T::from_byte(0) {
format!("({}->{})", link.source, link.target)
} else {
format!("({}: {}->{})", link.index, link.source, link.target)
}
}