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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
//! Index serving traits and implementations
//!
//! When uploading an image (a directory). First it's indexed with
//! [`dir-signature`] crate getting basically recursive directory list with
//! a hash for each file.
//!
//! Then the hashsum of that file is recorded as an `ImageId` and sent to
//! the peer. Subsequently peer asks for the index data (if it have no index
//! cached already).
//!
//! [`dir-signature`]: https://crates.io/crates/dir-signature
use std::collections::HashMap;
use std::fmt;
use std::io;
use std::sync::{RwLock, Arc};

use failure::Backtrace;
use dir_signature::get_hash;
use futures::{Future};
use futures::future::{FutureResult, ok, err};

pub use id::ImageId;


/// A trait to fulfill index data when uploading
pub trait GetIndex {
    /// An index data returned
    ///
    /// It's usually `Vec<u8>` but may also be an Arc'd container or a
    /// memory-mapped region.
    type Data: AsRef<[u8]>;
    /// Error returned by future
    ///
    /// This is used to print error and to send message to remote system
    type Error: fmt::Display;
    /// Future returned by `read_block`
    type Future: Future<Item=Self::Data, Error=Self::Error> + 'static;
    /// Read block by hash
    fn read_index(&self, id: &ImageId) -> Self::Future;
}

fn _object_safe() {
    use futures::future::FutureResult;
    let _: Option<&GetIndex<Data=Vec<u8>, Error=String,
                   Future=FutureResult<Vec<u8>, String>>> = None;
}

/// A GetIndex implementation that serves indexes from memory
///
/// Usually this is what you want because index is small enough, and client
/// finishes work as quick as image is served
#[derive(Debug, Clone)]
pub struct InMemoryIndexes {
    indexes: Arc<RwLock<HashMap<ImageId, Arc<[u8]>>>>,
}

/// Error adding index
#[derive(Debug, Fail)]
pub enum IndexError {
    // Error parsing index passed as index data
    // TODO(tailhook) this is doc(hidden) because `get_hash` returns io::Error
    #[doc(hidden)]
    #[fail(display="error parsing index")]
    ParseError,
    #[doc(hidden)]
    #[fail(display="index lock was poisoned")]
    LockError(Backtrace),
    #[doc(hidden)]
    #[fail(display="non-existent-error")]
    __Nonexhaustive,
}

/// Index not found error
#[derive(Debug, Fail)]
pub enum ReadError {
    /// Index not found
    #[fail(display="index {} with not found", _0)]
    NotFound(ImageId),
    #[doc(hidden)]
    #[fail(display="index lock was poisoned")]
    LockError(Backtrace),
    #[doc(hidden)]
    #[fail(display="non-existent-error")]
    __Nonexhaustive,
}

impl InMemoryIndexes {
    /// New in-memory index collection
    pub fn new() -> InMemoryIndexes {
        InMemoryIndexes {
            indexes: Arc::new(RwLock::new(HashMap::new())),
        }
    }
    /// Register index and return image id
    ///
    /// ImageId returned here for convenience and a bit microoptimization. You
    /// can get it from the image data on your own.
    pub fn register_index(&self, data: &[u8]) -> Result<ImageId, IndexError> {
        let image_id = ImageId::from(get_hash(&mut io::Cursor::new(&data))
            .map_err(|_| IndexError::ParseError)?);
        let mut indexes = self.indexes.write()
            .map_err(|_| IndexError::LockError(Backtrace::new()))?;
        indexes.insert(image_id.clone(), Arc::from(data));
        Ok(image_id)
    }
}

impl GetIndex for InMemoryIndexes {
    type Data = Arc<[u8]>;
    type Error = ReadError;
    /// Future returned by `read_block`
    type Future = FutureResult<Arc<[u8]>, ReadError>;
    /// Read block by hash
    fn read_index(&self, id: &ImageId) -> Self::Future {
        let data = match self.indexes.read() {
            Ok(data) => data,
            Err(_) => return err(ReadError::LockError(Backtrace::new())),
        };
        match data.get(id) {
            Some(data) => ok(data.clone()),
            None => err(ReadError::NotFound(id.clone())),
        }
    }
}