diff --git a/Cargo.lock b/Cargo.lock index 9e969ed74..86a3774a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -628,6 +628,7 @@ dependencies = [ "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "charset 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "debug_stub_derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "deltachat_derive 2.0.0", "email 0.0.21 (git+https://github.com/deltachat/rust-email)", diff --git a/Cargo.toml b/Cargo.toml index fbdd15f96..142f3ecd0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,6 +61,7 @@ pretty_env_logger = "0.3.1" rustyline = { version = "4.1.0", optional = true } futures = "0.3.4" +crossbeam-queue = "0.2.1" [dev-dependencies] tempfile = "3.0" diff --git a/examples/simple.rs b/examples/simple.rs index c4a5aa827..c4d96f50f 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -36,7 +36,7 @@ async fn main() { let dir = tempdir().unwrap(); let dbfile = dir.path().join("db.sqlite"); println!("creating database {:?}", dbfile); - let ctx = Context::new(Box::new(cb), "FakeOs".into(), dbfile.into()) + let ctx = Context::new("FakeOs".into(), dbfile.into()) .await .expect("Failed to create context"); let running = Arc::new(RwLock::new(true)); diff --git a/src/chat.rs b/src/chat.rs index 97dbf83e3..531cda89c 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -2766,7 +2766,7 @@ mod tests { #[async_std::test] async fn test_add_contact_to_chat_ex_add_self() { // Adding self to a contact should succeed, even though it's pointless. - let t = test_context(Some(Box::new(logging_cb))).await; + let t = test_context().await; let chat_id = create_group_chat(&t.ctx, VerifiedStatus::Unverified, "foo") .await .unwrap(); @@ -2814,7 +2814,7 @@ mod tests { #[async_std::test] async fn test_add_device_msg_unlabelled() { - let t = test_context(Some(Box::new(logging_cb))).await; + let t = test_context().await; // add two device-messages let mut msg1 = Message::new(Viewtype::Text); @@ -2849,7 +2849,7 @@ mod tests { #[async_std::test] async fn test_add_device_msg_labelled() { - let t = test_context(Some(Box::new(logging_cb))).await; + let t = test_context().await; // add two device-messages with the same label (second attempt is not added) let mut msg1 = Message::new(Viewtype::Text); @@ -2903,7 +2903,7 @@ mod tests { #[async_std::test] async fn test_add_device_msg_label_only() { - let t = test_context(Some(Box::new(logging_cb))).await; + let t = test_context().await; let res = add_device_msg(&t.ctx, Some(""), None).await; assert!(res.is_err()); let res = add_device_msg(&t.ctx, Some("some-label"), None).await; @@ -2923,7 +2923,7 @@ mod tests { #[async_std::test] async fn test_was_device_msg_ever_added() { - let t = test_context(Some(Box::new(logging_cb))).await; + let t = test_context().await; add_device_msg(&t.ctx, Some("some-label"), None).await.ok(); assert!(was_device_msg_ever_added(&t.ctx, "some-label") .await @@ -2947,7 +2947,7 @@ mod tests { #[async_std::test] async fn test_delete_device_chat() { - let t = test_context(Some(Box::new(logging_cb))).await; + let t = test_context().await; let mut msg = Message::new(Viewtype::Text); msg.text = Some("message text".to_string()); @@ -2967,7 +2967,7 @@ mod tests { #[async_std::test] async fn test_device_chat_cannot_sent() { - let t = test_context(Some(Box::new(logging_cb))).await; + let t = test_context().await; t.ctx.update_device_chats().await.unwrap(); let (device_chat_id, _) = create_or_lookup_by_contact_id(&t.ctx, DC_CONTACT_ID_DEVICE, Blocked::Not) @@ -2987,7 +2987,7 @@ mod tests { #[async_std::test] async fn test_delete_and_reset_all_device_msgs() { - let t = test_context(Some(Box::new(logging_cb))).await; + let t = test_context().await; let mut msg = Message::new(Viewtype::Text); msg.text = Some("message text".to_string()); let msg_id1 = add_device_msg(&t.ctx, Some("some-label"), Some(&mut msg)) diff --git a/src/contact.rs b/src/contact.rs index af8a5855e..cb3ab93be 100644 --- a/src/contact.rs +++ b/src/contact.rs @@ -1269,7 +1269,7 @@ mod tests { #[async_std::test] async fn test_is_self_addr() -> Result<()> { - let t = test_context(None).await; + let t = test_context().await; assert!(t.ctx.is_self_addr("me@me.org").await.is_err()); let addr = configure_alice_keypair(&t.ctx).await; diff --git a/src/context.rs b/src/context.rs index cba9d15f4..b0678fe8c 100644 --- a/src/context.rs +++ b/src/context.rs @@ -6,6 +6,7 @@ use std::sync::atomic::AtomicBool; use async_std::path::{Path, PathBuf}; use async_std::sync::{Arc, Mutex, RwLock}; +use crossbeam_queue::SegQueue; use crate::chat::*; use crate::config::Config; @@ -24,16 +25,6 @@ use crate::param::Params; use crate::smtp::Smtp; use crate::sql::Sql; -/// Callback function type for [Context] -/// -/// # Parameters -/// -/// * `context` - The context object as returned by [Context::new]. -/// * `event` - One of the [Event] items. -/// * `data1` - Depends on the event parameter, see [Event]. -/// * `data2` - Depends on the event parameter, see [Event]. -pub type ContextCallback = dyn Fn(&Context, Event) -> () + Send + Sync; - #[derive(DebugStub)] pub struct Context { /// Database file path @@ -48,8 +39,6 @@ pub struct Context { pub mvbox_thread: JobThread, pub smtp: Smtp, pub oauth2_critical: Arc>, - #[debug_stub = "Callback"] - cb: Box, pub os_name: Option, pub cmdline_sel_chat_id: Arc>, pub(crate) bob: Arc>, @@ -58,6 +47,8 @@ pub struct Context { /// Mutex to avoid generating the key for the user more than once. pub generating_key_mutex: Mutex<()>, pub translated_stockstrings: RwLock>, + + pub(crate) logs: SegQueue, } #[derive(Debug, PartialEq, Eq)] @@ -83,11 +74,7 @@ pub fn get_info() -> HashMap<&'static str, String> { impl Context { /// Creates new context. - pub async fn new( - cb: Box, - os_name: String, - dbfile: PathBuf, - ) -> Result { + pub async fn new(os_name: String, dbfile: PathBuf) -> Result { pretty_env_logger::try_init_timed().ok(); let mut blob_fname = OsString::new(); @@ -95,13 +82,12 @@ impl Context { blob_fname.push("-blobs"); let blobdir = dbfile.with_file_name(blob_fname); if !blobdir.exists().await { - std::fs::create_dir_all(&blobdir)?; + async_std::fs::create_dir_all(&blobdir).await?; } - Context::with_blobdir(cb, os_name, dbfile, blobdir).await + Context::with_blobdir(os_name, dbfile, blobdir).await } pub async fn with_blobdir( - cb: Box, os_name: String, dbfile: PathBuf, blobdir: PathBuf, @@ -114,7 +100,6 @@ impl Context { let ctx = Context { blobdir, dbfile, - cb, os_name: Some(os_name), running_state: Arc::new(RwLock::new(Default::default())), sql: Sql::new(), @@ -130,6 +115,7 @@ impl Context { perform_inbox_jobs_needed: Default::default(), generating_key_mutex: Mutex::new(()), translated_stockstrings: RwLock::new(HashMap::new()), + logs: SegQueue::new(), }; ensure!( @@ -151,7 +137,17 @@ impl Context { } pub fn call_cb(&self, event: Event) { - (*self.cb)(self, event); + self.logs.push(event); + } + + pub fn get_next_event(&self) -> Result { + let event = self.logs.pop()?; + + Ok(event) + } + + pub fn has_next_event(&self) -> bool { + !self.logs.is_empty() } /******************************************************************************* @@ -518,7 +514,7 @@ mod tests { let tmp = tempfile::tempdir().unwrap(); let dbfile = tmp.path().join("db.sqlite"); std::fs::write(&dbfile, b"123").unwrap(); - let res = Context::new(Box::new(|_, _| ()), "FakeOs".into(), dbfile.into()).await; + let res = Context::new("FakeOs".into(), dbfile.into()).await; assert!(res.is_err()); } @@ -533,9 +529,7 @@ mod tests { async fn test_blobdir_exists() { let tmp = tempfile::tempdir().unwrap(); let dbfile = tmp.path().join("db.sqlite"); - Context::new(Box::new(|_, _| ()), "FakeOS".into(), dbfile.into()) - .await - .unwrap(); + Context::new("FakeOS".into(), dbfile.into()).await.unwrap(); let blobdir = tmp.path().join("db.sqlite-blobs"); assert!(blobdir.is_dir()); } @@ -546,7 +540,7 @@ mod tests { let dbfile = tmp.path().join("db.sqlite"); let blobdir = tmp.path().join("db.sqlite-blobs"); std::fs::write(&blobdir, b"123").unwrap(); - let res = Context::new(Box::new(|_, _| ()), "FakeOS".into(), dbfile.into()).await; + let res = Context::new("FakeOS".into(), dbfile.into()).await; assert!(res.is_err()); } @@ -556,9 +550,7 @@ mod tests { let subdir = tmp.path().join("subdir"); let dbfile = subdir.join("db.sqlite"); let dbfile2 = dbfile.clone(); - Context::new(Box::new(|_, _| ()), "FakeOS".into(), dbfile.into()) - .await - .unwrap(); + Context::new("FakeOS".into(), dbfile.into()).await.unwrap(); assert!(subdir.is_dir()); assert!(dbfile2.is_file()); } @@ -568,13 +560,7 @@ mod tests { let tmp = tempfile::tempdir().unwrap(); let dbfile = tmp.path().join("db.sqlite"); let blobdir = PathBuf::new(); - let res = Context::with_blobdir( - Box::new(|_, _| ()), - "FakeOS".into(), - dbfile.into(), - blobdir.into(), - ) - .await; + let res = Context::with_blobdir("FakeOS".into(), dbfile.into(), blobdir.into()).await; assert!(res.is_err()); } @@ -583,13 +569,7 @@ mod tests { let tmp = tempfile::tempdir().unwrap(); let dbfile = tmp.path().join("db.sqlite"); let blobdir = tmp.path().join("blobs"); - let res = Context::with_blobdir( - Box::new(|_, _| ()), - "FakeOS".into(), - dbfile.into(), - blobdir.into(), - ) - .await; + let res = Context::with_blobdir("FakeOS".into(), dbfile.into(), blobdir.into()).await; assert!(res.is_err()); } diff --git a/src/error.rs b/src/error.rs index 9381fd0f4..49f87a706 100644 --- a/src/error.rs +++ b/src/error.rs @@ -57,6 +57,9 @@ pub enum Error { #[fail(display = "Not Configured")] NotConfigured, + + #[fail(display = "No event available")] + PopError(crossbeam_queue::PopError), } pub type Result = std::result::Result; @@ -67,6 +70,12 @@ impl From for Error { } } +impl From for Error { + fn from(err: crossbeam_queue::PopError) -> Error { + Error::PopError(err) + } +} + impl From for Error { fn from(err: base64::DecodeError) -> Error { Error::Base64Decode(err) diff --git a/src/imex.rs b/src/imex.rs index c5f82b97e..ae5589321 100644 --- a/src/imex.rs +++ b/src/imex.rs @@ -776,7 +776,7 @@ mod tests { #[async_std::test] async fn test_render_setup_file() { - let t = test_context(Some(Box::new(logging_cb))).await; + let t = test_context().await; configure_alice_keypair(&t.ctx).await; let msg = render_setup_file(&t.ctx, "hello").await.unwrap(); diff --git a/src/test_utils.rs b/src/test_utils.rs index 86c61adf2..394fafc02 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -5,9 +5,8 @@ use tempfile::{tempdir, TempDir}; use crate::config::Config; -use crate::context::{Context, ContextCallback}; +use crate::context::Context; use crate::dc_tools::EmailAddress; -use crate::events::Event; use crate::key::{self, DcKey}; /// A Context and temporary directory. @@ -25,16 +24,10 @@ pub(crate) struct TestContext { /// "db.sqlite" in the [TestContext.dir] directory. /// /// [Context]: crate::context::Context -pub(crate) async fn test_context(callback: Option>) -> TestContext { +pub(crate) async fn test_context() -> TestContext { let dir = tempdir().unwrap(); let dbfile = dir.path().join("db.sqlite"); - let cb: Box = match callback { - Some(cb) => cb, - None => Box::new(|_, _| ()), - }; - let ctx = Context::new(cb, "FakeOs".into(), dbfile.into()) - .await - .unwrap(); + let ctx = Context::new("FakeOs".into(), dbfile.into()).await.unwrap(); TestContext { ctx, dir } } @@ -44,16 +37,7 @@ pub(crate) async fn test_context(callback: Option>) -> Test /// specified in [test_context] but there is no callback hooked up, /// i.e. [Context::call_cb] will always return `0`. pub(crate) async fn dummy_context() -> TestContext { - test_context(None).await -} - -pub(crate) fn logging_cb(_ctx: &Context, evt: Event) { - match evt { - Event::Info(msg) => println!("I: {}", msg), - Event::Warning(msg) => println!("W: {}", msg), - Event::Error(msg) => println!("E: {}", msg), - _ => (), - } + test_context().await } /// Load a pre-generated keypair for alice@example.com from disk. diff --git a/tests/stress.rs b/tests/stress.rs index f44aa1e19..ed6710fc8 100644 --- a/tests/stress.rs +++ b/tests/stress.rs @@ -93,8 +93,6 @@ async fn stress_functions(context: &Context) { // free(qr.cast()); } -fn cb(_context: &Context, _event: Event) {} - #[allow(dead_code)] struct TestContext { ctx: Context, @@ -104,9 +102,7 @@ struct TestContext { async fn create_test_context() -> TestContext { let dir = tempdir().unwrap(); let dbfile = dir.path().join("db.sqlite"); - let ctx = Context::new(Box::new(cb), "FakeOs".into(), dbfile.into()) - .await - .unwrap(); + let ctx = Context::new("FakeOs".into(), dbfile.into()).await.unwrap(); TestContext { ctx, dir } }