use std::{borrow::Cow, rc::Rc};
use anyhow::{Result, bail};
use delegate::delegate;
use inkwell::{
builder::Builder,
types::{BasicType, BasicTypeEnum},
values::{BasicValue, BasicValueEnum, PointerValue},
};
use itertools::{Itertools as _, zip_eq};
#[derive(Eq, PartialEq, Clone)]
pub struct ValueMailBox<'c> {
typ: BasicTypeEnum<'c>,
ptr: PointerValue<'c>,
name: Cow<'static, str>,
}
fn join_names<'a>(names: impl IntoIterator<Item = &'a str>) -> String {
names
.into_iter()
.filter(|x| !x.is_empty())
.join("_")
.to_string()
}
impl<'c> ValueMailBox<'c> {
pub(super) fn new(
typ: impl BasicType<'c>,
ptr: PointerValue<'c>,
name: Option<String>,
) -> Self {
Self {
typ: typ.as_basic_type_enum(),
ptr,
name: name.map_or(Cow::Borrowed(""), Cow::Owned),
}
}
pub fn get_type(&self) -> BasicTypeEnum<'c> {
self.typ
}
pub fn name(&self) -> &str {
self.name.as_ref()
}
pub fn promise(&self) -> ValuePromise<'c> {
ValuePromise(self.clone())
}
pub fn read<'a>(
&'a self,
builder: &Builder<'c>,
labels: impl IntoIterator<Item = &'a str>,
) -> Result<BasicValueEnum<'c>> {
let r = builder.build_load(
self.typ,
self.ptr,
&join_names(
labels
.into_iter()
.chain(std::iter::once(self.name.as_ref())),
),
)?;
debug_assert_eq!(r.get_type(), self.get_type());
Ok(r)
}
fn write(&self, builder: &Builder<'c>, v: impl BasicValue<'c>) -> Result<()> {
builder.build_store(self.ptr, v)?;
Ok(())
}
}
#[must_use]
pub struct ValuePromise<'c>(ValueMailBox<'c>);
impl<'c> ValuePromise<'c> {
pub fn finish(self, builder: &Builder<'c>, v: impl BasicValue<'c>) -> Result<()> {
self.0.write(builder, v)
}
delegate! {
to self.0 {
pub fn get_type(&self) -> BasicTypeEnum<'c>;
}
}
}
#[derive(Eq, PartialEq, Clone)]
#[allow(clippy::len_without_is_empty)]
pub struct RowMailBox<'c>(Rc<Vec<ValueMailBox<'c>>>, Cow<'static, str>);
impl<'c> RowMailBox<'c> {
#[must_use]
pub fn new_empty() -> Self {
Self::new(std::iter::empty(), None)
}
pub(super) fn new(
mbs: impl IntoIterator<Item = ValueMailBox<'c>>,
name: Option<String>,
) -> Self {
Self(
Rc::new(mbs.into_iter().collect_vec()),
name.map_or(Cow::Borrowed(""), Cow::Owned),
)
}
pub fn promise(&self) -> RowPromise<'c> {
RowPromise(self.clone())
}
pub fn get_types(&'_ self) -> impl Iterator<Item = BasicTypeEnum<'c>> + '_ {
self.0.iter().map(ValueMailBox::get_type)
}
#[must_use]
pub fn len(&self) -> usize {
self.0.len()
}
pub fn read_vec<'a>(
&'a self,
builder: &Builder<'c>,
labels: impl IntoIterator<Item = &'a str>,
) -> Result<Vec<BasicValueEnum<'c>>> {
self.read(builder, labels)
}
pub fn read<'a, R: FromIterator<BasicValueEnum<'c>>>(
&'a self,
builder: &Builder<'c>,
labels: impl IntoIterator<Item = &'a str>,
) -> Result<R> {
let labels = labels.into_iter().collect_vec();
self.mailboxes()
.map(|mb| mb.read(builder, labels.clone()))
.collect::<Result<_>>()
}
pub(crate) fn write(
&self,
builder: &Builder<'c>,
vs: impl IntoIterator<Item = BasicValueEnum<'c>>,
) -> Result<()> {
let vs = vs.into_iter().collect_vec();
#[cfg(debug_assertions)]
{
let actual_types = vs.clone().into_iter().map(|x| x.get_type()).collect_vec();
let expected_types = self.get_types().collect_vec();
if actual_types != expected_types {
bail!(
"RowMailbox::write: Expected types {:?}, got {:?}",
expected_types,
actual_types
);
}
}
zip_eq(self.0.iter(), vs).try_for_each(|(mb, v)| mb.write(builder, v))
}
fn mailboxes(&'_ self) -> impl Iterator<Item = ValueMailBox<'c>> + '_ {
self.0.iter().cloned()
}
}
impl<'c> FromIterator<ValueMailBox<'c>> for RowMailBox<'c> {
fn from_iter<T: IntoIterator<Item = ValueMailBox<'c>>>(iter: T) -> Self {
Self::new(iter, None)
}
}
#[must_use]
#[allow(clippy::len_without_is_empty)]
pub struct RowPromise<'c>(RowMailBox<'c>);
impl<'c> RowPromise<'c> {
pub fn finish(
self,
builder: &Builder<'c>,
vs: impl IntoIterator<Item = BasicValueEnum<'c>>,
) -> Result<()> {
self.0.write(builder, vs)
}
delegate! {
to self.0 {
pub fn get_types(&'_ self) -> impl Iterator<Item=BasicTypeEnum<'c>> + '_;
#[must_use] pub fn len(&self) -> usize;
}
}
}