diff --git a/src/document.rs b/src/document.rs index d3c564c..e81bdd6 100644 --- a/src/document.rs +++ b/src/document.rs @@ -236,7 +236,7 @@ impl Database { } } - /// Returns the time, if any, at which a given document will expire and be purged. + /// Returns the time, if any, at which a given document will expire and be purged in milliseconds since the Unix epoch (1/1/1970.). /// Documents don't normally expire; you have to call `set_document_expiration` /// to set a document's expiration time. #[deprecated(note = "please use `document_expiration` on default collection instead")] @@ -250,7 +250,7 @@ impl Database { ); match exp { 0 => Ok(None), - _ if exp > 0 => Ok(Some(Timestamp(exp))), + _ if exp > 0 => Ok(Some(Timestamp::new(exp))), _ => failure(error), } } @@ -260,7 +260,7 @@ impl Database { #[deprecated(note = "please use `set_document_expiration` on default collection instead")] pub fn set_document_expiration(&mut self, doc_id: &str, when: Option) -> Result<()> { let exp: i64 = match when { - Some(Timestamp(n)) => n, + Some(Timestamp { timestamp }) => timestamp, _ => 0, }; unsafe { @@ -448,7 +448,7 @@ impl Collection { } } - /// Returns the time, if any, at which a given document will expire and be purged. + /// Returns the time, if any, at which a given document will expire and be purged in milliseconds since the Unix epoch (1/1/1970.). /// Documents don't normally expire; you have to call set_document_expiration /// to set a document's expiration time. pub fn document_expiration(&self, doc_id: &str) -> Result> { @@ -461,16 +461,16 @@ impl Collection { ); match exp { 0 => Ok(None), - _ if exp > 0 => Ok(Some(Timestamp(exp))), + _ if exp > 0 => Ok(Some(Timestamp::new(exp))), _ => failure(error), } } } - /// Sets or clears the expiration time of a document. + /// Sets or clears the expiration time of a document in milliseconds since the Unix epoch (1/1/1970.). pub fn set_document_expiration(&mut self, doc_id: &str, when: Option) -> Result<()> { let exp: i64 = match when { - Some(Timestamp(n)) => n, + Some(Timestamp { timestamp }) => timestamp, _ => 0, }; unsafe { diff --git a/src/fleece.rs b/src/fleece.rs index a73ba88..3045514 100644 --- a/src/fleece.rs +++ b/src/fleece.rs @@ -291,7 +291,7 @@ impl Value { if t == 0 { return None; } - Some(Timestamp(t)) + Some(Timestamp::new(t)) } } diff --git a/src/lib.rs b/src/lib.rs index 581d234..a6b10f7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,11 +54,11 @@ mod c_api; use self::c_api::{ CBLListenerToken, CBLRefCounted, CBL_DumpInstances, CBL_InstanceCount, CBL_Release, CBL_Retain, - CBLListener_Remove, CBLITE_VERSION, + CBLListener_Remove, CBL_Now, CBLITE_VERSION, }; #[cfg(target_os = "android")] use self::c_api::{CBLError, CBLInitContext, CBL_Init}; -use std::ffi::CStr; +use std::{ffi::CStr, time::Duration}; //////// RE-EXPORT: @@ -78,9 +78,32 @@ pub trait CblRef { fn get_ref(&self) -> Self::Output; } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] /// A time value for document expiration. Defined as milliseconds since the Unix epoch (1/1/1970.) -pub struct Timestamp(pub i64); +pub struct Timestamp { + pub(crate) timestamp: i64, +} + +impl Timestamp { + pub fn now() -> Timestamp { + Timestamp { + timestamp: unsafe { CBL_Now() }, + } + } + + /// Create a Timestamp from milliseconds since the Unix epoch (1/1/1970.) + pub(crate) fn new(milliseconds_from_epoch: i64) -> Self { + Timestamp { + timestamp: milliseconds_from_epoch, + } + } + + pub fn add(&self, duration: Duration) -> Self { + Timestamp { + timestamp: self.timestamp + duration.as_millis() as i64, + } + } +} pub struct Listener { pub listener_token: ListenerToken, diff --git a/tests/document_tests.rs b/tests/document_tests.rs index 8c3a32d..01e5afa 100644 --- a/tests/document_tests.rs +++ b/tests/document_tests.rs @@ -2,7 +2,7 @@ extern crate core; extern crate couchbase_lite; use self::couchbase_lite::*; -use std::time::Duration; +use std::{thread::sleep, time::Duration}; use utils::{init_logging, LeakChecker}; pub mod utils; @@ -296,12 +296,36 @@ fn database_document_expiration() { let mut document = Document::new_with_id("foo"); db.save_document_with_concurency_control(&mut document, ConcurrencyControl::FailOnConflict) .expect("save_document"); + + // No expiration by default let expiration = db.document_expiration("foo").expect("document_expiration"); assert!(expiration.is_none()); - db.set_document_expiration("foo", Some(Timestamp(1000000000))) + + // Set expiration in 2 seconds + let expiration = Timestamp::now().add(Duration::from_secs(2)); + db.set_document_expiration("foo", Some(expiration)) .expect("set_document_expiration"); - let expiration = db.document_expiration("foo").expect("document_expiration"); - assert!(expiration.is_some()); - assert_eq!(expiration.unwrap().0, 1000000000); + + // Check expiration is set up + let doc_expiration = db.document_expiration("foo").expect("document_expiration"); + assert_eq!(doc_expiration.unwrap(), expiration); + + // Check the document is still present after 1 second + sleep(Duration::from_secs(1)); + assert!(db.get_document("foo").is_ok()); + + // Move to expiration time + sleep(Duration::from_secs(1)); + + // Check documents disappears + for _ in 0..5 { + let doc = db.get_document("foo"); + if doc.is_err() || doc.unwrap().is_deleted() { + return; + } + + sleep(Duration::from_secs(1)); + } + panic!("The document is still present 10 seconds after its expiration time"); }); }