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
125
126
127
128
129
130
131
//! Defines "provider" traits and implementations for different types of I/O operations.
//!
//! The purpose of this is mainly for dependency injection: by having your code depend on a
//! generic provider, it can be tested by giving it a virtual, inspectable implementation of that
//! provider. In production, the "real" implementation can be used.
//!
//! Each type of provider exists in its own submodule and can be used independently. However,
//! this module also contains the all-encompassing `IoProvider` trait which provides access to
//! all of them. If you have a lot of I/O dependencies, it might be easier to create and pass
//! around one `&mut IoProvider` rather than several different providers.
//!
//! # Examples
//!
//! ```
//! extern crate io_providers;
//!
//! use std::io::Write;
//! use std::path::Path;
//! use io_providers::{IoProvider, LocalIoProvider, VirtualIoProvider};
//! use io_providers::env::Provider as EnvProvider;
//! use io_providers::stream::Provider as StreamProvider;
//!
//! /// Gets the current working directory and prints it to stdout.
//! fn do_work<P: IoProvider>(io: &mut P) {
//!     let cur_dir = io.env().current_dir().unwrap();
//!     let stdout = io.stream().output();
//!     writeln!(stdout, "The current directory is: {}", cur_dir.to_str().unwrap()).unwrap();
//! }
//!
//! fn main() {
//!     test_do_work_prints_current_dir();
//!
//!     // Use a local I/O provider here to get real interaction with the system
//!     let mut io = LocalIoProvider::new();
//!     do_work(&mut io);
//! }
//!
//! fn test_do_work_prints_current_dir() {
//!     // Use a virtual I/O provider here so we can verify how it was used
//!     let mut virtual_io = VirtualIoProvider::new();
//!     virtual_io.env().set_current_dir(Path::new("/foo/bar")).unwrap();
//!
//!     do_work(&mut virtual_io);
//!
//!     assert_eq!(
//!         "The current directory is: /foo/bar\n",
//!         ::std::str::from_utf8(virtual_io.stream().read_output()).unwrap());
//! }
//! ```

pub mod env;
pub mod stream;

/// Provides access to an environment provider and a stream provider.
///
/// See `env::Provider` and `stream::Provider` for more information.
pub trait IoProvider {
    // The type of the environment provider.
    type E: env::Provider;

    // The type of the stream provider.
    type S: stream::Provider;

    /// Gets the `env::Provider`.
    fn env<'a>(&'a mut self) -> &'a mut Self::E;

    /// Gets the `stream::Provider`.
    fn stream<'a>(&'a mut self) -> &'a mut Self::S;
}

/// "Real" implementer of `IoProvider`, using standard streams and the local environment.
///
/// See `env::Local` and `stream::Std` for more information.
pub struct LocalIoProvider {
    env: env::Local,
    stream: stream::Std,
}

impl LocalIoProvider {
    /// Creates a new `LocalIoProvider`.
    pub fn new() -> LocalIoProvider {
        LocalIoProvider {
            env: env::Local,
            stream: stream::Std::new(),
        }
    }
}

impl IoProvider for LocalIoProvider {
    type E = env::Local;
    type S = stream::Std;

    fn env<'a>(&'a mut self) -> &'a mut env::Local {
        &mut self.env
    }

    fn stream<'a>(&'a mut self) -> &'a mut stream::Std {
        &mut self.stream
    }
}

/// Virtual implementer of `IoProvider`, using in-memory data which can be inspected.
///
/// See `env::Virtual` and `stream::Virtual` for more information.
pub struct VirtualIoProvider {
    env: env::Virtual,
    stream: stream::Virtual,
}

impl VirtualIoProvider {
    /// Creates a new `VirtualIoProvider`.
    pub fn new() -> VirtualIoProvider {
        VirtualIoProvider {
            env: env::Virtual::new(),
            stream: stream::Virtual::new(),
        }
    }
}

impl IoProvider for VirtualIoProvider {
    type E = env::Virtual;
    type S = stream::Virtual;

    fn env<'a>(&'a mut self) -> &'a mut env::Virtual {
        &mut self.env
    }

    fn stream<'a>(&'a mut self) -> &'a mut stream::Virtual {
        &mut self.stream
    }
}