Add ArgCount
This commit is contained in:
parent
f93aadd0dd
commit
c3b91562d1
311
crates/bevy_reflect/src/func/args/count.rs
Normal file
311
crates/bevy_reflect/src/func/args/count.rs
Normal file
@ -0,0 +1,311 @@
|
||||
use crate::func::args::ArgCountOutOfBoundsError;
|
||||
use core::fmt::{Debug, Formatter};
|
||||
|
||||
/// A container for zero or more argument counts for a function.
|
||||
///
|
||||
/// For most functions, this will contain a single count,
|
||||
/// however, overloaded functions may contain more.
|
||||
///
|
||||
/// # Maximum Argument Count
|
||||
///
|
||||
/// The maximum number of arguments that can be represented by this struct is 63,
|
||||
/// as given by [`ArgCount::MAX_COUNT`].
|
||||
/// The reason for this is that all counts are stored internally as a single `u64`
|
||||
/// with each bit representing a specific count based on its bit index.
|
||||
///
|
||||
/// This allows for a smaller memory footprint and faster lookups compared to a
|
||||
/// `HashSet` or `Vec` of possible counts.
|
||||
/// It's also more appropriate for representing the argument counts of a function
|
||||
/// given that most functions will not have more than a few arguments.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ArgCount {
|
||||
/// The bits representing the argument counts.
|
||||
///
|
||||
/// Each bit represents a specific count based on its bit index.
|
||||
bits: u64,
|
||||
/// The total number of argument counts.
|
||||
len: u8,
|
||||
}
|
||||
|
||||
impl ArgCount {
|
||||
/// The maximum number of arguments that can be represented by this struct.
|
||||
pub const MAX_COUNT: usize = u64::BITS as usize - 1;
|
||||
|
||||
/// Create a new [`ArgCount`] with the given count.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the count is greater than [`Self::MAX_COUNT`].
|
||||
pub fn new(count: usize) -> Result<Self, ArgCountOutOfBoundsError> {
|
||||
Ok(Self {
|
||||
bits: 1 << Self::try_to_u8(count)?,
|
||||
len: 1,
|
||||
})
|
||||
}
|
||||
|
||||
/// Adds the given count to this [`ArgCount`].
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the count is greater than [`Self::MAX_COUNT`].
|
||||
pub fn add(&mut self, count: usize) {
|
||||
self.try_add(count).unwrap();
|
||||
}
|
||||
|
||||
/// Attempts to add the given count to this [`ArgCount`].
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the count is greater than [`Self::MAX_COUNT`].
|
||||
pub fn try_add(&mut self, count: usize) -> Result<(), ArgCountOutOfBoundsError> {
|
||||
let count = Self::try_to_u8(count)?;
|
||||
|
||||
if !self.contains_unchecked(count) {
|
||||
self.len += 1;
|
||||
self.bits |= 1 << count;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Removes the given count from this [`ArgCount`].
|
||||
pub fn remove(&mut self, count: usize) {
|
||||
self.try_remove(count).unwrap();
|
||||
}
|
||||
|
||||
/// Attempts to remove the given count from this [`ArgCount`].
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the count is greater than [`Self::MAX_COUNT`].
|
||||
pub fn try_remove(&mut self, count: usize) -> Result<(), ArgCountOutOfBoundsError> {
|
||||
let count = Self::try_to_u8(count)?;
|
||||
|
||||
if self.contains_unchecked(count) {
|
||||
self.len -= 1;
|
||||
self.bits &= !(1 << count);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Checks if this [`ArgCount`] contains the given count.
|
||||
pub fn contains(&self, count: usize) -> bool {
|
||||
count < usize::BITS as usize && (self.bits >> count) & 1 == 1
|
||||
}
|
||||
|
||||
/// Returns the total number of argument counts that this [`ArgCount`] contains.
|
||||
pub fn len(&self) -> usize {
|
||||
self.len as usize
|
||||
}
|
||||
|
||||
/// Returns true if this [`ArgCount`] contains no argument counts.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len == 0
|
||||
}
|
||||
|
||||
/// Returns an iterator over the argument counts in this [`ArgCount`].
|
||||
pub fn iter(&self) -> ArgCountIter {
|
||||
ArgCountIter {
|
||||
count: *self,
|
||||
index: 0,
|
||||
found: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if this [`ArgCount`] contains the given count without any bounds checking.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the count is greater than [`Self::MAX_COUNT`].
|
||||
fn contains_unchecked(&self, count: u8) -> bool {
|
||||
(self.bits >> count) & 1 == 1
|
||||
}
|
||||
|
||||
/// Attempts to convert the given count to a `u8` within the bounds of the [maximum count].
|
||||
///
|
||||
/// [maximum count]: Self::MAX_COUNT
|
||||
fn try_to_u8(count: usize) -> Result<u8, ArgCountOutOfBoundsError> {
|
||||
if count > Self::MAX_COUNT {
|
||||
Err(ArgCountOutOfBoundsError(count))
|
||||
} else {
|
||||
Ok(count as u8)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Defaults this [`ArgCount`] to empty.
|
||||
///
|
||||
/// This means that it contains no argument counts, including zero.
|
||||
impl Default for ArgCount {
|
||||
fn default() -> Self {
|
||||
Self { bits: 0, len: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for ArgCount {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_set().entries(self.iter()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator for the argument counts in an [`ArgCount`].
|
||||
pub struct ArgCountIter {
|
||||
count: ArgCount,
|
||||
index: u8,
|
||||
found: u8,
|
||||
}
|
||||
|
||||
impl Iterator for ArgCountIter {
|
||||
type Item = usize;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
if self.index as usize > ArgCount::MAX_COUNT {
|
||||
return None;
|
||||
}
|
||||
|
||||
if self.found == self.count.len {
|
||||
// All counts have been found
|
||||
return None;
|
||||
}
|
||||
|
||||
if self.count.contains_unchecked(self.index) {
|
||||
self.index += 1;
|
||||
self.found += 1;
|
||||
return Some(self.index as usize - 1);
|
||||
}
|
||||
|
||||
self.index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(self.count.len(), Some(self.count.len()))
|
||||
}
|
||||
}
|
||||
|
||||
impl ExactSizeIterator for ArgCountIter {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn should_default_to_empty() {
|
||||
let count = ArgCount::default();
|
||||
|
||||
assert_eq!(count.len(), 0);
|
||||
assert!(count.is_empty());
|
||||
|
||||
assert!(!count.contains(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_construct_with_count() {
|
||||
let count = ArgCount::new(3).unwrap();
|
||||
|
||||
assert_eq!(count.len(), 1);
|
||||
assert!(!count.is_empty());
|
||||
|
||||
assert!(count.contains(3));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_add_count() {
|
||||
let mut count = ArgCount::default();
|
||||
count.add(3);
|
||||
|
||||
assert_eq!(count.len(), 1);
|
||||
|
||||
assert!(count.contains(3));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_add_multiple_counts() {
|
||||
let mut count = ArgCount::default();
|
||||
count.add(3);
|
||||
count.add(5);
|
||||
count.add(7);
|
||||
|
||||
assert_eq!(count.len(), 3);
|
||||
|
||||
assert!(!count.contains(0));
|
||||
assert!(!count.contains(1));
|
||||
assert!(!count.contains(2));
|
||||
|
||||
assert!(count.contains(3));
|
||||
assert!(count.contains(5));
|
||||
assert!(count.contains(7));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_add_idempotently() {
|
||||
let mut count = ArgCount::default();
|
||||
count.add(3);
|
||||
count.add(3);
|
||||
|
||||
assert_eq!(count.len(), 1);
|
||||
assert!(count.contains(3));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_remove_count() {
|
||||
let mut count = ArgCount::default();
|
||||
count.add(3);
|
||||
|
||||
assert_eq!(count.len(), 1);
|
||||
assert!(count.contains(3));
|
||||
|
||||
count.remove(3);
|
||||
|
||||
assert_eq!(count.len(), 0);
|
||||
assert!(!count.contains(3));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_allow_removeting_nonexistent_count() {
|
||||
let mut count = ArgCount::default();
|
||||
|
||||
assert_eq!(count.len(), 0);
|
||||
assert!(!count.contains(3));
|
||||
|
||||
count.remove(3);
|
||||
|
||||
assert_eq!(count.len(), 0);
|
||||
assert!(!count.contains(3));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_iterate_over_counts() {
|
||||
let mut count = ArgCount::default();
|
||||
count.add(3);
|
||||
count.add(5);
|
||||
count.add(7);
|
||||
|
||||
let mut iter = count.iter();
|
||||
|
||||
assert_eq!(iter.len(), 3);
|
||||
|
||||
assert_eq!(iter.next(), Some(3));
|
||||
assert_eq!(iter.next(), Some(5));
|
||||
assert_eq!(iter.next(), Some(7));
|
||||
assert_eq!(iter.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_return_error_for_out_of_bounds_count() {
|
||||
let count = ArgCount::new(64);
|
||||
assert_eq!(count, Err(ArgCountOutOfBoundsError(64)));
|
||||
|
||||
let mut count = ArgCount::default();
|
||||
assert_eq!(count.try_add(64), Err(ArgCountOutOfBoundsError(64)));
|
||||
assert_eq!(count.try_remove(64), Err(ArgCountOutOfBoundsError(64)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_return_false_for_out_of_bounds_contains() {
|
||||
let count = ArgCount::default();
|
||||
assert!(!count.contains(64));
|
||||
}
|
||||
}
|
@ -32,3 +32,8 @@ pub enum ArgError {
|
||||
#[error("expected an argument but received none")]
|
||||
EmptyArgList,
|
||||
}
|
||||
|
||||
/// The given argument count is out of bounds.
|
||||
#[derive(Debug, Error, PartialEq)]
|
||||
#[error("argument count out of bounds: {0}")]
|
||||
pub struct ArgCountOutOfBoundsError(pub usize);
|
||||
|
@ -4,6 +4,7 @@
|
||||
//! [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut
|
||||
|
||||
pub use arg::*;
|
||||
pub use count::*;
|
||||
pub use error::*;
|
||||
pub use from_arg::*;
|
||||
pub use info::*;
|
||||
@ -11,6 +12,7 @@ pub use list::*;
|
||||
pub use ownership::*;
|
||||
|
||||
mod arg;
|
||||
mod count;
|
||||
mod error;
|
||||
mod from_arg;
|
||||
mod info;
|
||||
|
@ -2,7 +2,9 @@ use crate::{
|
||||
self as bevy_reflect,
|
||||
__macro_exports::RegisterForReflection,
|
||||
func::{
|
||||
args::ArgList, dynamic_function_internal::DynamicFunctionInternal, info::FunctionInfo,
|
||||
args::{ArgCount, ArgList},
|
||||
dynamic_function_internal::DynamicFunctionInternal,
|
||||
info::FunctionInfo,
|
||||
DynamicFunctionMut, Function, FunctionOverloadError, FunctionResult, IntoFunction,
|
||||
IntoFunctionMut,
|
||||
},
|
||||
@ -13,7 +15,6 @@ use crate::{
|
||||
use alloc::{borrow::Cow, boxed::Box, sync::Arc};
|
||||
use bevy_reflect_derive::impl_type_path;
|
||||
use core::fmt::{Debug, Formatter};
|
||||
use core::ops::RangeInclusive;
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
use alloc::{boxed::Box, format, vec};
|
||||
@ -83,7 +84,10 @@ impl<'env> DynamicFunction<'env> {
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if no [`SignatureInfo`] is provided or if the conversion to [`FunctionInfo`] fails.
|
||||
/// This function may panic for any of the following reasons:
|
||||
/// - No [`SignatureInfo`] is provided.
|
||||
/// - A provided [`SignatureInfo`] has more arguments than [`ArgCount::MAX_COUNT`].
|
||||
/// - The conversion to [`FunctionInfo`] fails.
|
||||
///
|
||||
/// [calling]: crate::func::dynamic_function::DynamicFunction::call
|
||||
/// [`SignatureInfo`]: crate::func::SignatureInfo
|
||||
@ -309,27 +313,25 @@ impl<'env> DynamicFunction<'env> {
|
||||
pub fn is_overloaded(&self) -> bool {
|
||||
self.internal.is_overloaded()
|
||||
}
|
||||
|
||||
/// Returns the number of arguments the function expects.
|
||||
///
|
||||
/// For [overloaded] functions that can have a variable number of arguments,
|
||||
/// this will return the minimum and maximum number of arguments.
|
||||
///
|
||||
/// Otherwise, the range will have the same start and end.
|
||||
/// this will contain the full set of counts for all signatures.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_reflect::func::IntoFunction;
|
||||
/// let add = (|a: i32, b: i32| a + b).into_function();
|
||||
/// assert_eq!(add.arg_count(), 2..=2);
|
||||
/// assert!(add.arg_count().contains(2));
|
||||
///
|
||||
/// let add = add.with_overload(|a: f32, b: f32, c: f32| a + b + c);
|
||||
/// assert_eq!(add.arg_count(), 2..=3);
|
||||
/// assert!(add.arg_count().contains(2));
|
||||
/// assert!(add.arg_count().contains(3));
|
||||
/// ```
|
||||
///
|
||||
/// [overloaded]: Self::with_overload
|
||||
pub fn arg_count(&self) -> RangeInclusive<usize> {
|
||||
pub fn arg_count(&self) -> ArgCount {
|
||||
self.internal.arg_count()
|
||||
}
|
||||
}
|
||||
@ -510,7 +512,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
error,
|
||||
FunctionError::ArgCountMismatch {
|
||||
expected: 2..=2,
|
||||
expected: ArgCount::new(2).unwrap(),
|
||||
received: 1
|
||||
}
|
||||
);
|
||||
@ -527,12 +529,16 @@ mod tests {
|
||||
.push_owned(2_i32)
|
||||
.push_owned(3_i32)
|
||||
.push_owned(4_i32);
|
||||
|
||||
let error = func.call(args).unwrap_err();
|
||||
|
||||
let mut expected_count = ArgCount::new(2).unwrap();
|
||||
expected_count.add(3);
|
||||
|
||||
assert_eq!(
|
||||
error,
|
||||
FunctionError::ArgCountMismatch {
|
||||
expected: 2..=3,
|
||||
expected: expected_count,
|
||||
received: 4
|
||||
}
|
||||
);
|
||||
|
@ -1,9 +1,9 @@
|
||||
use crate::func::args::ArgCount;
|
||||
use crate::func::signature::{ArgListSignature, ArgumentSignature};
|
||||
use crate::func::{ArgList, FunctionError, FunctionInfo, FunctionOverloadError};
|
||||
use alloc::borrow::Cow;
|
||||
use bevy_utils::hashbrown::HashMap;
|
||||
use core::fmt::{Debug, Formatter};
|
||||
use core::ops::RangeInclusive;
|
||||
|
||||
/// An internal structure for storing a function and its corresponding [function information].
|
||||
///
|
||||
@ -101,11 +101,9 @@ impl<F> DynamicFunctionInternal<F> {
|
||||
|
||||
/// Returns the number of arguments the function expects.
|
||||
///
|
||||
/// For[overloaded functions that can have a variable number of arguments,
|
||||
/// this will return the minimum and maximum number of arguments.
|
||||
///
|
||||
/// Otherwise, the range will have the same start and end.
|
||||
pub fn arg_count(&self) -> RangeInclusive<usize> {
|
||||
/// For overloaded functions that can have a variable number of arguments,
|
||||
/// this will contain the full set of counts for all signatures.
|
||||
pub fn arg_count(&self) -> ArgCount {
|
||||
self.info.arg_count()
|
||||
}
|
||||
|
||||
@ -117,7 +115,7 @@ impl<F> DynamicFunctionInternal<F> {
|
||||
let expected_arg_count = self.arg_count();
|
||||
let received_arg_count = args.len();
|
||||
|
||||
if !expected_arg_count.contains(&received_arg_count) {
|
||||
if !expected_arg_count.contains(received_arg_count) {
|
||||
Err(FunctionError::ArgCountMismatch {
|
||||
expected: expected_arg_count,
|
||||
received: received_arg_count,
|
||||
|
@ -1,13 +1,13 @@
|
||||
use alloc::{borrow::Cow, boxed::Box, sync::Arc};
|
||||
use core::fmt::{Debug, Formatter};
|
||||
use core::ops::RangeInclusive;
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
use alloc::{boxed::Box, format, vec};
|
||||
|
||||
use crate::func::{
|
||||
args::ArgList, dynamic_function_internal::DynamicFunctionInternal, DynamicFunction,
|
||||
FunctionInfo, FunctionOverloadError, FunctionResult, IntoFunctionMut,
|
||||
args::{ArgCount, ArgList},
|
||||
dynamic_function_internal::DynamicFunctionInternal,
|
||||
DynamicFunction, FunctionInfo, FunctionOverloadError, FunctionResult, IntoFunctionMut,
|
||||
};
|
||||
|
||||
/// A [`Box`] containing a callback to a reflected function.
|
||||
@ -85,7 +85,10 @@ impl<'env> DynamicFunctionMut<'env> {
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if no [`SignatureInfo`] is provided or if the conversion to [`FunctionInfo`] fails.
|
||||
/// This function may panic for any of the following reasons:
|
||||
/// - No [`SignatureInfo`] is provided.
|
||||
/// - A provided [`SignatureInfo`] has more arguments than [`ArgCount::MAX_COUNT`].
|
||||
/// - The conversion to [`FunctionInfo`] fails.
|
||||
///
|
||||
/// [calling]: crate::func::dynamic_function_mut::DynamicFunctionMut::call
|
||||
/// [`SignatureInfo`]: crate::func::SignatureInfo
|
||||
@ -313,23 +316,22 @@ impl<'env> DynamicFunctionMut<'env> {
|
||||
/// Returns the number of arguments the function expects.
|
||||
///
|
||||
/// For [overloaded] functions that can have a variable number of arguments,
|
||||
/// this will return the minimum and maximum number of arguments.
|
||||
///
|
||||
/// Otherwise, the range will have the same start and end.
|
||||
/// this will contain the full set of counts for all signatures.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_reflect::func::IntoFunctionMut;
|
||||
/// let add = (|a: i32, b: i32| a + b).into_function_mut();
|
||||
/// assert_eq!(add.arg_count(), 2..=2);
|
||||
/// assert!(add.arg_count().contains(2));
|
||||
///
|
||||
/// let add = add.with_overload(|a: f32, b: f32, c: f32| a + b + c);
|
||||
/// assert_eq!(add.arg_count(), 2..=3);
|
||||
/// assert!(add.arg_count().contains(2));
|
||||
/// assert!(add.arg_count().contains(3));
|
||||
/// ```
|
||||
///
|
||||
/// [overloaded]: Self::with_overload
|
||||
pub fn arg_count(&self) -> RangeInclusive<usize> {
|
||||
pub fn arg_count(&self) -> ArgCount {
|
||||
self.internal.arg_count()
|
||||
}
|
||||
}
|
||||
@ -412,7 +414,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
error,
|
||||
FunctionError::ArgCountMismatch {
|
||||
expected: 2..=2,
|
||||
expected: ArgCount::new(2).unwrap(),
|
||||
received: 1
|
||||
}
|
||||
);
|
||||
@ -422,7 +424,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
error,
|
||||
FunctionError::ArgCountMismatch {
|
||||
expected: 2..=2,
|
||||
expected: ArgCount::new(2).unwrap(),
|
||||
received: 1
|
||||
}
|
||||
);
|
||||
|
@ -1,8 +1,10 @@
|
||||
use crate::func::signature::ArgumentSignature;
|
||||
use crate::func::{args::ArgError, Return};
|
||||
use crate::func::{
|
||||
args::{ArgCount, ArgError},
|
||||
Return,
|
||||
};
|
||||
use alloc::borrow::Cow;
|
||||
use bevy_utils::HashSet;
|
||||
use core::ops::RangeInclusive;
|
||||
use thiserror::Error;
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
@ -18,11 +20,8 @@ pub enum FunctionError {
|
||||
#[error(transparent)]
|
||||
ArgError(#[from] ArgError),
|
||||
/// The number of arguments provided does not match the expected number.
|
||||
#[error("expected {expected:?} arguments but received {received}")]
|
||||
ArgCountMismatch {
|
||||
expected: RangeInclusive<usize>,
|
||||
received: usize,
|
||||
},
|
||||
#[error("received {received} arguments but expected one of {expected:?}")]
|
||||
ArgCountMismatch { expected: ArgCount, received: usize },
|
||||
/// No overload was found for the given set of arguments.
|
||||
#[error("no overload found for arguments with signature `{received:?}`, expected one of `{expected:?}`")]
|
||||
NoOverload {
|
||||
@ -51,6 +50,12 @@ pub enum FunctionOverloadError {
|
||||
/// An error that occurs when attempting to add a function overload with a duplicate signature.
|
||||
#[error("could not add function overload: duplicate found for signature `{0:?}`")]
|
||||
DuplicateSignature(ArgumentSignature),
|
||||
#[error(
|
||||
"argument signature `{:?}` has too many arguments (max {})",
|
||||
0,
|
||||
ArgCount::MAX_COUNT
|
||||
)]
|
||||
TooManyArguments(ArgumentSignature),
|
||||
}
|
||||
|
||||
/// An error that occurs when registering a function into a [`FunctionRegistry`].
|
||||
|
@ -1,10 +1,12 @@
|
||||
use crate::{
|
||||
func::{ArgList, DynamicFunction, FunctionInfo, FunctionResult},
|
||||
func::{
|
||||
args::{ArgCount, ArgList},
|
||||
DynamicFunction, FunctionInfo, FunctionResult,
|
||||
},
|
||||
PartialReflect,
|
||||
};
|
||||
use alloc::borrow::Cow;
|
||||
use core::fmt::Debug;
|
||||
use core::ops::RangeInclusive;
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
use alloc::{boxed::Box, format, vec};
|
||||
@ -51,10 +53,8 @@ pub trait Function: PartialReflect + Debug {
|
||||
/// Returns the number of arguments the function expects.
|
||||
///
|
||||
/// For overloaded functions that can have a variable number of arguments,
|
||||
/// this will return the minimum and maximum number of arguments.
|
||||
///
|
||||
/// Otherwise, the range will have the same start and end.
|
||||
fn arg_count(&self) -> RangeInclusive<usize> {
|
||||
/// this will contain the full set of counts for all signatures.
|
||||
fn arg_count(&self) -> ArgCount {
|
||||
self.info().arg_count()
|
||||
}
|
||||
|
||||
|
@ -1,18 +1,17 @@
|
||||
use alloc::{borrow::Cow, vec};
|
||||
use core::fmt::{Debug, Formatter};
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
use alloc::{boxed::Box, format, vec};
|
||||
|
||||
use crate::{
|
||||
func::args::{ArgInfo, GetOwnership, Ownership},
|
||||
func::args::{ArgCount, ArgCountOutOfBoundsError, ArgInfo, GetOwnership, Ownership},
|
||||
func::signature::ArgumentSignature,
|
||||
func::FunctionOverloadError,
|
||||
type_info::impl_type_methods,
|
||||
Type, TypePath,
|
||||
};
|
||||
|
||||
use core::fmt::{Debug, Formatter};
|
||||
use core::ops::RangeInclusive;
|
||||
use variadics_please::all_tuples;
|
||||
|
||||
/// Type information for a [`DynamicFunction`] or [`DynamicFunctionMut`].
|
||||
@ -28,14 +27,21 @@ use variadics_please::all_tuples;
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FunctionInfo {
|
||||
name: Option<Cow<'static, str>>,
|
||||
arg_count: ArgCount,
|
||||
signatures: Box<[SignatureInfo]>,
|
||||
}
|
||||
|
||||
impl FunctionInfo {
|
||||
/// Create a new [`FunctionInfo`] for a function with the given signature.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the given signature has more than the maximum number of arguments
|
||||
/// as specified by [`ArgCount::MAX_COUNT`].
|
||||
pub fn new(signature: SignatureInfo) -> Self {
|
||||
Self {
|
||||
name: signature.name.clone(),
|
||||
arg_count: ArgCount::new(signature.arg_count()).unwrap(),
|
||||
signatures: vec![signature].into(),
|
||||
}
|
||||
}
|
||||
@ -48,9 +54,23 @@ impl FunctionInfo {
|
||||
) -> Result<Self, FunctionOverloadError> {
|
||||
let mut iter = signatures.into_iter();
|
||||
|
||||
let mut info = Self::new(iter.next().ok_or(FunctionOverloadError::MissingSignature)?);
|
||||
let base = iter.next().ok_or(FunctionOverloadError::MissingSignature)?;
|
||||
|
||||
if base.arg_count() > ArgCount::MAX_COUNT {
|
||||
return Err(FunctionOverloadError::TooManyArguments(
|
||||
ArgumentSignature::from(&base),
|
||||
));
|
||||
}
|
||||
|
||||
let mut info = Self::new(base);
|
||||
|
||||
for signature in iter {
|
||||
if signature.arg_count() > ArgCount::MAX_COUNT {
|
||||
return Err(FunctionOverloadError::TooManyArguments(
|
||||
ArgumentSignature::from(&signature),
|
||||
));
|
||||
}
|
||||
|
||||
info = info.with_overload(signature).map_err(|sig| {
|
||||
FunctionOverloadError::DuplicateSignature(ArgumentSignature::from(&sig))
|
||||
})?;
|
||||
@ -102,6 +122,11 @@ impl FunctionInfo {
|
||||
///
|
||||
/// If a signature with the same [`ArgumentSignature`] already exists,
|
||||
/// an error is returned with the given signature.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the given signature has more than the maximum number of arguments
|
||||
/// as specified by [`ArgCount::MAX_COUNT`].
|
||||
pub fn with_overload(mut self, signature: SignatureInfo) -> Result<Self, SignatureInfo> {
|
||||
let is_duplicate = self.signatures.iter().any(|s| {
|
||||
s.arg_count() == signature.arg_count()
|
||||
@ -112,6 +137,7 @@ impl FunctionInfo {
|
||||
return Err(signature);
|
||||
}
|
||||
|
||||
self.arg_count.add(signature.arg_count());
|
||||
self.signatures = IntoIterator::into_iter(self.signatures)
|
||||
.chain(Some(signature))
|
||||
.collect();
|
||||
@ -121,16 +147,9 @@ impl FunctionInfo {
|
||||
/// Returns the number of arguments the function expects.
|
||||
///
|
||||
/// For overloaded functions that can have a variable number of arguments,
|
||||
/// this will return the minimum and maximum number of arguments.
|
||||
///
|
||||
/// Otherwise, the range will have the same start and end.
|
||||
pub fn arg_count(&self) -> RangeInclusive<usize> {
|
||||
self.signatures
|
||||
.iter()
|
||||
.map(SignatureInfo::arg_count)
|
||||
.fold(RangeInclusive::new(usize::MAX, usize::MIN), |acc, count| {
|
||||
RangeInclusive::new((*acc.start()).min(count), (*acc.end()).max(count))
|
||||
})
|
||||
/// this will contain the full set of counts for all signatures.
|
||||
pub fn arg_count(&self) -> ArgCount {
|
||||
self.arg_count
|
||||
}
|
||||
|
||||
/// The signatures of the function.
|
||||
@ -164,6 +183,11 @@ impl FunctionInfo {
|
||||
}
|
||||
|
||||
/// Extend this [`FunctionInfo`] with another without checking for duplicates.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the given signature has more than the maximum number of arguments
|
||||
/// as specified by [`ArgCount::MAX_COUNT`].
|
||||
pub(super) fn extend_unchecked(&mut self, other: FunctionInfo) {
|
||||
if self.name.is_none() {
|
||||
self.name = other.name;
|
||||
@ -173,12 +197,26 @@ impl FunctionInfo {
|
||||
self.signatures = IntoIterator::into_iter(signatures)
|
||||
.chain(IntoIterator::into_iter(other.signatures))
|
||||
.collect();
|
||||
self.arg_count = self
|
||||
.signatures
|
||||
.iter()
|
||||
.fold(ArgCount::default(), |mut count, sig| {
|
||||
count.add(sig.arg_count());
|
||||
count
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SignatureInfo> for FunctionInfo {
|
||||
fn from(info: SignatureInfo) -> Self {
|
||||
Self::new(info)
|
||||
impl TryFrom<SignatureInfo> for FunctionInfo {
|
||||
type Error = ArgCountOutOfBoundsError;
|
||||
|
||||
fn try_from(signature: SignatureInfo) -> Result<Self, Self::Error> {
|
||||
let count = signature.arg_count();
|
||||
if count > ArgCount::MAX_COUNT {
|
||||
return Err(ArgCountOutOfBoundsError(count));
|
||||
}
|
||||
|
||||
Ok(Self::new(signature))
|
||||
}
|
||||
}
|
||||
|
||||
@ -531,7 +569,7 @@ impl<'a> Debug for PrettyPrintSignatureInfo<'a> {
|
||||
///
|
||||
/// let info = print.get_function_info();
|
||||
/// assert!(info.name().unwrap().ends_with("print"));
|
||||
/// assert!(info.arg_count().contains(&1));
|
||||
/// assert!(info.arg_count().contains(1));
|
||||
/// assert_eq!(info.base().args()[0].type_path(), "alloc::string::String");
|
||||
/// assert_eq!(info.base().return_info().type_path(), "()");
|
||||
/// ```
|
||||
|
@ -191,13 +191,13 @@ pub mod signature;
|
||||
mod tests {
|
||||
use alloc::borrow::Cow;
|
||||
|
||||
use super::*;
|
||||
use crate::func::args::ArgCount;
|
||||
use crate::{
|
||||
func::args::{ArgError, ArgList, Ownership},
|
||||
TypePath,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn should_error_on_missing_args() {
|
||||
fn foo(_: i32) {}
|
||||
@ -208,7 +208,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
result.unwrap_err(),
|
||||
FunctionError::ArgCountMismatch {
|
||||
expected: 1..=1,
|
||||
expected: ArgCount::new(1).unwrap(),
|
||||
received: 0
|
||||
}
|
||||
);
|
||||
@ -224,7 +224,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
result.unwrap_err(),
|
||||
FunctionError::ArgCountMismatch {
|
||||
expected: 0..=0,
|
||||
expected: ArgCount::new(0).unwrap(),
|
||||
received: 1
|
||||
}
|
||||
);
|
||||
|
@ -5,8 +5,9 @@ use alloc::{boxed::Box, format, vec};
|
||||
|
||||
use crate::{
|
||||
func::{
|
||||
args::FromArg, macros::count_tokens, ArgList, FunctionError, FunctionResult, IntoReturn,
|
||||
ReflectFnMut,
|
||||
args::{ArgCount, FromArg},
|
||||
macros::count_tokens,
|
||||
ArgList, FunctionError, FunctionResult, IntoReturn, ReflectFnMut,
|
||||
},
|
||||
Reflect, TypePath,
|
||||
};
|
||||
@ -96,7 +97,7 @@ macro_rules! impl_reflect_fn {
|
||||
|
||||
if args.len() != COUNT {
|
||||
return Err(FunctionError::ArgCountMismatch {
|
||||
expected: COUNT..=COUNT,
|
||||
expected: ArgCount::new(COUNT).unwrap(),
|
||||
received: args.len(),
|
||||
});
|
||||
}
|
||||
@ -125,7 +126,7 @@ macro_rules! impl_reflect_fn {
|
||||
|
||||
if args.len() != COUNT {
|
||||
return Err(FunctionError::ArgCountMismatch {
|
||||
expected: COUNT..=COUNT,
|
||||
expected: ArgCount::new(COUNT).unwrap(),
|
||||
received: args.len(),
|
||||
});
|
||||
}
|
||||
@ -155,7 +156,7 @@ macro_rules! impl_reflect_fn {
|
||||
|
||||
if args.len() != COUNT {
|
||||
return Err(FunctionError::ArgCountMismatch {
|
||||
expected: COUNT..=COUNT,
|
||||
expected: ArgCount::new(COUNT).unwrap(),
|
||||
received: args.len(),
|
||||
});
|
||||
}
|
||||
@ -185,7 +186,7 @@ macro_rules! impl_reflect_fn {
|
||||
|
||||
if args.len() != COUNT {
|
||||
return Err(FunctionError::ArgCountMismatch {
|
||||
expected: COUNT..=COUNT,
|
||||
expected: ArgCount::new(COUNT).unwrap(),
|
||||
received: args.len(),
|
||||
});
|
||||
}
|
||||
|
@ -5,7 +5,9 @@ use alloc::{boxed::Box, format, vec};
|
||||
|
||||
use crate::{
|
||||
func::{
|
||||
args::FromArg, macros::count_tokens, ArgList, FunctionError, FunctionResult, IntoReturn,
|
||||
args::{ArgCount, FromArg},
|
||||
macros::count_tokens,
|
||||
ArgList, FunctionError, FunctionResult, IntoReturn,
|
||||
},
|
||||
Reflect, TypePath,
|
||||
};
|
||||
@ -102,7 +104,7 @@ macro_rules! impl_reflect_fn_mut {
|
||||
|
||||
if args.len() != COUNT {
|
||||
return Err(FunctionError::ArgCountMismatch {
|
||||
expected: COUNT..=COUNT,
|
||||
expected: ArgCount::new(COUNT).unwrap(),
|
||||
received: args.len(),
|
||||
});
|
||||
}
|
||||
@ -131,7 +133,7 @@ macro_rules! impl_reflect_fn_mut {
|
||||
|
||||
if args.len() != COUNT {
|
||||
return Err(FunctionError::ArgCountMismatch {
|
||||
expected: COUNT..=COUNT,
|
||||
expected: ArgCount::new(COUNT).unwrap(),
|
||||
received: args.len(),
|
||||
});
|
||||
}
|
||||
@ -161,7 +163,7 @@ macro_rules! impl_reflect_fn_mut {
|
||||
|
||||
if args.len() != COUNT {
|
||||
return Err(FunctionError::ArgCountMismatch {
|
||||
expected: COUNT..=COUNT,
|
||||
expected: ArgCount::new(COUNT).unwrap(),
|
||||
received: args.len(),
|
||||
});
|
||||
}
|
||||
@ -191,7 +193,7 @@ macro_rules! impl_reflect_fn_mut {
|
||||
|
||||
if args.len() != COUNT {
|
||||
return Err(FunctionError::ArgCountMismatch {
|
||||
expected: COUNT..=COUNT,
|
||||
expected: ArgCount::new(COUNT).unwrap(),
|
||||
received: args.len(),
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user