1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
//! An engine for keeping remote siles synchronized.
//!
//! The engine is based on the Admissabilty Based Sequence Transformation algorithm (doi: [10.1109/TPDS.2010.64](http://dx.doi.org.ezproxy.library.dal.ca/10.1109/TPDS.2010.64))
//! The idea is that each site that wants to have a file synchronized will have an instance of [`Engine`](engine/struct.Engine.html) running.
//! Any changes that are made to teh file should be run through the engine using either `process_diffs()` or `process_transaction()` prior to being broadcast.
//! Any changes that are made at a remote site should be run through the engine using `integrate_remote()` prior to being applied to the file.
//!
//! This crate generally works well with [`rdiff`](https://crates.io/crates/rdiff), but can work with
//! any system that generates difference operations that are limited to insert and delete.
//!
//!# Examples
//!
//! Assuming you have a method `read_transaction` for reading transactions from remote sites,
//! the process for integrating remote changes onto a local file go like this:
//!
//! ```no_run
//! use optra::{Engine, TransactionSequence, TimeStamper};
//! use std::collections::{LinkedList, BTreeMap};
//! use std::fs::OpenOptions;
//!# fn read_transaction() -> (TransactionSequence, BTreeMap<u32, (u32, u32)>) {
//!#       (TransactionSequence::new(None, LinkedList::new(), LinkedList::new()), BTreeMap::new())
//!# }
//!#
//!# let mut engine = Engine::new(1);
//!# let mut time_stamper = TimeStamper::new();
//! // Assume we already have an engine up and running
//! let (mut remote_sequence, remote_lookup) = read_transaction();
//! engine.integrate_remote(&mut remote_sequence, &remote_lookup, &mut time_stamper);
//! let mut file = OpenOptions::new()
//!                .read(true)
//!                .write(true)
//!                .open("local_file")
//!                .unwrap();
//! remote_sequence.apply(&mut file).unwrap();
//! ```
//!
//! On the other hand, if you have a Diff generated by detecting differences to a local file,
//! you can process the diff and then send it out (assuming you have a method called `send_transaction()`)
//!
//! In order to ensure that your tranaction has appropriate timestamps, you must have a [`TimeStamper`](engine/strict.TimeStamper.html)
//! to keep track of the sequencing of operations.
//!
//! ```no_run
//!# extern crate rdiff;
//!# extern crate optra;
//!# use optra::{Engine, TransactionSequence, TimeStamper};
//!# use rdiff::BlockHashes;
//!# use std::collections::{LinkedList, BTreeMap};
//!# use std::fs::File;
//!# fn main() {
//!# fn send_transaction(_seq: TransactionSequence, _lookup: BTreeMap<u32, (u32, u32)>) {
//!#
//!# }
//!#
//!# let mut engine = Engine::new(1);
//!# let mut file_hashes = BlockHashes::new(File::open("local_file").unwrap(), 8).unwrap();
//!# let mut time_stamper = TimeStamper::new();
//! let diffs = file_hashes.diff_and_update(File::open("local_file").unwrap()).unwrap();
//! let (transaction, lookup) = engine.process_diffs(diffs, &mut time_stamper);
//! send_transaction(transaction, lookup);
//!# }
//! ```
#![feature(linked_list_extras)]
#![deny(missing_docs)]
#[macro_use]
extern crate log;
extern crate rdiff;
extern crate byteorder;

mod operations;
mod utils;
mod engine;

pub use operations::{InsertOperation, DeleteOperation, Operation};

pub use engine::{Engine, TransactionSequence, TimeStamper};

type Offset = i64;
type Position = u64;

/// Represents an error in attempting to synchronize remote operations
#[derive(Debug)]
pub struct OTError {
    /// The kind of error this is
    pub kind: ErrorKind
}

/// Represents the kind of error we encountered synchronizing operations
#[derive(Debug)]
pub enum ErrorKind {
    /// The remote operations refer to a state that we have not yet recieved
    NoSuchState
}


impl OTError {
    /// Create a new OTError of the given kind.
    #[inline]
    pub fn new(kind: ErrorKind) -> OTError {
        OTError {
            kind: kind
        }
    }
}