Refactor context creation to a builder pattern

This mainly refactors context creation to use a builder pattern.  It
also adds a logdir option to this builder patter but doesn't yet use
this.
This commit is contained in:
Floris Bruynooghe
2019-11-06 17:17:44 +01:00
parent dacde72456
commit f8736895cd
6 changed files with 133 additions and 40 deletions

View File

@@ -24,7 +24,7 @@ use num_traits::{FromPrimitive, ToPrimitive};
use deltachat::constants::DC_MSG_ID_LAST_SPECIAL; use deltachat::constants::DC_MSG_ID_LAST_SPECIAL;
use deltachat::contact::Contact; use deltachat::contact::Contact;
use deltachat::context::Context; use deltachat::context::{Context, ContextBuilder};
use deltachat::dc_tools::{ use deltachat::dc_tools::{
as_path, dc_strdup, to_opt_string_lossy, to_string_lossy, OsStrExt, StrExt, as_path, dc_strdup, to_opt_string_lossy, to_string_lossy, OsStrExt, StrExt,
}; };
@@ -248,22 +248,15 @@ pub unsafe extern "C" fn dc_open(
} }
let ffi_context = &*context; let ffi_context = &*context;
let rust_cb = move |_ctx: &Context, evt: Event| ffi_context.translate_cb(evt); let rust_cb = move |_ctx: &Context, evt: Event| ffi_context.translate_cb(evt);
let mut builder = ContextBuilder::new(
let ctx = if blobdir.is_null() || *blobdir == 0 {
Context::new(
Box::new(rust_cb), Box::new(rust_cb),
ffi_context.os_name.clone(), ffi_context.os_name.clone(),
as_path(dbfile).to_path_buf(), as_path(dbfile).to_path_buf(),
) );
} else { if !blobdir.is_null() && *blobdir != 0 {
Context::with_blobdir( builder = builder.blobdir(as_path(blobdir).to_path_buf());
Box::new(rust_cb), }
ffi_context.os_name.clone(), match builder.create() {
as_path(dbfile).to_path_buf(),
as_path(blobdir).to_path_buf(),
)
};
match ctx {
Ok(ctx) => { Ok(ctx) => {
let mut inner_guard = ffi_context.inner.write().unwrap(); let mut inner_guard = ffi_context.inner.write().unwrap();
*inner_guard = Some(ctx); *inner_guard = Some(ctx);

View File

@@ -366,11 +366,12 @@ fn main_0(args: Vec<String>) -> Result<(), failure::Error> {
println!("Error: Bad arguments, expected [db-name]."); println!("Error: Bad arguments, expected [db-name].");
return Err(format_err!("No db-name specified")); return Err(format_err!("No db-name specified"));
} }
let context = Context::new( let context = ContextBuilder::new(
Box::new(receive_event), Box::new(receive_event),
"CLI".into(), "CLI".into(),
Path::new(&args[1]).to_path_buf(), Path::new(&args[1]).to_path_buf(),
)?; )
.create()?;
println!("Delta Chat Core is awaiting your commands."); println!("Delta Chat Core is awaiting your commands.");

View File

@@ -39,8 +39,9 @@ fn main() {
let dir = tempdir().unwrap(); let dir = tempdir().unwrap();
let dbfile = dir.path().join("db.sqlite"); let dbfile = dir.path().join("db.sqlite");
println!("creating database {:?}", dbfile); println!("creating database {:?}", dbfile);
let ctx = let ctx = ContextBuilder::new(Box::new(cb), "FakeOs".into(), dbfile)
Context::new(Box::new(cb), "FakeOs".into(), dbfile).expect("Failed to create context"); .create()
.expect("Failed to create context");
let running = Arc::new(RwLock::new(true)); let running = Arc::new(RwLock::new(true));
let info = ctx.get_info(); let info = ctx.get_info();
let duration = time::Duration::from_millis(4000); let duration = time::Duration::from_millis(4000);

View File

@@ -94,30 +94,92 @@ pub fn get_info() -> HashMap<&'static str, String> {
res res
} }
impl Context { /// Builder for [Context].
/// Creates new context. ///
pub fn new(cb: Box<ContextCallback>, os_name: String, dbfile: PathBuf) -> Result<Context> { /// # Example
let mut blob_fname = OsString::new(); ///
blob_fname.push(dbfile.file_name().unwrap_or_default()); /// ```
blob_fname.push("-blobs"); /// use deltachat::context::ContextBuilder;
let blobdir = dbfile.with_file_name(blob_fname); /// let dir = tempfile::tempdir().unwrap();
if !blobdir.exists() { /// let dbfile = dir.path().join("my-context.db");
std::fs::create_dir_all(&blobdir)?; /// let ctx = ContextBuilder::new(Box::new(|_, _| 0), "AppName".into(), dbfile)
} /// .blobdir(dir.path().join("my-context-blobs"))
Context::with_blobdir(cb, os_name, dbfile, blobdir) /// .logdir(dir.path().join("my-context-logs"))
} /// .create()
/// .unwrap();
pub fn with_blobdir( /// assert_eq!(ctx.get_blobdir(), dir.path().join("my-context-blobs"));
/// ```
#[derive(DebugStub)]
pub struct ContextBuilder {
#[debug_stub = "Callback"]
cb: Box<ContextCallback>, cb: Box<ContextCallback>,
os_name: String, os_name: String,
dbfile: PathBuf, dbfile: PathBuf,
blobdir: PathBuf, blobdir: PathBuf,
logdir: PathBuf,
}
impl ContextBuilder {
pub fn new(cb: Box<ContextCallback>, os_name: String, dbfile: PathBuf) -> Self {
let db_fname = OsString::from(dbfile.file_name().unwrap_or(&OsString::from("dc-context")));
let mut blob_fname = db_fname.clone();
blob_fname.push("-blobs");
let mut log_fname = db_fname.clone();
log_fname.push("-logs");
ContextBuilder {
cb,
os_name,
blobdir: dbfile.with_file_name(blob_fname),
logdir: dbfile.with_file_name(log_fname),
dbfile,
}
}
pub fn blobdir(mut self, path: PathBuf) -> Self {
self.blobdir = path;
self
}
pub fn logdir(mut self, path: PathBuf) -> Self {
self.logdir = path;
self
}
pub fn create(self) -> Result<Context> {
if !self.blobdir.exists() {
std::fs::create_dir_all(&self.blobdir)?;
}
if !self.logdir.exists() {
std::fs::create_dir_all(&self.logdir)?;
}
Context::new(
self.cb,
self.os_name,
self.dbfile,
self.blobdir,
self.logdir,
)
}
}
impl Context {
fn new(
cb: Box<ContextCallback>,
os_name: String,
dbfile: PathBuf,
blobdir: PathBuf,
logdir: PathBuf,
) -> Result<Context> { ) -> Result<Context> {
ensure!( ensure!(
blobdir.is_dir(), blobdir.is_dir(),
"Blobdir does not exist: {}", "Blobdir does not exist: {}",
blobdir.display() blobdir.display()
); );
ensure!(
logdir.is_dir(),
"Logdir does not exist: {}",
logdir.display()
);
let ctx = Context { let ctx = Context {
blobdir, blobdir,
dbfile, dbfile,
@@ -526,7 +588,7 @@ mod tests {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite"); let dbfile = tmp.path().join("db.sqlite");
std::fs::write(&dbfile, b"123").unwrap(); std::fs::write(&dbfile, b"123").unwrap();
let res = Context::new(Box::new(|_, _| 0), "FakeOs".into(), dbfile); let res = ContextBuilder::new(Box::new(|_, _| 0), "FakeOs".into(), dbfile).create();
assert!(res.is_err()); assert!(res.is_err());
} }
@@ -541,7 +603,9 @@ mod tests {
fn test_blobdir_exists() { fn test_blobdir_exists() {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite"); let dbfile = tmp.path().join("db.sqlite");
Context::new(Box::new(|_, _| 0), "FakeOS".into(), dbfile).unwrap(); ContextBuilder::new(Box::new(|_, _| 0), "FakeOS".into(), dbfile)
.create()
.unwrap();
let blobdir = tmp.path().join("db.sqlite-blobs"); let blobdir = tmp.path().join("db.sqlite-blobs");
assert!(blobdir.is_dir()); assert!(blobdir.is_dir());
} }
@@ -552,7 +616,7 @@ mod tests {
let dbfile = tmp.path().join("db.sqlite"); let dbfile = tmp.path().join("db.sqlite");
let blobdir = tmp.path().join("db.sqlite-blobs"); let blobdir = tmp.path().join("db.sqlite-blobs");
std::fs::write(&blobdir, b"123").unwrap(); std::fs::write(&blobdir, b"123").unwrap();
let res = Context::new(Box::new(|_, _| 0), "FakeOS".into(), dbfile); let res = ContextBuilder::new(Box::new(|_, _| 0), "FakeOS".into(), dbfile).create();
assert!(res.is_err()); assert!(res.is_err());
} }
@@ -562,7 +626,9 @@ mod tests {
let subdir = tmp.path().join("subdir"); let subdir = tmp.path().join("subdir");
let dbfile = subdir.join("db.sqlite"); let dbfile = subdir.join("db.sqlite");
let dbfile2 = dbfile.clone(); let dbfile2 = dbfile.clone();
Context::new(Box::new(|_, _| 0), "FakeOS".into(), dbfile).unwrap(); ContextBuilder::new(Box::new(|_, _| 0), "FakeOS".into(), dbfile)
.create()
.unwrap();
assert!(subdir.is_dir()); assert!(subdir.is_dir());
assert!(dbfile2.is_file()); assert!(dbfile2.is_file());
} }
@@ -572,7 +638,9 @@ mod tests {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite"); let dbfile = tmp.path().join("db.sqlite");
let blobdir = PathBuf::new(); let blobdir = PathBuf::new();
let res = Context::with_blobdir(Box::new(|_, _| 0), "FakeOS".into(), dbfile, blobdir); let res = ContextBuilder::new(Box::new(|_, _| 0), "FakeOS".into(), dbfile)
.blobdir(blobdir)
.create();
assert!(res.is_err()); assert!(res.is_err());
} }
@@ -581,10 +649,36 @@ mod tests {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite"); let dbfile = tmp.path().join("db.sqlite");
let blobdir = tmp.path().join("blobs"); let blobdir = tmp.path().join("blobs");
let res = Context::with_blobdir(Box::new(|_, _| 0), "FakeOS".into(), dbfile, blobdir); ContextBuilder::new(Box::new(|_, _| 0), "FakeOS".into(), dbfile)
.blobdir(blobdir.clone())
.create()
.unwrap();
assert!(blobdir.is_dir());
}
#[test]
fn test_with_empty_logdir() {
let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite");
let logdir = PathBuf::new();
let res = ContextBuilder::new(Box::new(|_, _| 0), "FakeOS".into(), dbfile)
.logdir(logdir)
.create();
assert!(res.is_err()); assert!(res.is_err());
} }
#[test]
fn test_with_logdir_not_exists() {
let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite");
let logdir = tmp.path().join("logs");
ContextBuilder::new(Box::new(|_, _| 0), "FakeOS".into(), dbfile)
.logdir(logdir.clone())
.create()
.unwrap();
assert!(logdir.is_dir());
}
#[test] #[test]
fn no_crashes_on_context_deref() { fn no_crashes_on_context_deref() {
let t = dummy_context(); let t = dummy_context();

View File

@@ -7,7 +7,7 @@ use tempfile::{tempdir, TempDir};
use crate::config::Config; use crate::config::Config;
use crate::constants::KeyType; use crate::constants::KeyType;
use crate::context::{Context, ContextCallback}; use crate::context::{Context, ContextBuilder, ContextCallback};
use crate::events::Event; use crate::events::Event;
use crate::key; use crate::key;
@@ -33,7 +33,9 @@ pub fn test_context(callback: Option<Box<ContextCallback>>) -> TestContext {
Some(cb) => cb, Some(cb) => cb,
None => Box::new(|_, _| 0), None => Box::new(|_, _| 0),
}; };
let ctx = Context::new(cb, "FakeOs".into(), dbfile).unwrap(); let ctx = ContextBuilder::new(cb, "FakeOs".into(), dbfile)
.create()
.unwrap();
TestContext { ctx: ctx, dir: dir } TestContext { ctx: ctx, dir: dir }
} }

View File

@@ -219,7 +219,9 @@ struct TestContext {
fn create_test_context() -> TestContext { fn create_test_context() -> TestContext {
let dir = tempdir().unwrap(); let dir = tempdir().unwrap();
let dbfile = dir.path().join("db.sqlite"); let dbfile = dir.path().join("db.sqlite");
let ctx = Context::new(Box::new(cb), "FakeOs".into(), dbfile).unwrap(); let ctx = ContextBuilder::new(Box::new(cb), "FakeOs".into(), dbfile)
.create()
.unwrap();
TestContext { ctx, dir } TestContext { ctx, dir }
} }