use crate::planner::MethodFailure;
use itertools::Itertools;
use std::sync::Arc;
use std::{convert::TryInto, fmt::Debug};
pub type MethodResult<T> = Result<Vec<T>, MethodFailure>;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum MethodInput {
Ref(String),
MutRef(String),
}
impl MethodInput {
pub fn make_ref<S: Into<String>>(name: S) -> Self {
MethodInput::Ref(name.into())
}
pub fn make_mut_ref<S: Into<String>>(name: S) -> Self {
MethodInput::MutRef(name.into())
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct MethodOutput {
name: String,
}
impl MethodOutput {
pub fn new<S: Into<String>>(name: S) -> Self {
Self { name: name.into() }
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum MethodArg<'a, T> {
Ref(&'a T),
MutRef(&'a mut T),
}
impl<'a, T> From<&'a T> for MethodArg<'a, T> {
fn from(r: &'a T) -> Self {
MethodArg::Ref(r)
}
}
impl<'a, T> From<&'a mut T> for MethodArg<'a, T> {
fn from(mr: &'a mut T) -> Self {
MethodArg::MutRef(mr)
}
}
struct Ref<T>(pub T);
impl<'a, T> TryInto<Ref<&'a T>> for MethodArg<'a, T> {
type Error = MutabilityMismatch;
fn try_into(self) -> Result<Ref<&'a T>, Self::Error> {
match self {
MethodArg::Ref(r) => Ok(Ref(r)),
MethodArg::MutRef(_) => Err(MutabilityMismatch::ExpectedImmutableGotMutable),
}
}
}
impl<'a, T> TryInto<Ref<&'a mut T>> for MethodArg<'a, T> {
type Error = MutabilityMismatch;
fn try_into(self) -> Result<Ref<&'a mut T>, Self::Error> {
match self {
MethodArg::Ref(_) => Err(MutabilityMismatch::ExpectedMutableGotImmutable),
MethodArg::MutRef(r) => Ok(Ref(r)),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ConversionError {
MutabilityMismatch(MutabilityMismatch),
TypeConversionFailure,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum MutabilityMismatch {
ExpectedImmutableGotMutable,
ExpectedMutableGotImmutable,
}
impl From<MutabilityMismatch> for MethodFailure {
fn from(mm: MutabilityMismatch) -> Self {
MethodFailure::MutabilityMismatch(mm)
}
}
impl From<ConversionError> for MethodFailure {
fn from(ce: ConversionError) -> Self {
match ce {
ConversionError::MutabilityMismatch(mm) => MethodFailure::MutabilityMismatch(mm),
ConversionError::TypeConversionFailure => {
MethodFailure::TypeConversionFailure("<VARIABLE>", "<TYPE>")
}
}
}
}
impl<'a, T> MethodArg<'a, T> {
pub fn try_into_ref<U>(self) -> Result<&'a U, ConversionError>
where
&'a T: TryInto<&'a U>,
{
match self {
MethodArg::Ref(r) => match r.try_into() {
Ok(r) => Ok(r),
Err(_) => Err(ConversionError::TypeConversionFailure),
},
_ => Err(ConversionError::MutabilityMismatch(
MutabilityMismatch::ExpectedImmutableGotMutable,
)),
}
}
pub fn try_into_mut<U>(self) -> Result<&'a mut U, ConversionError>
where
&'a mut T: TryInto<&'a mut U>,
{
match self {
MethodArg::MutRef(r) => match r.try_into() {
Ok(r) => Ok(r),
Err(_) => Err(ConversionError::TypeConversionFailure),
},
_ => Err(ConversionError::MutabilityMismatch(
MutabilityMismatch::ExpectedMutableGotImmutable,
)),
}
}
}
type MethodFunctionInner<T> = Arc<dyn for<'a> Fn(Vec<MethodArg<'a, T>>) -> MethodResult<T>>;
#[derive(Clone)]
pub struct MethodBuilder<T> {
name: String,
inputs: Vec<MethodInput>,
outputs: Vec<MethodOutput>,
apply: Option<MethodFunctionInner<T>>,
pure: bool,
}
impl<T> MethodBuilder<T> {
pub fn new<S: Into<String>>(name: S) -> Self {
Self {
name: name.into(),
inputs: Vec::new(),
outputs: Vec::new(),
apply: None,
pure: true,
}
}
pub fn input<S: Into<String>>(mut self, input: S) -> Self {
self.inputs.push(MethodInput::Ref(input.into()));
self
}
pub fn input_mut<S: Into<String>>(mut self, input: S) -> Self {
self.inputs.push(MethodInput::MutRef(input.into()));
self
}
pub fn inputs(mut self, inputs: Vec<MethodInput>) -> Self {
self.inputs = inputs;
self
}
pub fn outputs(mut self, outputs: Vec<MethodOutput>) -> Self {
self.outputs = outputs.into_iter().map_into().collect();
self
}
pub fn apply(
mut self,
apply: impl for<'a> Fn(Vec<MethodArg<'a, T>>) -> MethodResult<T> + 'static,
) -> Self {
self.apply = Some(Arc::new(apply));
self
}
pub fn pure(mut self, pure: bool) -> Self {
self.pure = pure;
self
}
}
impl<T> Debug for MethodBuilder<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Method {}({:?}) -> [{:?}]",
self.name, self.inputs, self.outputs
)
}
}
impl<T> PartialEq for MethodBuilder<T> {
fn eq(&self, other: &Self) -> bool {
(&self.name, &self.inputs, &self.outputs) == (&other.name, &other.inputs, &other.outputs)
}
}
impl<T> Eq for MethodBuilder<T> {}
#[cfg(test)]
mod tests {
use super::{MethodArg, MethodBuilder, MethodFunctionInner, MethodOutput, MutabilityMismatch};
use crate::{planner::MethodFailure, ret};
use std::sync::Arc;
#[derive(Copy, Clone, Debug, PartialEq)]
struct A;
#[derive(Copy, Clone, Debug, PartialEq)]
struct B;
sum_type! {
#[derive(Debug, PartialEq)]
enum AB {
A,
B
}
}
#[test]
fn builder_builds() {
let mb = MethodBuilder::new("m")
.input("a")
.input_mut("b")
.outputs(vec![MethodOutput::new("c")])
.apply(|mut v: Vec<MethodArg<'_, _>>| {
let a: &_ = v.remove(0).try_into_ref()?;
assert_eq!(a, &3);
let b: &mut _ = v.remove(0).try_into_mut()?;
assert_eq!(b, &4);
Ok(vec![*a + *b])
});
if let Some(f) = &mb.apply {
let result = f(vec![MethodArg::from(&3), MethodArg::from(&mut 4)]);
assert_eq!(result, Ok(vec![7]))
}
}
#[test]
fn wrong_mutability_gives_error() {
let f: MethodFunctionInner<i32> = Arc::new(|mut v| {
let a: &mut i32 = v.remove(0).try_into_mut()?;
Ok(vec![*a])
});
let result = f(vec![MethodArg::from(&0)]);
assert_eq!(
result,
Err(MethodFailure::MutabilityMismatch(
MutabilityMismatch::ExpectedMutableGotImmutable
))
);
let f: MethodFunctionInner<i32> = Arc::new(|mut v| {
let a: &i32 = v.remove(0).try_into_ref()?;
Ok(vec![*a])
});
let result = f(vec![MethodArg::from(&mut 0)]);
assert_eq!(
result,
Err(MethodFailure::MutabilityMismatch(
MutabilityMismatch::ExpectedImmutableGotMutable
))
);
}
#[test]
fn auto_conversion_in_apply() {
let f: MethodFunctionInner<AB> = Arc::new(|mut v| {
let a: &A = v.remove(0).try_into_ref::<A>()?;
assert_eq!(a, &A);
let b: &mut B = v.remove(0).try_into_mut::<B>()?;
assert_eq!(b, &B);
Ok(vec![A.into(), B.into()])
});
let result = f(vec![
MethodArg::from(&AB::A(A)),
MethodArg::from(&mut AB::B(B)),
]);
assert_eq!(result, Ok(vec![AB::A(A), AB::B(B)]));
}
#[test]
fn auto_conversion_may_fail() {
let f: MethodFunctionInner<AB> = Arc::new(|mut v| {
let b: &B = v.remove(0).try_into_ref()?;
Ok(vec![(*b).into()])
});
let result = f(vec![MethodArg::from(&AB::A(A))]);
assert_eq!(
result,
Err(MethodFailure::TypeConversionFailure("<VARIABLE>", "<TYPE>"))
);
}
#[test]
fn make_method() {
let _: MethodBuilder<AB> = method! {
@impure
fn m(a: &A, b: &mut B) -> [c, d] {
let a: &A = a;
let b: &B = b;
ret![*a, *b]
}
};
}
}