Factor replica and sync configuration into simple owned structs

This commit is contained in:
Dustin J. Mitchell 2020-11-28 16:57:32 -05:00
parent 87596bb1f0
commit 8af7ba286d
16 changed files with 81 additions and 23 deletions

View file

@ -38,7 +38,7 @@ define_subcommand! {
subcommand_invocation! {
fn run(&self, command: &CommandInvocation) -> Fallible<()> {
let t = command
.get_replica()
.get_replica()?
.new_task(Status::Pending, self.description.clone())
.unwrap();
println!("added task {}", t.get_uuid());

View file

@ -20,7 +20,7 @@ define_subcommand! {
subcommand_invocation! {
fn run(&self, command: &CommandInvocation) -> Fallible<()> {
command.get_replica().gc()?;
command.get_replica()?.gc()?;
println!("garbage collected.");
Ok(())
}

View file

@ -30,7 +30,7 @@ define_subcommand! {
subcommand_invocation! {
fn run(&self, command: &CommandInvocation) -> Fallible<()> {
let mut replica = command.get_replica();
let mut replica = command.get_replica()?;
let task = shared::get_task(&mut replica, &self.task)?;
let uuid = task.get_uuid();

View file

@ -23,7 +23,7 @@ define_subcommand! {
subcommand_invocation! {
fn run(&self, command: &CommandInvocation) -> Fallible<()> {
let mut replica = command.get_replica();
let mut replica = command.get_replica()?;
let mut t = Table::new();
t.set_format(table::format());
t.set_titles(row![b->"id", b->"description"]);

View file

@ -36,7 +36,7 @@ define_subcommand! {
subcommand_invocation! {
fn run(&self, command: &CommandInvocation) -> Fallible<()> {
let mut replica = command.get_replica();
let mut replica = command.get_replica()?;
let task = shared::get_task(&mut replica, &self.task)?;
let mut task = task.into_mut(&mut replica);

View file

@ -25,7 +25,7 @@ define_subcommand! {
subcommand_invocation! {
fn run(&self, command: &CommandInvocation) -> Fallible<()> {
let working_set = command.get_replica().working_set().unwrap();
let working_set = command.get_replica()?.working_set().unwrap();
let mut t = Table::new();
t.set_format(table::format());
t.set_titles(row![b->"id", b->"description"]);

View file

@ -2,7 +2,7 @@ use clap::Arg;
use failure::{format_err, Fallible};
use std::env;
use std::ffi::OsString;
use taskchampion::{server, taskstorage, Replica, Task, Uuid};
use taskchampion::{server, Replica, ReplicaConfig, ServerConfig, Task, Uuid};
pub(super) fn task_arg<'a>() -> Arg<'a, 'a> {
Arg::with_name("task")
@ -46,20 +46,29 @@ impl CommandInvocation {
// -- utilities for command invocations
pub(super) fn get_replica(&self) -> Replica {
pub(super) fn get_replica(&self) -> Fallible<Replica> {
// temporarily use $TASK_DB to locate the taskdb
let taskdb_dir = env::var_os("TASK_DB").unwrap_or_else(|| OsString::from("/tmp/tasks"));
Replica::new(Box::new(taskstorage::KVStorage::new(taskdb_dir).unwrap()))
let replica_config = ReplicaConfig {
taskdb_dir: taskdb_dir.into(),
};
Ok(Replica::from_config(replica_config)?)
}
pub(super) fn get_server(&self) -> Fallible<impl server::Server> {
pub(super) fn get_server(&self) -> Fallible<Box<dyn server::Server>> {
// temporarily use $SYNC_SERVER_ORIGIN for the sync server
let sync_server_origin = env::var_os("SYNC_SERVER_ORIGIN")
let origin = env::var_os("SYNC_SERVER_ORIGIN")
.map(|osstr| osstr.into_string().unwrap())
.unwrap_or_else(|| String::from("http://localhost:8080"));
Ok(server::RemoteServer::new(
sync_server_origin,
Uuid::parse_str("d5b55cbd-9a82-4860-9a39-41b67893b22f").unwrap(),
))
let client_id = env::var_os("SYNC_SERVER_CLIENT_ID")
.ok_or_else(|| format_err!("SYNC_SERVER_CLIENT_ID not set"))?;
let client_id = client_id
.into_string()
.map_err(|_| format_err!("SYNC_SERVER_CLIENT_ID is not a valid UUID"))?;
let client_id = Uuid::parse_str(&client_id)?;
Ok(server::from_config(ServerConfig::Remote {
origin,
client_id,
})?)
}
}

View file

@ -21,7 +21,7 @@ define_subcommand! {
subcommand_invocation! {
fn run(&self, command: &CommandInvocation) -> Fallible<()> {
let mut replica = command.get_replica();
let mut replica = command.get_replica()?;
let mut server = command.get_server()?;
replica.sync(&mut server)?;
Ok(())

View file

@ -12,6 +12,7 @@ Note that the `task` interface does not match that of TaskWarrior.
Temporarily, configuration is by environment variables.
The directory containing the replica's task data is given by `TASK_DB`, defaulting to `/tmp/tasks`.
the origin of the sync server is given by `SYNC_SERVER_ORIGIN`, defaulting to `http://localhost:8080`.
The client ID to use with the sync server is givne by `SYNC_SERVER_CLIENT_ID` (with no default).
## `taskchampion-sync-server`

View file

@ -0,0 +1,26 @@
use std::path::PathBuf;
use uuid::Uuid;
/// The configuration required for a replica. Use with [`crate::Replica::from_config`].
pub struct ReplicaConfig {
/// Path containing the task DB.
pub taskdb_dir: PathBuf,
}
/// The configuration for a replica's access to a sync server. Use with
/// [`crate::server::from_config`].
pub enum ServerConfig {
/// A local task database, for situations with a single replica.
Local {
/// Path containing the server's DB
server_dir: PathBuf,
},
/// A remote taskchampion-sync-server instance
Remote {
/// Sync server "origin"; a URL with schema and hostname but no path or trailing `/`
origin: String,
/// Client ID to identify this replica to the server
client_id: Uuid,
},
}

View file

@ -23,6 +23,7 @@ for more information about the design and usage of the tool.
*/
mod config;
mod errors;
mod replica;
pub mod server;
@ -31,6 +32,7 @@ mod taskdb;
pub mod taskstorage;
mod utils;
pub use config::{ReplicaConfig, ServerConfig};
pub use replica::Replica;
pub use task::Priority;
pub use task::Status;

View file

@ -1,8 +1,9 @@
use crate::config::ReplicaConfig;
use crate::errors::Error;
use crate::server::Server;
use crate::task::{Status, Task};
use crate::taskdb::TaskDB;
use crate::taskstorage::{Operation, TaskMap, TaskStorage};
use crate::taskstorage::{KVStorage, Operation, TaskMap, TaskStorage};
use chrono::Utc;
use failure::Fallible;
use std::collections::HashMap;
@ -21,6 +22,11 @@ impl Replica {
}
}
pub fn from_config(config: ReplicaConfig) -> Fallible<Replica> {
let storage = Box::new(KVStorage::new(config.taskdb_dir)?);
Ok(Replica::new(storage))
}
#[cfg(test)]
pub fn new_inmemory() -> Replica {
Replica::new(Box::new(crate::taskstorage::InMemoryStorage::new()))
@ -140,7 +146,7 @@ impl Replica {
}
/// Synchronize this replica against the given server.
pub fn sync(&mut self, server: &mut dyn Server) -> Fallible<()> {
pub fn sync(&mut self, server: &mut Box<dyn Server>) -> Fallible<()> {
self.taskdb.sync(server)
}

View file

@ -25,7 +25,7 @@ pub struct LocalServer<'t> {
impl<'t> LocalServer<'t> {
/// A test server has no notion of clients, signatures, encryption, etc.
pub fn new(directory: &Path) -> Fallible<LocalServer> {
pub fn new<P: AsRef<Path>>(directory: P) -> Fallible<LocalServer<'t>> {
let mut config = Config::default(directory);
config.bucket("versions", None);
config.bucket("numbers", None);

View file

@ -1,3 +1,6 @@
use crate::ServerConfig;
use failure::Fallible;
#[cfg(test)]
pub(crate) mod test;
@ -8,3 +11,12 @@ mod types;
pub use local::LocalServer;
pub use remote::RemoteServer;
pub use types::*;
pub fn from_config(config: ServerConfig) -> Fallible<Box<dyn Server>> {
Ok(match config {
ServerConfig::Local { server_dir } => Box::new(LocalServer::new(server_dir)?),
ServerConfig::Remote { origin, client_id } => {
Box::new(RemoteServer::new(origin, client_id))
}
})
}

View file

@ -164,7 +164,7 @@ impl TaskDB {
}
/// Sync to the given server, pulling remote changes and pushing local changes.
pub fn sync(&mut self, server: &mut dyn Server) -> Fallible<()> {
pub fn sync(&mut self, server: &mut Box<dyn Server>) -> Fallible<()> {
let mut txn = self.storage.txn()?;
// retry synchronizing until the server accepts our version (this allows for races between
@ -542,7 +542,7 @@ mod tests {
#[test]
fn test_sync() {
let mut server = TestServer::new();
let mut server: Box<dyn Server> = Box::new(TestServer::new());
let mut db1 = newdb();
db1.sync(&mut server).unwrap();
@ -602,7 +602,7 @@ mod tests {
#[test]
fn test_sync_create_delete() {
let mut server = TestServer::new();
let mut server: Box<dyn Server> = Box::new(TestServer::new());
let mut db1 = newdb();
db1.sync(&mut server).unwrap();
@ -692,7 +692,7 @@ mod tests {
// and delete operations that results in a task existing in one TaskDB but not existing in
// another. So, the generated sequences focus on a single task UUID.
fn transform_sequences_of_operations(action_sequence in action_sequence_strategy()) {
let mut server = TestServer::new();
let mut server: Box<dyn Server> = Box::new(TestServer::new());
let mut dbs = [newdb(), newdb(), newdb()];
for (action, db) in action_sequence {

View file

@ -2,11 +2,13 @@ use failure::Fallible;
use std::collections::HashMap;
use uuid::Uuid;
#[cfg(test)]
mod inmemory;
mod kv;
mod operation;
pub use self::kv::KVStorage;
#[cfg(test)]
pub use inmemory::InMemoryStorage;
pub use operation::Operation;