From 58146e4a227c4cc4c49669a749dc04ee54d8f009 Mon Sep 17 00:00:00 2001
From: Shaopeng Wang <spxwang@gmail.com>
Date: Sat, 16 Nov 2019 21:10:07 +1300
Subject: [PATCH] LinkedList enumerator (#27)

* LinkedList iterator.

* Update naming.

* Default impl for LinkedItem.

* LinkList: add 'take_all' enumeration.

* Reuse Enumerator for take_all and enumerate fn.

* LinkedList: update take fn impl.
---
 utilities/src/linked_item.rs | 131 ++++++++++++++++++++++++++++++-----
 1 file changed, 115 insertions(+), 16 deletions(-)

diff --git a/utilities/src/linked_item.rs b/utilities/src/linked_item.rs
index 348fd82..0d8bf7a 100644
--- a/utilities/src/linked_item.rs
+++ b/utilities/src/linked_item.rs
@@ -1,4 +1,5 @@
 use codec::{Decode, Encode};
+use rstd::{iter, marker};
 use sr_primitives::{traits::Member, RuntimeDebug};
 use support::{Parameter, StorageMap};
 
@@ -8,9 +9,15 @@ pub struct LinkedItem<Item> {
 	pub next: Option<Item>,
 }
 
+impl<Item> Default for LinkedItem<Item> {
+	fn default() -> Self {
+		LinkedItem { prev: None, next: None }
+	}
+}
+
 pub struct LinkedList<Storage, Key, Item>(rstd::marker::PhantomData<(Storage, Key, Item)>);
 
-impl<Storage, Key, Value> LinkedList<Storage, Key, Value>
+impl<'a, Storage, Key, Value> LinkedList<Storage, Key, Value>
 where
 	Value: Parameter + Member + Copy,
 	Key: Parameter,
@@ -20,12 +27,18 @@ where
 		Self::read(key, None)
 	}
 
-	fn write_head(account: &Key, item: LinkedItem<Value>) {
-		Self::write(account, None, item);
+	fn write_head(key: &Key, item: LinkedItem<Value>) {
+		Self::write(key, None, item);
 	}
 
 	fn read(key: &Key, value: Option<Value>) -> LinkedItem<Value> {
-		Storage::get(&(key.clone(), value)).unwrap_or_else(|| LinkedItem { prev: None, next: None })
+		Storage::get(&(key.clone(), value)).unwrap_or_else(|| Default::default())
+	}
+
+	fn take(key: &Key, value: Value) -> LinkedItem<Value> {
+		let item = Self::read(key, Some(value));
+		Self::remove(key, value);
+		item
 	}
 
 	fn write(key: &Key, value: Option<Value>, item: LinkedItem<Value>) {
@@ -74,8 +87,68 @@ where
 			Self::write(key, item.next, new_next);
 		}
 	}
+
+	pub fn enumerate(key: &'a Key) -> Enumerator<'a, Key, Value, Self> {
+		Enumerator::<'a, Key, Value, Self> {
+			key,
+			should_take: false,
+			linkage: Self::read_head(key),
+			_phantom: Default::default(),
+		}
+	}
+
+	pub fn take_all(key: &'a Key) -> Enumerator<'a, Key, Value, Self> {
+		Enumerator::<'a, Key, Value, Self> {
+			key,
+			should_take: true,
+			linkage: Self::read_head(key),
+			_phantom: Default::default(),
+		}
+	}
+}
+
+pub struct Enumerator<'a, Key, Value, LinkedList> {
+	key: &'a Key,
+	should_take: bool,
+	linkage: LinkedItem<Value>,
+	_phantom: marker::PhantomData<LinkedList>,
+}
+
+impl<'a, Key, Value, Storage> iter::Iterator for Enumerator<'a, Key, Value, LinkedList<Storage, Key, Value>>
+where
+	Key: Parameter,
+	Value: Parameter + Member + Copy,
+	Storage: StorageMap<(Key, Option<Value>), LinkedItem<Value>, Query = Option<LinkedItem<Value>>>,
+{
+	type Item = Value;
+
+	fn next(&mut self) -> Option<Self::Item> {
+		let next_value = self.linkage.next?;
+		if self.should_take {
+			self.linkage = <LinkedList<Storage, Key, Value>>::take(self.key, next_value);
+		} else {
+			self.linkage = <LinkedList<Storage, Key, Value>>::read(self.key, Some(next_value));
+		}
+		Some(next_value)
+	}
 }
 
+// FIXME: error[E0366]: Implementations of Drop cannot be specialized
+// impl<'a, Key, Value, Storage> Drop for Enumerator<'a, Key, Value, LinkedList<Storage, Key, Value>>
+// where
+// 	Key: Parameter,
+// 	Value: Parameter + Member + Copy,
+// 	Storage: StorageMap<(Key, Option<Value>), LinkedItem<Value>, Query = Option<LinkedItem<Value>>>,
+// {
+// 	fn drop(&mut self) {
+// 		if !self.should_take {
+// 			return
+// 		}
+
+// 		while self.next() != None {}
+// 	}
+// }
+
 #[cfg(test)]
 mod tests {
 	use super::*;
@@ -152,10 +225,7 @@ mod tests {
 				})
 			);
 
-			assert_eq!(
-				TestItem::get(&(0, Some(1))),
-				Some(TestLinkedItem { prev: None, next: None })
-			);
+			assert_eq!(TestItem::get(&(0, Some(1))), Some(Default::default()));
 
 			TestLinkedList::append(&0, 2);
 
@@ -268,17 +338,11 @@ mod tests {
 
 			assert_eq!(TestItem::get(&(0, Some(2))), None);
 
-			assert_eq!(
-				TestItem::get(&(0, Some(3))),
-				Some(TestLinkedItem { prev: None, next: None })
-			);
+			assert_eq!(TestItem::get(&(0, Some(3))), Some(Default::default()));
 
 			TestLinkedList::remove(&0, 3);
 
-			assert_eq!(
-				TestItem::get(&(0, None)),
-				Some(TestLinkedItem { prev: None, next: None })
-			);
+			assert_eq!(TestItem::get(&(0, None)), Some(Default::default()));
 
 			assert_eq!(TestItem::get(&(0, Some(1))), None);
 
@@ -287,4 +351,39 @@ mod tests {
 			assert_eq!(TestItem::get(&(0, Some(2))), None);
 		});
 	}
+
+	#[test]
+	fn linked_list_can_enumerate() {
+		new_test_ext().execute_with(|| {
+			assert_eq!(TestLinkedList::enumerate(&0).collect::<Vec<_>>(), []);
+
+			TestLinkedList::append(&0, 1);
+			TestLinkedList::append(&0, 2);
+			TestLinkedList::append(&0, 3);
+
+			// iteration
+			assert_eq!(TestLinkedList::enumerate(&0).collect::<Vec<_>>(), [1, 2, 3]);
+
+			// should not take
+			assert_eq!(TestLinkedList::enumerate(&0).collect::<Vec<_>>(), [1, 2, 3]);
+		});
+	}
+
+	#[test]
+	fn linked_list_can_take_all() {
+		new_test_ext().execute_with(|| {
+			assert_eq!(TestLinkedList::take_all(&0).collect::<Vec<_>>(), []);
+
+			TestLinkedList::append(&0, 1);
+			TestLinkedList::append(&0, 2);
+			TestLinkedList::append(&0, 3);
+
+			assert_eq!(TestLinkedList::take_all(&0).collect::<Vec<_>>(), [1, 2, 3]);
+
+			assert_eq!(TestItem::get(&(0, Some(1))), None);
+			assert_eq!(TestItem::get(&(0, Some(2))), None);
+			assert_eq!(TestItem::get(&(0, Some(3))), None);
+			assert_eq!(TestLinkedList::enumerate(&0).collect::<Vec<_>>(), []);
+		});
+	}
 }
-- 
GitLab