|  |  | @ -1,5 +1,6 @@ | 
			
		
	
		
		
			
				
					
					|  |  |  | use std::iter::Iterator; |  |  |  | use std::iter::Iterator; | 
			
		
	
		
		
			
				
					
					|  |  |  | use std::ptr; |  |  |  | use std::ptr; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | use std::thread; | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | use mongo_c_driver_wrapper::bindings; |  |  |  | use mongo_c_driver_wrapper::bindings; | 
			
		
	
		
		
			
				
					
					|  |  |  | use bson::Document; |  |  |  | use bson::Document; | 
			
		
	
	
		
		
			
				
					|  |  | @ -33,14 +34,14 @@ impl<'a> Cursor<'a> { | 
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     pub fn is_alive(&self) -> bool { |  |  |  |     fn is_alive(&self) -> bool { | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |         assert!(!self.inner.is_null()); |  |  |  |         assert!(!self.inner.is_null()); | 
			
		
	
		
		
			
				
					
					|  |  |  |         unsafe { |  |  |  |         unsafe { | 
			
		
	
		
		
			
				
					
					|  |  |  |             bindings::mongoc_cursor_is_alive(self.inner) == 1 |  |  |  |             bindings::mongoc_cursor_is_alive(self.inner) == 1 | 
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     pub fn more(&self) -> bool { |  |  |  |     fn more(&self) -> bool { | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |         assert!(!self.inner.is_null()); |  |  |  |         assert!(!self.inner.is_null()); | 
			
		
	
		
		
			
				
					
					|  |  |  |         unsafe { |  |  |  |         unsafe { | 
			
		
	
		
		
			
				
					
					|  |  |  |             bindings::mongoc_cursor_more(self.inner) == 1 |  |  |  |             bindings::mongoc_cursor_more(self.inner) == 1 | 
			
		
	
	
		
		
			
				
					|  |  | @ -66,6 +67,11 @@ impl<'a> Iterator for Cursor<'a> { | 
			
		
	
		
		
			
				
					
					|  |  |  |     fn next(&mut self) -> Option<Self::Item> { |  |  |  |     fn next(&mut self) -> Option<Self::Item> { | 
			
		
	
		
		
			
				
					
					|  |  |  |         assert!(!self.inner.is_null()); |  |  |  |         assert!(!self.inner.is_null()); | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         loop { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             if !self.more() { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 return None | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |             // The C driver writes the document to memory and sets an
 |  |  |  |             // The C driver writes the document to memory and sets an
 | 
			
		
	
		
		
			
				
					
					|  |  |  |             // already existing pointer to it.
 |  |  |  |             // already existing pointer to it.
 | 
			
		
	
		
		
			
				
					
					|  |  |  |             let mut bson_ptr: *const bindings::bson_t = ptr::null(); |  |  |  |             let mut bson_ptr: *const bindings::bson_t = ptr::null(); | 
			
		
	
	
		
		
			
				
					|  |  | @ -76,21 +82,37 @@ impl<'a> Iterator for Cursor<'a> { | 
			
		
	
		
		
			
				
					
					|  |  |  |                 ) |  |  |  |                 ) | 
			
		
	
		
		
			
				
					
					|  |  |  |             }; |  |  |  |             }; | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |         if success == 0 { |  |  |  |             // Fetch error that might have occurred while getting
 | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             // the next cursor.
 | 
			
		
	
		
		
			
				
					
					|  |  |  |             let error = self.error(); |  |  |  |             let error = self.error(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             if success == 0 { | 
			
		
	
		
		
			
				
					
					|  |  |  |                 if error.is_empty() { |  |  |  |                 if error.is_empty() { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                     if self.is_alive() { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                         // Since there was no error and the cursor is
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                         // alive this must be a tailing cursor and we'll
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                         // wait for 500ms before trying again.
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                         thread::sleep_ms(500); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                         continue; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                     } else { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                         // No result, no error and cursor not alive anymore
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                         // so we must be at the end.
 | 
			
		
	
		
		
			
				
					
					|  |  |  |                         return None |  |  |  |                         return None | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                     } | 
			
		
	
		
		
			
				
					
					|  |  |  |                 } else { |  |  |  |                 } else { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                     // There was an error
 | 
			
		
	
		
		
			
				
					
					|  |  |  |                     return Some(Err(error.into())) |  |  |  |                     return Some(Err(error.into())) | 
			
		
	
		
		
			
				
					
					|  |  |  |                 } |  |  |  |                 } | 
			
		
	
		
		
			
				
					
					|  |  |  |             } |  |  |  |             } | 
			
		
	
		
		
			
				
					
					|  |  |  |             assert!(!bson_ptr.is_null()); |  |  |  |             assert!(!bson_ptr.is_null()); | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             // Parse and return bson document.
 | 
			
		
	
		
		
			
				
					
					|  |  |  |             let bsonc    = bsonc::Bsonc::from_ptr(bson_ptr); |  |  |  |             let bsonc    = bsonc::Bsonc::from_ptr(bson_ptr); | 
			
		
	
		
		
			
				
					
					|  |  |  |             let document = bsonc.as_document(); |  |  |  |             let document = bsonc.as_document(); | 
			
		
	
		
		
			
				
					
					|  |  |  |             match document { |  |  |  |             match document { | 
			
		
	
		
		
			
				
					
					|  |  |  |             Ok(document) => Some(Ok(document)), |  |  |  |                 Ok(document) => return Some(Ok(document)), | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             Err(error)   => Some(Err(error.into())) |  |  |  |                 Err(error)   => return Some(Err(error.into())) | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             } | 
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
	
		
		
			
				
					|  |  | @ -106,7 +128,9 @@ impl<'a> Drop for Cursor<'a> { | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | #[cfg(test)] |  |  |  | #[cfg(test)] | 
			
		
	
		
		
			
				
					
					|  |  |  | mod tests { |  |  |  | mod tests { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     use std::thread; | 
			
		
	
		
		
			
				
					
					|  |  |  |     use bson; |  |  |  |     use bson; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     use super::super::flags; | 
			
		
	
		
		
			
				
					
					|  |  |  |     use super::super::uri::Uri; |  |  |  |     use super::super::uri::Uri; | 
			
		
	
		
		
			
				
					
					|  |  |  |     use super::super::client::ClientPool; |  |  |  |     use super::super::client::ClientPool; | 
			
		
	
		
		
			
				
					
					|  |  |  |     use super::super::Result; |  |  |  |     use super::super::Result; | 
			
		
	
	
		
		
			
				
					|  |  | @ -136,4 +160,90 @@ mod tests { | 
			
		
	
		
		
			
				
					
					|  |  |  |         // See if we got 10 results and the iterator then stopped
 |  |  |  |         // See if we got 10 results and the iterator then stopped
 | 
			
		
	
		
		
			
				
					
					|  |  |  |         assert_eq!(10, documents.len()); |  |  |  |         assert_eq!(10, documents.len()); | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     #[test] | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     fn test_tailing_cursor() { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         // See: http://api.mongodb.org/c/1.1.8/cursors.html#tailable
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         let uri      = Uri::new("mongodb://localhost:27017/"); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         let pool     = ClientPool::new(uri); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         let client   = pool.pop(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         let database = client.get_database("rust_test"); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         database.get_collection("capped").drop().unwrap_or(()); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         database.get_collection("not_capped").drop().unwrap_or(()); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         let mut options = bson::Document::new(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         options.insert("capped".to_string(), bson::Bson::Boolean(true)); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         options.insert("size".to_string(), bson::Bson::I32(100000)); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         let capped_collection = database.create_collection("capped", Some(&options)).unwrap(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         let normal_collection = database.create_collection("not_capped", None).unwrap(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         let mut flags = flags::Flags::new(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         flags.add(flags::QueryFlag::TailableCursor); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         flags.add(flags::QueryFlag::AwaitData); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         // Try to tail on a normal collection
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         let failing_cursor = normal_collection.find_with_options( | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             &flags, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             0, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             0, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             0, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             &bson::Document::new(), | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             None, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             None | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         ).unwrap(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         let failing_result = failing_cursor.into_iter().next().unwrap(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         assert!(failing_result.is_err()); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         assert_eq!( | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             "MongoError (BsoncError: Unable to execute query: error processing query: ns=rust_test.not_capped limit=0 skip=0\nTree: $and\nSort: {}\nProj: {}\n tailable cursor requested on non capped collection)", | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             format!("{:?}", failing_result.err().unwrap()) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         ); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         let mut document = bson::Document::new(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         document.insert("key_1".to_string(), bson::Bson::String("Value 1".to_string())); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         // Insert some documents into the collection
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         for _ in 0..5 { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             capped_collection.insert(&document).unwrap(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         // Start a tailing iterator in a thread
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         let cloned_pool = pool.clone(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         let guard = thread::spawn(move || { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             let client     = cloned_pool.pop(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             let collection = client.get_collection("rust_test", "capped"); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             let cursor = collection.find_with_options( | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 &flags, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 0, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 0, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 0, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 &bson::Document::new(), | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 None, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 None | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             ).unwrap(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             let mut counter = 0usize; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             for result in cursor.into_iter() { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 assert!(result.is_ok()); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 counter += 1; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 if counter == 15 { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                     break; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             counter | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         }); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         // Wait for the thread to boot up
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         thread::sleep_ms(200); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         // Insert some more documents into the collection
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         for _ in 0..10 { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             capped_collection.insert(&document).unwrap(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         // See if they appeared while iterating the cursor
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         // The for loop returns whenever we get more than
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         // 15 results.
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         assert_eq!(15, guard.join().unwrap()); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
	
		
		
			
				
					|  |  | 
 |