use crate::Env;
use crate::source::Location;
use std::borrow::Borrow;
use std::collections::HashSet;
use std::fmt::Debug;
use std::fmt::Display;
use std::hash::Hash;
use std::hash::Hasher;
use std::iter::FusedIterator;
use std::pin::Pin;
use std::rc::Rc;
use thiserror::Error;
pub trait FunctionBody<S>: Debug + Display {
#[allow(async_fn_in_trait)] async fn execute(&self, env: &mut Env<S>) -> crate::semantics::Result;
}
pub trait FunctionBodyObject<S>: Debug + Display {
fn execute<'a>(
&'a self,
env: &'a mut Env<S>,
) -> Pin<Box<dyn Future<Output = crate::semantics::Result> + 'a>>;
}
impl<S, T: FunctionBody<S> + ?Sized> FunctionBodyObject<S> for T {
fn execute<'a>(
&'a self,
env: &'a mut Env<S>,
) -> Pin<Box<dyn Future<Output = crate::semantics::Result> + 'a>> {
Box::pin(self.execute(env))
}
}
pub struct Function<S> {
pub name: String,
pub body: Rc<dyn FunctionBodyObject<S>>,
pub origin: Location,
pub read_only_location: Option<Location>,
}
impl<S> Function<S> {
#[inline]
#[must_use]
pub fn new<N: Into<String>, B: Into<Rc<dyn FunctionBodyObject<S>>>>(
name: N,
body: B,
origin: Location,
) -> Self {
Function {
name: name.into(),
body: body.into(),
origin,
read_only_location: None,
}
}
#[inline]
#[must_use]
pub fn make_read_only(mut self, location: Location) -> Self {
self.read_only_location = Some(location);
self
}
#[must_use]
pub const fn is_read_only(&self) -> bool {
self.read_only_location.is_some()
}
}
impl<S> Clone for Function<S> {
fn clone(&self) -> Self {
Self {
name: self.name.clone(),
body: self.body.clone(),
origin: self.origin.clone(),
read_only_location: self.read_only_location.clone(),
}
}
}
impl<S> PartialEq for Function<S> {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
&& Rc::ptr_eq(&self.body, &other.body)
&& self.origin == other.origin
&& self.read_only_location == other.read_only_location
}
}
impl<S> Eq for Function<S> {}
impl<S> Debug for Function<S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Function")
.field("name", &self.name)
.field("body", &self.body)
.field("origin", &self.origin)
.field("read_only_location", &self.read_only_location)
.finish()
}
}
struct HashEntry<S>(Rc<Function<S>>);
impl<S> Clone for HashEntry<S> {
fn clone(&self) -> Self {
HashEntry(Rc::clone(&self.0))
}
}
impl<S> PartialEq for HashEntry<S> {
fn eq(&self, other: &HashEntry<S>) -> bool {
self.0.name == other.0.name
}
}
impl<S> Eq for HashEntry<S> {}
impl<S> Hash for HashEntry<S> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.name.hash(state)
}
}
impl<S> Borrow<str> for HashEntry<S> {
fn borrow(&self) -> &str {
&self.0.name
}
}
impl<S> Debug for HashEntry<S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("HashEntry").field(&self.0).finish()
}
}
pub struct FunctionSet<S> {
entries: HashSet<HashEntry<S>>,
}
impl<S> Debug for FunctionSet<S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FunctionSet")
.field("entries", &self.entries)
.finish()
}
}
impl<S> Clone for FunctionSet<S> {
fn clone(&self) -> Self {
#[allow(clippy::mutable_key_type)]
let entries = self.entries.clone();
Self { entries }
}
}
impl<S> Default for FunctionSet<S> {
fn default() -> Self {
#[allow(clippy::mutable_key_type)]
let entries = HashSet::default();
Self { entries }
}
}
#[derive(Error)]
#[error("cannot redefine read-only function `{}`", .existing.name)]
#[non_exhaustive]
pub struct DefineError<S> {
pub existing: Rc<Function<S>>,
pub new: Rc<Function<S>>,
}
impl<S> Clone for DefineError<S> {
fn clone(&self) -> Self {
Self {
existing: self.existing.clone(),
new: self.new.clone(),
}
}
}
impl<S> Debug for DefineError<S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DefineError")
.field("existing", &self.existing)
.field("new", &self.new)
.finish()
}
}
impl<S> PartialEq for DefineError<S> {
fn eq(&self, other: &Self) -> bool {
self.existing == other.existing && self.new == other.new
}
}
impl<S> Eq for DefineError<S> {}
#[derive(Error)]
#[error("cannot unset read-only function `{}`", .existing.name)]
#[non_exhaustive]
pub struct UnsetError<S> {
pub existing: Rc<Function<S>>,
}
impl<S> Clone for UnsetError<S> {
fn clone(&self) -> Self {
Self {
existing: self.existing.clone(),
}
}
}
impl<S> Debug for UnsetError<S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("UnsetError")
.field("existing", &self.existing)
.finish()
}
}
impl<S> PartialEq for UnsetError<S> {
fn eq(&self, other: &Self) -> bool {
self.existing == other.existing
}
}
impl<S> Eq for UnsetError<S> {}
pub struct Iter<'a, S> {
inner: std::collections::hash_set::Iter<'a, HashEntry<S>>,
}
impl<S> Clone for Iter<'_, S> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl<S> Debug for Iter<'_, S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Iter").field("inner", &self.inner).finish()
}
}
impl<S> FunctionSet<S> {
#[must_use]
pub fn new() -> Self {
FunctionSet::default()
}
#[must_use]
pub fn get(&self, name: &str) -> Option<&Rc<Function<S>>> {
self.entries.get(name).map(|entry| &entry.0)
}
#[inline]
#[must_use]
pub fn len(&self) -> usize {
self.entries.len()
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn define<F: Into<Rc<Function<S>>>>(
&mut self,
function: F,
) -> Result<Option<Rc<Function<S>>>, DefineError<S>> {
#[allow(clippy::mutable_key_type)]
fn inner<S>(
entries: &mut HashSet<HashEntry<S>>,
new: Rc<Function<S>>,
) -> Result<Option<Rc<Function<S>>>, DefineError<S>> {
match entries.get(new.name.as_str()) {
Some(existing) if existing.0.is_read_only() => Err(DefineError {
existing: Rc::clone(&existing.0),
new,
}),
_ => Ok(entries.replace(HashEntry(new)).map(|entry| entry.0)),
}
}
inner(&mut self.entries, function.into())
}
pub fn unset(&mut self, name: &str) -> Result<Option<Rc<Function<S>>>, UnsetError<S>> {
match self.entries.get(name) {
Some(entry) if entry.0.is_read_only() => Err(UnsetError {
existing: Rc::clone(&entry.0),
}),
_ => Ok(self.entries.take(name).map(|entry| entry.0)),
}
}
pub fn iter(&self) -> Iter<'_, S> {
let inner = self.entries.iter();
Iter { inner }
}
}
impl<'a, S> Iterator for Iter<'a, S> {
type Item = &'a Rc<Function<S>>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|entry| &entry.0)
}
}
impl<S> ExactSizeIterator for Iter<'_, S> {
#[inline]
fn len(&self) -> usize {
self.inner.len()
}
}
impl<S> FusedIterator for Iter<'_, S> {}
impl<'a, S> IntoIterator for &'a FunctionSet<S> {
type Item = &'a Rc<Function<S>>;
type IntoIter = Iter<'a, S>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Clone, Debug)]
struct FunctionBodyStub;
impl std::fmt::Display for FunctionBodyStub {
fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
unreachable!()
}
}
impl<S> FunctionBody<S> for FunctionBodyStub {
async fn execute(&self, _: &mut Env<S>) -> crate::semantics::Result {
unreachable!()
}
}
fn function_body_stub<S>() -> Rc<dyn FunctionBodyObject<S>> {
Rc::new(FunctionBodyStub)
}
#[test]
fn defining_new_function() {
let mut set = FunctionSet::<()>::new();
let function = Rc::new(Function::new(
"foo",
function_body_stub(),
Location::dummy("foo"),
));
let result = set.define(function.clone());
assert_eq!(result, Ok(None));
assert_eq!(set.get("foo"), Some(&function));
}
#[test]
fn redefining_existing_function() {
let mut set = FunctionSet::<()>::new();
let function1 = Rc::new(Function::new(
"foo",
function_body_stub(),
Location::dummy("foo 1"),
));
let function2 = Rc::new(Function::new(
"foo",
function_body_stub(),
Location::dummy("foo 2"),
));
set.define(function1.clone()).unwrap();
let result = set.define(function2.clone());
assert_eq!(result, Ok(Some(function1)));
assert_eq!(set.get("foo"), Some(&function2));
}
#[test]
fn redefining_readonly_function() {
let mut set = FunctionSet::<()>::new();
let function1 = Rc::new(
Function::new("foo", function_body_stub(), Location::dummy("foo 1"))
.make_read_only(Location::dummy("readonly")),
);
let function2 = Rc::new(Function::new(
"foo",
function_body_stub(),
Location::dummy("foo 2"),
));
set.define(function1.clone()).unwrap();
let error = set.define(function2.clone()).unwrap_err();
assert_eq!(error.existing, function1);
assert_eq!(error.new, function2);
assert_eq!(set.get("foo"), Some(&function1));
}
#[test]
fn unsetting_existing_function() {
let mut set = FunctionSet::<()>::new();
let function = Rc::new(Function::new(
"foo",
function_body_stub(),
Location::dummy("foo"),
));
set.define(function.clone()).unwrap();
let result = set.unset("foo").unwrap();
assert_eq!(result, Some(function));
assert_eq!(set.get("foo"), None);
}
#[test]
fn unsetting_nonexisting_function() {
let mut set = FunctionSet::<()>::new();
let result = set.unset("foo").unwrap();
assert_eq!(result, None);
assert_eq!(set.get("foo"), None);
}
#[test]
fn unsetting_readonly_function() {
let mut set = FunctionSet::<()>::new();
let function = Rc::new(
Function::new("foo", function_body_stub(), Location::dummy("foo"))
.make_read_only(Location::dummy("readonly")),
);
set.define(function.clone()).unwrap();
let error = set.unset("foo").unwrap_err();
assert_eq!(error.existing, function);
}
#[test]
fn iteration() {
let mut set = FunctionSet::<()>::new();
let function1 = Rc::new(Function::new(
"foo",
function_body_stub(),
Location::dummy("foo"),
));
let function2 = Rc::new(Function::new(
"bar",
function_body_stub(),
Location::dummy("bar"),
));
set.define(function1.clone()).unwrap();
set.define(function2.clone()).unwrap();
let functions = set.iter().collect::<Vec<_>>();
assert!(
functions[..] == [&function1, &function2] || functions[..] == [&function2, &function1],
"{functions:?}"
);
}
}