A bunch of unit tests for assist
continuous-integration/drone/push Build is failing
Details
continuous-integration/drone/push Build is failing
Details
This commit is contained in:
parent
b9351d057e
commit
54f19cf73e
|
@ -7,12 +7,12 @@ use pkmn_lib_interface::handling::{Script, ScriptCapabilities};
|
||||||
script!(Assist, "assist");
|
script!(Assist, "assist");
|
||||||
|
|
||||||
impl Assist {
|
impl Assist {
|
||||||
|
/// Gets all the learned moves from the entire party, except the ones from the user, and the ones
|
||||||
|
/// that are not allowed to be copied.
|
||||||
fn get_party_moves(party: &Party, user: &Pokemon) -> Vec<MoveData> {
|
fn get_party_moves(party: &Party, user: &Pokemon) -> Vec<MoveData> {
|
||||||
let mut possible_moves = Vec::new();
|
let mut possible_moves = Vec::new();
|
||||||
// Iterate over every mon in the party
|
// Iterate over every mon in the party
|
||||||
for mon_index in 0..party.length() {
|
for mon in party.into_iter().flatten() {
|
||||||
let mon = party.get_pokemon(mon_index);
|
|
||||||
if let Some(mon) = mon {
|
|
||||||
// Ignore moves from the user
|
// Ignore moves from the user
|
||||||
if mon.equals(user) {
|
if mon.equals(user) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -28,7 +28,6 @@ impl Assist {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
possible_moves
|
possible_moves
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,7 +48,13 @@ impl Script for Assist {
|
||||||
fn change_move(&self, choice: TurnChoice, move_name: &mut StringKey) {
|
fn change_move(&self, choice: TurnChoice, move_name: &mut StringKey) {
|
||||||
let user = choice.user();
|
let user = choice.user();
|
||||||
let battle = user.battle().unwrap();
|
let battle = user.battle().unwrap();
|
||||||
let party = battle.find_party_for_pokemon(&user).unwrap().party();
|
let party = battle.find_party_for_pokemon(&user);
|
||||||
|
if party.is_none() {
|
||||||
|
choice.fail();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let party = party.unwrap().party();
|
||||||
let possible_moves = Self::get_party_moves(&party, &user);
|
let possible_moves = Self::get_party_moves(&party, &user);
|
||||||
|
|
||||||
if possible_moves.is_empty() {
|
if possible_moves.is_empty() {
|
||||||
|
@ -64,3 +69,211 @@ impl Script for Assist {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use alloc::boxed::Box;
|
||||||
|
use alloc::rc::Rc;
|
||||||
|
use alloc::vec;
|
||||||
|
use pkmn_lib_interface::app_interface::{
|
||||||
|
MockBaseTurnChoiceData, MockBattle, MockBattleParty, MockBattleRandom, MockLearnedMove,
|
||||||
|
MockMoveData, MockMoveTurnChoiceData, MockParty, MockPokemon,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn mock_pokemon_with_moves(moves: Vec<StringKey>, equals_user: bool) -> Pokemon {
|
||||||
|
let mut mon = MockPokemon::new();
|
||||||
|
let moves = Rc::new(moves);
|
||||||
|
mon.expect_get_learned_move().returning_st(move |index| {
|
||||||
|
let moves = moves.clone();
|
||||||
|
if index < moves.len() {
|
||||||
|
let mut learned_move = MockLearnedMove::new();
|
||||||
|
learned_move.expect_move_data().returning_st(move || {
|
||||||
|
let mut move_data = MockMoveData::new();
|
||||||
|
let moves = moves.clone();
|
||||||
|
move_data
|
||||||
|
.expect_name()
|
||||||
|
.returning_st(move || moves.get(index).unwrap().clone());
|
||||||
|
Rc::new(move_data)
|
||||||
|
});
|
||||||
|
Some(Rc::new(learned_move))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mon.expect_equals().return_const(equals_user);
|
||||||
|
Rc::new(mon)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_party_moves_returns_moves_from_party_length_1() {
|
||||||
|
let mut party = MockParty::new();
|
||||||
|
party.expect_length().once().return_const(1usize);
|
||||||
|
party
|
||||||
|
.expect_get_pokemon()
|
||||||
|
.times(1)
|
||||||
|
.returning_st(move |_| Some(mock_pokemon_with_moves(vec!["tackle".into()], false)));
|
||||||
|
|
||||||
|
let party: Party = Rc::new(party);
|
||||||
|
let user: Pokemon = Rc::new(MockPokemon::new());
|
||||||
|
|
||||||
|
let moves = Assist::get_party_moves(&party, &user);
|
||||||
|
assert_eq!(1, moves.len());
|
||||||
|
assert_eq!(moves[0].name(), "tackle".into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_party_moves_returns_moves_from_party_length_7() {
|
||||||
|
let mut party = MockParty::new();
|
||||||
|
party.expect_length().once().return_const(3usize);
|
||||||
|
party
|
||||||
|
.expect_get_pokemon()
|
||||||
|
.times(3)
|
||||||
|
.returning_st(move |index| {
|
||||||
|
if index == 0 {
|
||||||
|
Some(mock_pokemon_with_moves(
|
||||||
|
vec!["move1".into(), "move2".into(), "move3".into()],
|
||||||
|
false,
|
||||||
|
))
|
||||||
|
} else if index == 1 {
|
||||||
|
Some(mock_pokemon_with_moves(
|
||||||
|
vec!["move1".into(), "move4".into()],
|
||||||
|
false,
|
||||||
|
))
|
||||||
|
} else if index == 2 {
|
||||||
|
Some(mock_pokemon_with_moves(
|
||||||
|
vec!["move2".into(), "move5".into()],
|
||||||
|
false,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let party: Party = Rc::new(party);
|
||||||
|
let user: Pokemon = Rc::new(MockPokemon::new());
|
||||||
|
|
||||||
|
let moves = Assist::get_party_moves(&party, &user);
|
||||||
|
assert_eq!(7, moves.len());
|
||||||
|
assert_eq!(moves[0].name(), "move1".into());
|
||||||
|
assert_eq!(moves[1].name(), "move2".into());
|
||||||
|
assert_eq!(moves[2].name(), "move3".into());
|
||||||
|
assert_eq!(moves[3].name(), "move1".into());
|
||||||
|
assert_eq!(moves[4].name(), "move4".into());
|
||||||
|
assert_eq!(moves[5].name(), "move2".into());
|
||||||
|
assert_eq!(moves[6].name(), "move5".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_party_moves_ignores_user() {
|
||||||
|
let mut party = MockParty::new();
|
||||||
|
party.expect_length().once().return_const(3usize);
|
||||||
|
party
|
||||||
|
.expect_get_pokemon()
|
||||||
|
.times(3)
|
||||||
|
.returning_st(move |index| {
|
||||||
|
if index == 0 {
|
||||||
|
Some(mock_pokemon_with_moves(
|
||||||
|
vec!["move1".into(), "move2".into(), "move3".into()],
|
||||||
|
false,
|
||||||
|
))
|
||||||
|
} else if index == 1 {
|
||||||
|
Some(mock_pokemon_with_moves(
|
||||||
|
vec!["move1".into(), "move4".into()],
|
||||||
|
true,
|
||||||
|
))
|
||||||
|
} else if index == 2 {
|
||||||
|
Some(mock_pokemon_with_moves(
|
||||||
|
vec!["move2".into(), "move5".into()],
|
||||||
|
false,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let party: Party = Rc::new(party);
|
||||||
|
let user: Pokemon = Rc::new(MockPokemon::new());
|
||||||
|
|
||||||
|
let moves = Assist::get_party_moves(&party, &user);
|
||||||
|
assert_eq!(5, moves.len());
|
||||||
|
assert_eq!(moves[0].name(), "move1".into());
|
||||||
|
assert_eq!(moves[1].name(), "move2".into());
|
||||||
|
assert_eq!(moves[2].name(), "move3".into());
|
||||||
|
assert_eq!(moves[3].name(), "move2".into());
|
||||||
|
assert_eq!(moves[4].name(), "move5".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_party_moves_ignores_non_copyable_move() {
|
||||||
|
let mut party = MockParty::new();
|
||||||
|
party.expect_length().once().return_const(1usize);
|
||||||
|
party
|
||||||
|
.expect_get_pokemon()
|
||||||
|
.times(1)
|
||||||
|
.returning_st(move |_| Some(mock_pokemon_with_moves(vec!["chatter".into()], false)));
|
||||||
|
let party: Party = Rc::new(party);
|
||||||
|
let user: Pokemon = Rc::new(MockPokemon::new());
|
||||||
|
|
||||||
|
let moves = Assist::get_party_moves(&party, &user);
|
||||||
|
assert_eq!(0, moves.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn if_moves_found_assist_uses_that_move() {
|
||||||
|
let mut party = MockParty::new();
|
||||||
|
party.expect_length().once().return_const(1usize);
|
||||||
|
party
|
||||||
|
.expect_get_pokemon()
|
||||||
|
.times(1)
|
||||||
|
.returning_st(move |_| Some(mock_pokemon_with_moves(vec!["tackle".into()], false)));
|
||||||
|
|
||||||
|
let party: Party = Rc::new(party);
|
||||||
|
|
||||||
|
let mut base_turn_choice = MockBaseTurnChoiceData::new();
|
||||||
|
|
||||||
|
base_turn_choice.expect_user().once().returning_st(move || {
|
||||||
|
let party = party.clone();
|
||||||
|
let mut user = MockPokemon::new();
|
||||||
|
user.expect_battle().once().returning_st(move || {
|
||||||
|
let party = party.clone();
|
||||||
|
let mut battle = MockBattle::new();
|
||||||
|
battle
|
||||||
|
.expect_find_party_for_pokemon()
|
||||||
|
.once()
|
||||||
|
.returning_st(move |_| {
|
||||||
|
let party = party.clone();
|
||||||
|
let mut p = MockBattleParty::new();
|
||||||
|
p.expect_party().returning_st(move || party.clone());
|
||||||
|
|
||||||
|
Some(Rc::new(p))
|
||||||
|
});
|
||||||
|
|
||||||
|
battle.expect_random().returning_st(|| {
|
||||||
|
let mut random = MockBattleRandom::new();
|
||||||
|
random.expect_get_max().return_const(0);
|
||||||
|
Rc::new(random)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(Rc::new(battle))
|
||||||
|
});
|
||||||
|
|
||||||
|
Rc::new(user)
|
||||||
|
});
|
||||||
|
base_turn_choice.expect_fail().never();
|
||||||
|
|
||||||
|
let base_turn_choice = Rc::new(base_turn_choice);
|
||||||
|
|
||||||
|
let mut move_turn_choice = MockMoveTurnChoiceData::new();
|
||||||
|
move_turn_choice
|
||||||
|
.expect_base()
|
||||||
|
.returning_st(move || base_turn_choice.clone());
|
||||||
|
|
||||||
|
let choice_data = TurnChoice::Move(Box::new(move_turn_choice));
|
||||||
|
|
||||||
|
let mut move_name: StringKey = "".into();
|
||||||
|
let script = Assist::new();
|
||||||
|
script.change_move(choice_data, &mut move_name);
|
||||||
|
assert_eq!(move_name, "tackle".into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -16,8 +16,7 @@ macro_rules! non_copyable {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn can_copy_move(mv: &MoveData) -> bool {
|
pub fn can_copy_move(mv: &MoveData) -> bool {
|
||||||
// A list of all moves that cannot be copied. This expands to a match statement, so is fast on
|
// A list of all moves that cannot be copied. This expands to a match statement on the move name.
|
||||||
// runtime.
|
|
||||||
non_copyable!(
|
non_copyable!(
|
||||||
mv,
|
mv,
|
||||||
"assist",
|
"assist",
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
use crate::app_interface::Party;
|
use crate::app_interface::Party;
|
||||||
use alloc::rc::Rc;
|
use alloc::rc::Rc;
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "mock_data", mockall::automock)]
|
||||||
pub trait BattlePartyTrait {
|
pub trait BattlePartyTrait {
|
||||||
fn party(&self) -> Party;
|
fn party(&self) -> Party;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type BattleParty = Rc<dyn BattlePartyTrait>;
|
pub type BattleParty = Rc<dyn BattlePartyTrait>;
|
||||||
|
#[cfg(feature = "mock_data")]
|
||||||
|
pub type MockBattleParty = MockBattlePartyTrait;
|
||||||
|
|
||||||
#[cfg(not(feature = "mock_data"))]
|
#[cfg(not(feature = "mock_data"))]
|
||||||
mod implementation {
|
mod implementation {
|
||||||
|
|
|
@ -11,6 +11,7 @@ pub enum MoveLearnMethod {
|
||||||
Level = 1,
|
Level = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "mock_data", mockall::automock)]
|
||||||
pub trait LearnedMoveTrait {
|
pub trait LearnedMoveTrait {
|
||||||
fn move_data(&self) -> MoveData;
|
fn move_data(&self) -> MoveData;
|
||||||
fn learn_method(&self) -> MoveLearnMethod;
|
fn learn_method(&self) -> MoveLearnMethod;
|
||||||
|
@ -19,6 +20,8 @@ pub trait LearnedMoveTrait {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type LearnedMove = Rc<dyn LearnedMoveTrait>;
|
pub type LearnedMove = Rc<dyn LearnedMoveTrait>;
|
||||||
|
#[cfg(feature = "mock_data")]
|
||||||
|
pub type MockLearnedMove = MockLearnedMoveTrait;
|
||||||
|
|
||||||
#[cfg(not(feature = "mock_data"))]
|
#[cfg(not(feature = "mock_data"))]
|
||||||
mod implementation {
|
mod implementation {
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
use crate::app_interface::Pokemon;
|
use crate::app_interface::Pokemon;
|
||||||
|
use alloc::boxed::Box;
|
||||||
use alloc::rc::Rc;
|
use alloc::rc::Rc;
|
||||||
|
use core::iter::IntoIterator;
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "mock_data", mockall::automock)]
|
||||||
pub trait PartyTrait {
|
pub trait PartyTrait {
|
||||||
fn get_pokemon(&self, index: usize) -> Option<Pokemon>;
|
fn get_pokemon(&self, index: usize) -> Option<Pokemon>;
|
||||||
fn length(&self) -> usize;
|
fn length(&self) -> usize;
|
||||||
|
@ -8,6 +11,18 @@ pub trait PartyTrait {
|
||||||
|
|
||||||
pub type Party = Rc<dyn PartyTrait>;
|
pub type Party = Rc<dyn PartyTrait>;
|
||||||
|
|
||||||
|
impl<'a> IntoIterator for &'a dyn PartyTrait {
|
||||||
|
type Item = Option<Pokemon>;
|
||||||
|
type IntoIter = ExternIterator<'a, Self::Item>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
ExternIterator::new(self.length(), Box::new(move |i| self.get_pokemon(i)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "mock_data")]
|
||||||
|
pub type MockParty = MockPartyTrait;
|
||||||
|
|
||||||
#[cfg(not(feature = "mock_data"))]
|
#[cfg(not(feature = "mock_data"))]
|
||||||
mod implementation {
|
mod implementation {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -54,5 +69,6 @@ mod implementation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use crate::utils::ExternIterator;
|
||||||
#[cfg(not(feature = "mock_data"))]
|
#[cfg(not(feature = "mock_data"))]
|
||||||
pub use implementation::*;
|
pub use implementation::*;
|
||||||
|
|
|
@ -36,6 +36,7 @@ impl TurnChoice {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "mock_data", mockall::automock)]
|
||||||
pub trait BaseTurnChoiceDataTrait {
|
pub trait BaseTurnChoiceDataTrait {
|
||||||
fn reference(&self) -> u32;
|
fn reference(&self) -> u32;
|
||||||
fn user(&self) -> Pokemon;
|
fn user(&self) -> Pokemon;
|
||||||
|
@ -45,15 +46,20 @@ pub trait BaseTurnChoiceDataTrait {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type BaseTurnChoiceData = Rc<dyn BaseTurnChoiceDataTrait>;
|
pub type BaseTurnChoiceData = Rc<dyn BaseTurnChoiceDataTrait>;
|
||||||
|
#[cfg(feature = "mock_data")]
|
||||||
|
pub type MockBaseTurnChoiceData = MockBaseTurnChoiceDataTrait;
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "mock_data", mockall::automock)]
|
||||||
pub trait MoveTurnChoiceDataTrait {
|
pub trait MoveTurnChoiceDataTrait {
|
||||||
fn base(&self) -> BaseTurnChoiceData;
|
fn base(&self) -> BaseTurnChoiceData;
|
||||||
fn used_move(&self) -> LearnedMove;
|
fn used_move(&self) -> LearnedMove;
|
||||||
fn target_side(&self) -> u8;
|
fn target_side(&self) -> u8;
|
||||||
fn target_index(&self) -> u8;
|
fn target_index(&self) -> u8;
|
||||||
fn priority(&self) -> i8;
|
fn priority(&self) -> i8;
|
||||||
fn move_script(&self) -> Option<&Box<dyn Script>>;
|
fn move_script<'a>(&self) -> Option<&'a Box<dyn Script>>;
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "mock_data")]
|
||||||
|
pub type MockMoveTurnChoiceData = MockMoveTurnChoiceDataTrait;
|
||||||
|
|
||||||
#[cfg(not(feature = "mock_data"))]
|
#[cfg(not(feature = "mock_data"))]
|
||||||
mod implementation {
|
mod implementation {
|
||||||
|
|
|
@ -29,6 +29,7 @@ pub enum MoveTarget {
|
||||||
OnSelf,
|
OnSelf,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "mock_data", mockall::automock)]
|
||||||
pub trait MoveDataTrait {
|
pub trait MoveDataTrait {
|
||||||
fn name(&self) -> StringKey;
|
fn name(&self) -> StringKey;
|
||||||
fn move_type(&self) -> u8;
|
fn move_type(&self) -> u8;
|
||||||
|
@ -42,6 +43,8 @@ pub trait MoveDataTrait {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type MoveData = Rc<dyn MoveDataTrait>;
|
pub type MoveData = Rc<dyn MoveDataTrait>;
|
||||||
|
#[cfg(feature = "mock_data")]
|
||||||
|
pub type MockMoveData = MockMoveDataTrait;
|
||||||
|
|
||||||
#[cfg(not(feature = "mock_data"))]
|
#[cfg(not(feature = "mock_data"))]
|
||||||
mod implementation {
|
mod implementation {
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::handling::Cacheable;
|
||||||
use crate::{ExternRef, ExternalReferenceType};
|
use crate::{ExternRef, ExternalReferenceType};
|
||||||
use alloc::rc::Rc;
|
use alloc::rc::Rc;
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
use core::fmt::{Display, Formatter};
|
use core::fmt::{Debug, Display, Formatter};
|
||||||
use cstr_core::CString;
|
use cstr_core::CString;
|
||||||
|
|
||||||
struct StringKeyInner {
|
struct StringKeyInner {
|
||||||
|
@ -19,6 +19,12 @@ pub struct StringKey {
|
||||||
data: Rc<StringKeyInner>,
|
data: Rc<StringKeyInner>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Debug for StringKey {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "\"{}\"", self.str().to_str().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl StringKey {
|
impl StringKey {
|
||||||
#[cfg(not(feature = "mock_data"))]
|
#[cfg(not(feature = "mock_data"))]
|
||||||
pub(crate) fn new(ptr: ExternRef<Self>) -> Self {
|
pub(crate) fn new(ptr: ExternRef<Self>) -> Self {
|
||||||
|
@ -185,4 +191,10 @@ mod test {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&str> for StringKey {
|
||||||
|
fn from(value: &str) -> Self {
|
||||||
|
StringKey::new(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use alloc::boxed::Box;
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
#[cfg(not(feature = "mock_data"))]
|
#[cfg(not(feature = "mock_data"))]
|
||||||
macro_rules! println { ($($args:tt)*) => { crate::utils::print_raw(alloc::format!($($args)*).as_bytes()); } }
|
macro_rules! println { ($($args:tt)*) => { crate::utils::print_raw(alloc::format!($($args)*).as_bytes()); } }
|
||||||
|
@ -77,3 +79,33 @@ mod implementation {
|
||||||
|
|
||||||
#[cfg(not(feature = "mock_data"))]
|
#[cfg(not(feature = "mock_data"))]
|
||||||
pub use implementation::*;
|
pub use implementation::*;
|
||||||
|
|
||||||
|
pub struct ExternIterator<'a, T> {
|
||||||
|
len: usize,
|
||||||
|
index: usize,
|
||||||
|
getter: Box<dyn Fn(usize) -> T + 'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> ExternIterator<'a, T> {
|
||||||
|
pub fn new(len: usize, f: Box<dyn Fn(usize) -> T + 'a>) -> Self {
|
||||||
|
Self {
|
||||||
|
len,
|
||||||
|
index: 0,
|
||||||
|
getter: f,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> Iterator for ExternIterator<'a, T> {
|
||||||
|
type Item = T;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let index = self.index;
|
||||||
|
if index >= self.len {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
self.index += 1;
|
||||||
|
Some((self.getter)(index))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue