Path: blob/main/crates/bevy_reflect/src/func/args/list.rs
6600 views
use crate::{1func::{2args::{Arg, ArgValue, FromArg},3ArgError,4},5PartialReflect, Reflect, TypePath,6};7use alloc::{8boxed::Box,9collections::vec_deque::{Iter, VecDeque},10};1112/// A list of arguments that can be passed to a [`DynamicFunction`] or [`DynamicFunctionMut`].13///14/// # Example15///16/// ```17/// # use bevy_reflect::func::{ArgValue, ArgList};18/// let foo = 123;19/// let bar = 456;20/// let mut baz = 789;21/// let args = ArgList::new()22/// // Push an owned argument23/// .with_owned(foo)24/// // Push an owned and boxed argument25/// .with_boxed(Box::new(foo))26/// // Push a reference argument27/// .with_ref(&bar)28/// // Push a mutable reference argument29/// .with_mut(&mut baz)30/// // Push a manually constructed argument31/// .with_arg(ArgValue::Ref(&3.14));32/// ```33///34/// [arguments]: Arg35/// [`DynamicFunction`]: crate::func::DynamicFunction36/// [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut37#[derive(Default, Debug)]38pub struct ArgList<'a> {39list: VecDeque<Arg<'a>>,40/// A flag that indicates if the list needs to be re-indexed.41///42/// This flag should be set when an argument is removed from the beginning of the list,43/// so that any future push operations will re-index the arguments.44needs_reindex: bool,45}4647impl<'a> ArgList<'a> {48/// Create a new empty list of arguments.49pub fn new() -> Self {50Self {51list: VecDeque::new(),52needs_reindex: false,53}54}5556/// Push an [`ArgValue`] onto the list.57///58/// If an argument was previously removed from the beginning of the list,59/// this method will also re-index the list.60pub fn push_arg(&mut self, arg: ArgValue<'a>) {61if self.needs_reindex {62for (index, arg) in self.list.iter_mut().enumerate() {63arg.set_index(index);64}65self.needs_reindex = false;66}6768let index = self.list.len();69self.list.push_back(Arg::new(index, arg));70}7172/// Push an [`ArgValue::Ref`] onto the list with the given reference.73///74/// If an argument was previously removed from the beginning of the list,75/// this method will also re-index the list.76pub fn push_ref(&mut self, arg: &'a dyn PartialReflect) {77self.push_arg(ArgValue::Ref(arg));78}7980/// Push an [`ArgValue::Mut`] onto the list with the given mutable reference.81///82/// If an argument was previously removed from the beginning of the list,83/// this method will also re-index the list.84pub fn push_mut(&mut self, arg: &'a mut dyn PartialReflect) {85self.push_arg(ArgValue::Mut(arg));86}8788/// Push an [`ArgValue::Owned`] onto the list with the given owned value.89///90/// If an argument was previously removed from the beginning of the list,91/// this method will also re-index the list.92pub fn push_owned(&mut self, arg: impl PartialReflect) {93self.push_arg(ArgValue::Owned(Box::new(arg)));94}9596/// Push an [`ArgValue::Owned`] onto the list with the given boxed value.97///98/// If an argument was previously removed from the beginning of the list,99/// this method will also re-index the list.100pub fn push_boxed(&mut self, arg: Box<dyn PartialReflect>) {101self.push_arg(ArgValue::Owned(arg));102}103104/// Push an [`ArgValue`] onto the list.105///106/// If an argument was previously removed from the beginning of the list,107/// this method will also re-index the list.108pub fn with_arg(mut self, arg: ArgValue<'a>) -> Self {109self.push_arg(arg);110self111}112113/// Push an [`ArgValue::Ref`] onto the list with the given reference.114///115/// If an argument was previously removed from the beginning of the list,116/// this method will also re-index the list.117pub fn with_ref(self, arg: &'a dyn PartialReflect) -> Self {118self.with_arg(ArgValue::Ref(arg))119}120121/// Push an [`ArgValue::Mut`] onto the list with the given mutable reference.122///123/// If an argument was previously removed from the beginning of the list,124/// this method will also re-index the list.125pub fn with_mut(self, arg: &'a mut dyn PartialReflect) -> Self {126self.with_arg(ArgValue::Mut(arg))127}128129/// Push an [`ArgValue::Owned`] onto the list with the given owned value.130///131/// If an argument was previously removed from the beginning of the list,132/// this method will also re-index the list.133pub fn with_owned(self, arg: impl PartialReflect) -> Self {134self.with_arg(ArgValue::Owned(Box::new(arg)))135}136137/// Push an [`ArgValue::Owned`] onto the list with the given boxed value.138///139/// If an argument was previously removed from the beginning of the list,140/// this method will also re-index the list.141pub fn with_boxed(self, arg: Box<dyn PartialReflect>) -> Self {142self.with_arg(ArgValue::Owned(arg))143}144145/// Remove the first argument in the list and return it.146///147/// It's generally preferred to use [`Self::take`] instead of this method148/// as it provides a more ergonomic way to immediately downcast the argument.149pub fn take_arg(&mut self) -> Result<Arg<'a>, ArgError> {150self.needs_reindex = true;151self.list.pop_front().ok_or(ArgError::EmptyArgList)152}153154/// Remove the first argument in the list and return `Ok(T::This)`.155///156/// If the list is empty or the [`FromArg::from_arg`] call fails, returns an error.157///158/// # Example159///160/// ```161/// # use bevy_reflect::func::ArgList;162/// let a = 1u32;163/// let b = 2u32;164/// let mut c = 3u32;165/// let mut args = ArgList::new().with_owned(a).with_ref(&b).with_mut(&mut c);166///167/// let a = args.take::<u32>().unwrap();168/// assert_eq!(a, 1);169///170/// let b = args.take::<&u32>().unwrap();171/// assert_eq!(*b, 2);172///173/// let c = args.take::<&mut u32>().unwrap();174/// assert_eq!(*c, 3);175/// ```176pub fn take<T: FromArg>(&mut self) -> Result<T::This<'a>, ArgError> {177self.take_arg()?.take::<T>()178}179180/// Remove the first argument in the list and return `Ok(T)` if the argument is [`ArgValue::Owned`].181///182/// If the list is empty or the argument is not owned, returns an error.183///184/// It's generally preferred to use [`Self::take`] instead of this method.185///186/// # Example187///188/// ```189/// # use bevy_reflect::func::ArgList;190/// let value = 123u32;191/// let mut args = ArgList::new().with_owned(value);192/// let value = args.take_owned::<u32>().unwrap();193/// assert_eq!(value, 123);194/// ```195pub fn take_owned<T: Reflect + TypePath>(&mut self) -> Result<T, ArgError> {196self.take_arg()?.take_owned()197}198199/// Remove the first argument in the list and return `Ok(&T)` if the argument is [`ArgValue::Ref`].200///201/// If the list is empty or the argument is not a reference, returns an error.202///203/// It's generally preferred to use [`Self::take`] instead of this method.204///205/// # Example206///207/// ```208/// # use bevy_reflect::func::ArgList;209/// let value = 123u32;210/// let mut args = ArgList::new().with_ref(&value);211/// let value = args.take_ref::<u32>().unwrap();212/// assert_eq!(*value, 123);213/// ```214pub fn take_ref<T: Reflect + TypePath>(&mut self) -> Result<&'a T, ArgError> {215self.take_arg()?.take_ref()216}217218/// Remove the first argument in the list and return `Ok(&mut T)` if the argument is [`ArgValue::Mut`].219///220/// If the list is empty or the argument is not a mutable reference, returns an error.221///222/// It's generally preferred to use [`Self::take`] instead of this method.223///224/// # Example225///226/// ```227/// # use bevy_reflect::func::ArgList;228/// let mut value = 123u32;229/// let mut args = ArgList::new().with_mut(&mut value);230/// let value = args.take_mut::<u32>().unwrap();231/// assert_eq!(*value, 123);232/// ```233pub fn take_mut<T: Reflect + TypePath>(&mut self) -> Result<&'a mut T, ArgError> {234self.take_arg()?.take_mut()235}236237/// Remove the last argument in the list and return it.238///239/// It's generally preferred to use [`Self::pop`] instead of this method240/// as it provides a more ergonomic way to immediately downcast the argument.241pub fn pop_arg(&mut self) -> Result<Arg<'a>, ArgError> {242self.list.pop_back().ok_or(ArgError::EmptyArgList)243}244245/// Remove the last argument in the list and return `Ok(T::This)`.246///247/// If the list is empty or the [`FromArg::from_arg`] call fails, returns an error.248///249/// # Example250///251/// ```252/// # use bevy_reflect::func::ArgList;253/// let a = 1u32;254/// let b = 2u32;255/// let mut c = 3u32;256/// let mut args = ArgList::new().with_owned(a).with_ref(&b).with_mut(&mut c);257///258/// let c = args.pop::<&mut u32>().unwrap();259/// assert_eq!(*c, 3);260///261/// let b = args.pop::<&u32>().unwrap();262/// assert_eq!(*b, 2);263///264/// let a = args.pop::<u32>().unwrap();265/// assert_eq!(a, 1);266/// ```267pub fn pop<T: FromArg>(&mut self) -> Result<T::This<'a>, ArgError> {268self.pop_arg()?.take::<T>()269}270271/// Remove the last argument in the list and return `Ok(T)` if the argument is [`ArgValue::Owned`].272///273/// If the list is empty or the argument is not owned, returns an error.274///275/// It's generally preferred to use [`Self::pop`] instead of this method.276///277/// # Example278///279/// ```280/// # use bevy_reflect::func::ArgList;281/// let value = 123u32;282/// let mut args = ArgList::new().with_owned(value);283/// let value = args.pop_owned::<u32>().unwrap();284/// assert_eq!(value, 123);285/// ```286pub fn pop_owned<T: Reflect + TypePath>(&mut self) -> Result<T, ArgError> {287self.pop_arg()?.take_owned()288}289290/// Remove the last argument in the list and return `Ok(&T)` if the argument is [`ArgValue::Ref`].291///292/// If the list is empty or the argument is not a reference, returns an error.293///294/// It's generally preferred to use [`Self::pop`] instead of this method.295///296/// # Example297///298/// ```299/// # use bevy_reflect::func::ArgList;300/// let value = 123u32;301/// let mut args = ArgList::new().with_ref(&value);302/// let value = args.pop_ref::<u32>().unwrap();303/// assert_eq!(*value, 123);304/// ```305pub fn pop_ref<T: Reflect + TypePath>(&mut self) -> Result<&'a T, ArgError> {306self.pop_arg()?.take_ref()307}308309/// Remove the last argument in the list and return `Ok(&mut T)` if the argument is [`ArgValue::Mut`].310///311/// If the list is empty or the argument is not a mutable reference, returns an error.312///313/// It's generally preferred to use [`Self::pop`] instead of this method.314///315/// # Example316///317/// ```318/// # use bevy_reflect::func::ArgList;319/// let mut value = 123u32;320/// let mut args = ArgList::new().with_mut(&mut value);321/// let value = args.pop_mut::<u32>().unwrap();322/// assert_eq!(*value, 123);323/// ```324pub fn pop_mut<T: Reflect + TypePath>(&mut self) -> Result<&'a mut T, ArgError> {325self.pop_arg()?.take_mut()326}327328/// Returns an iterator over the arguments in the list.329pub fn iter(&self) -> Iter<'_, Arg<'a>> {330self.list.iter()331}332333/// Returns the number of arguments in the list.334pub fn len(&self) -> usize {335self.list.len()336}337338/// Returns `true` if the list of arguments is empty.339pub fn is_empty(&self) -> bool {340self.list.is_empty()341}342}343344#[cfg(test)]345mod tests {346use super::*;347use alloc::string::String;348349#[test]350fn should_push_arguments_in_order() {351let args = ArgList::new()352.with_owned(123)353.with_owned(456)354.with_owned(789);355356assert_eq!(args.len(), 3);357assert_eq!(args.list[0].index(), 0);358assert_eq!(args.list[1].index(), 1);359assert_eq!(args.list[2].index(), 2);360}361362#[test]363fn should_push_arg_with_correct_ownership() {364let a = String::from("a");365let b = String::from("b");366let mut c = String::from("c");367let d = String::from("d");368let e = String::from("e");369let f = String::from("f");370let mut g = String::from("g");371372let args = ArgList::new()373.with_arg(ArgValue::Owned(Box::new(a)))374.with_arg(ArgValue::Ref(&b))375.with_arg(ArgValue::Mut(&mut c))376.with_owned(d)377.with_boxed(Box::new(e))378.with_ref(&f)379.with_mut(&mut g);380381assert!(matches!(args.list[0].value(), &ArgValue::Owned(_)));382assert!(matches!(args.list[1].value(), &ArgValue::Ref(_)));383assert!(matches!(args.list[2].value(), &ArgValue::Mut(_)));384assert!(matches!(args.list[3].value(), &ArgValue::Owned(_)));385assert!(matches!(args.list[4].value(), &ArgValue::Owned(_)));386assert!(matches!(args.list[5].value(), &ArgValue::Ref(_)));387assert!(matches!(args.list[6].value(), &ArgValue::Mut(_)));388}389390#[test]391fn should_take_args_in_order() {392let a = String::from("a");393let b = 123_i32;394let c = 456_usize;395let mut d = 5.78_f32;396397let mut args = ArgList::new()398.with_owned(a)399.with_ref(&b)400.with_ref(&c)401.with_mut(&mut d);402403assert_eq!(args.len(), 4);404assert_eq!(args.take_owned::<String>().unwrap(), String::from("a"));405assert_eq!(args.take::<&i32>().unwrap(), &123);406assert_eq!(args.take_ref::<usize>().unwrap(), &456);407assert_eq!(args.take_mut::<f32>().unwrap(), &mut 5.78);408assert_eq!(args.len(), 0);409}410411#[test]412fn should_pop_args_in_reverse_order() {413let a = String::from("a");414let b = 123_i32;415let c = 456_usize;416let mut d = 5.78_f32;417418let mut args = ArgList::new()419.with_owned(a)420.with_ref(&b)421.with_ref(&c)422.with_mut(&mut d);423424assert_eq!(args.len(), 4);425assert_eq!(args.pop_mut::<f32>().unwrap(), &mut 5.78);426assert_eq!(args.pop_ref::<usize>().unwrap(), &456);427assert_eq!(args.pop::<&i32>().unwrap(), &123);428assert_eq!(args.pop_owned::<String>().unwrap(), String::from("a"));429assert_eq!(args.len(), 0);430}431432#[test]433fn should_reindex_on_push_after_take() {434let mut args = ArgList::new()435.with_owned(123)436.with_owned(456)437.with_owned(789);438439assert!(!args.needs_reindex);440441args.take_arg().unwrap();442assert!(args.needs_reindex);443assert!(args.list[0].value().reflect_partial_eq(&456).unwrap());444assert_eq!(args.list[0].index(), 1);445assert!(args.list[1].value().reflect_partial_eq(&789).unwrap());446assert_eq!(args.list[1].index(), 2);447448let args = args.with_owned(123);449assert!(!args.needs_reindex);450assert!(args.list[0].value().reflect_partial_eq(&456).unwrap());451assert_eq!(args.list[0].index(), 0);452assert!(args.list[1].value().reflect_partial_eq(&789).unwrap());453assert_eq!(args.list[1].index(), 1);454assert!(args.list[2].value().reflect_partial_eq(&123).unwrap());455assert_eq!(args.list[2].index(), 2);456}457}458459460