pijul_org / pijul

Cleaning up the error modules

By blabla on November 15, 2018
This patch is not signed.
B86TKtcSSyUB4hnrh643FkkWCpvVKTkfnExj8tn9STZfn2mH4jpUYJnzeJ5iwpxi3xom2UmLyz5so3G2MptK3NCX
This patch is in the following branches:
latest
master
testing






















































































































































extern crate base64;

use std::io;
use std;
use std::fmt;
use std::path::PathBuf;
use bincode;
use sanakirja;
use backend;
use ring;

#[derive(Debug)]
pub enum Error {
    IO(io::Error),
    Sanakirja(sanakirja::Error),
    AlreadyApplied,
    AlreadyAdded,
    FileNotInRepo(PathBuf),
    Bincode(bincode::Error),
    NothingToDecode,
    InternalHashNotFound(backend::Hash),
    PatchNotFound(PathBuf, backend::Hash),
    Utf8(std::str::Utf8Error),
    NoDb(backend::Root),
    ReadOnlyTransaction,
    WrongHash,
    WrongPatchSignature,
    BranchNameAlreadyExists,
    ChangesFile,
    PatchVersionMismatch(u64, u64),
    Ring(ring::error::Unspecified)
}

impl Error {
    pub fn lacks_space(&self) -> bool {
        match *self {
            Error::Sanakirja(sanakirja::Error::NotEnoughSpace) => true,
            _ => false
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::IO(ref err) => write!(f, "IO error: {}", err),
            Error::Sanakirja(ref err) => write!(f, "Sanakirja error: {}", err),
            Error::AlreadyApplied => write!(f, "Patch already applied"),
            Error::AlreadyAdded => write!(f, "File already here"),
            Error::Bincode(ref err) => write!(f, "Bincode error {}", err),
            Error::NothingToDecode => write!(f, "Nothing to decode"),
            Error::FileNotInRepo(ref path) => write!(f, "File {} not tracked", path.display()),
            Error::InternalHashNotFound(ref hash) => {
                write!(f, "Internal hash {:?} not found", hash.as_ref())
            }
            Error::PatchNotFound(ref path, ref hash) => {
                write!(f, "Patch {} not found in {}",
                       hash.to_base64(base64::STANDARD),
                       path.display())
            }
            Error::Utf8(ref e) => write!(f, "Utf8 Error {:?}", e),
            Error::NoDb(ref e) => write!(f, "No root database {:?}", e),
            Error::ReadOnlyTransaction => write!(f, "Read-only transaction"),
            Error::WrongHash => write!(f, "Wrong patch hash"),
            Error::WrongPatchSignature => write!(f, "Wrong patch signature"),
            Error::BranchNameAlreadyExists => write!(f, "Branch name already exists"),
            Error::ChangesFile => write!(f, "Invalid changes file"),
            Error::PatchVersionMismatch(a, b) => write!(f, "Patch version mismatch: this Pijul knows version {}, the patch is version {}", b, a),
            Error::Ring(ref e) => write!(f, "Ring error {:?}", e),
        }
    }
}

impl std::error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::IO(ref err) => err.description(),
            Error::Sanakirja(ref err) => err.description(),
            Error::AlreadyApplied => "Patch already applied",
            Error::AlreadyAdded => "File already here",
            Error::Bincode(ref err) => err.description(),
            Error::NothingToDecode => "Nothing to decode",
            Error::FileNotInRepo(_) => "Operation on untracked file",
            Error::InternalHashNotFound(_) => "Internal hash not found",
            Error::PatchNotFound(_, _) => "Patch not found",
            Error::Utf8(ref e) => e.description(),
            Error::NoDb(_) => "No root database",
            Error::ReadOnlyTransaction => "Read-only transaction",
            Error::WrongHash => "Wrong patch hash",
            Error::WrongPatchSignature => "Wrong patch signature",
            Error::BranchNameAlreadyExists => "Branch name already exists",
            Error::ChangesFile => "Invalid changes file",
            Error::PatchVersionMismatch(_, _) => "Patch version mismatch",
            Error::Ring(ref e) => e.description(),
        }
    }

    fn cause(&self) -> Option<&std::error::Error> {
        match *self {
            Error::IO(ref err) => Some(err),
            Error::Sanakirja(ref err) => Some(err),
            Error::AlreadyApplied => None,
            Error::AlreadyAdded => None,
            Error::Bincode(ref err) => Some(err),
            Error::NothingToDecode => None,
            Error::FileNotInRepo(_) => None,
            Error::InternalHashNotFound(_) => None,
            Error::PatchNotFound(_, _) => None,
            Error::Utf8(ref e) => Some(e),
            Error::NoDb(_) => None,
            Error::ReadOnlyTransaction => None,
            Error::WrongHash => None,
            Error::WrongPatchSignature => None,
            Error::BranchNameAlreadyExists => None,
            Error::ChangesFile => None,
            Error::PatchVersionMismatch(_, _) => None,
            Error::Ring(ref e) => Some(e),
        }
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::IO(err)
    }
}

impl From<sanakirja::Error> for Error {
    fn from(err: sanakirja::Error) -> Error {
        Error::Sanakirja(err)
    }
}

impl From<bincode::Error> for Error {
    fn from(err: bincode::Error) -> Error {
        Error::Bincode(err)
    }
}

impl From<std::str::Utf8Error> for Error {
    fn from(err: std::str::Utf8Error) -> Error {
        Error::Utf8(err)
    }
}

impl From<ring::error::Unspecified> for Error {
    fn from(err: ring::error::Unspecified) -> Error {
        Error::Ring(err)
    }
}




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
132
133
134
135
136
137
138

139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
            Error::Keys(ref e) => e.fmt(fmt),
            Error::BranchNameAlreadyExists(ref name) => write!(fmt, "Branch {:?} already exists", name),
            Error::WrongFileHeader(ref h) => write!(fmt, "Wrong file header (possible branch corruption): {:?}", h),
            Error::FileNameCount(ref f) => write!(fmt, "Name {:?} doesn't have exactly one child", f),
//! This crate contains the core API to access Pijul repositories.
//!
//! The key object is a `Repository`, on which `Txn` (immutable
//! transactions) and `MutTxn` (mutable transactions) can be started,
//! to perform a variety of operations.
//!
//! Another important object is a `Patch`, which encodes two different pieces of information:
//!
//! - Information about deleted and inserted lines between two versions of a file.
//!
//! - Information about file moves, additions and deletions.
//!
//! The standard layout of a repository is defined in module
//! `fs_representation`, and mainly consists of a directory called
//! `.pijul` at the root of the repository, containing:
//!
//! - a directory called `pristine`, containing a Sanakirja database
//! storing most of the repository information.
//!
//! - a directory called `patches`, actually containing the patches,
//! where each patch is a gzipped compression of the bincode encoding
//! of the `patch::Patch` type.
//!
//! At the moment, users of this library, such as the Pijul
//! command-line tool, may use other files in the `.pijul` directory,
//! such as user preferences, or information about remote branches and
//! repositories.
#![recursion_limit = "128"]
#[macro_use]
extern crate bitflags;
extern crate chrono;
#[macro_use]
extern crate log;

extern crate base64;
extern crate bincode;
extern crate bs58;
extern crate byteorder;
extern crate flate2;
extern crate hex;
extern crate ignore;
extern crate openssl;
extern crate rand;
extern crate sanakirja;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate diffs;
extern crate failure;
extern crate sequoia_openpgp;
extern crate serde_json;
extern crate tempdir;
extern crate toml;

pub use sanakirja::Transaction;
use std::collections::HashSet;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;

#[derive(Debug)]
pub enum Error {
    IO(std::io::Error),
    Sanakirja(sanakirja::Error),
    Bincode(bincode::Error),
    Utf8(std::str::Utf8Error),
    Serde(serde_json::Error),
    OpenSSL(openssl::error::Error),
    OpenSSLStack(openssl::error::ErrorStack),
    Base58Decode(bs58::decode::DecodeError),
    Failure(failure::Error),
    AlreadyAdded,
    FileNotInRepo(PathBuf),
    NoDb(backend::Root),
    WrongHash,
    EOF,
    WrongPatchSignature,
    BranchNameAlreadyExists(String),
    WrongFileHeader(Key<PatchId>),
    FileNameCount(Key<PatchId>),
    MissingDependency(Hash),
    PatchNotOnBranch(PatchId),
    CannotAddDotPijul,
    KeyIsEncrypted,
}

impl std::convert::From<std::io::Error> for Error {
    fn from(e: std::io::Error) -> Self {
        Error::IO(e)
    }
}

impl std::convert::From<failure::Error> for Error {
    fn from(e: failure::Error) -> Self {
        Error::Failure(e)
    }
}

impl std::convert::From<sanakirja::Error> for Error {
    fn from(e: sanakirja::Error) -> Self {
        Error::Sanakirja(e)
    }
}

impl std::convert::From<bincode::Error> for Error {
    fn from(e: bincode::Error) -> Self {
        Error::Bincode(e)
    }
}

impl std::convert::From<serde_json::Error> for Error {
    fn from(e: serde_json::Error) -> Self {
        Error::Serde(e)
    }
}

impl std::convert::From<std::str::Utf8Error> for Error {
    fn from(e: std::str::Utf8Error) -> Self {
        Error::Utf8(e)
    }
}

impl std::convert::From<openssl::error::ErrorStack> for Error {
    fn from(e: openssl::error::ErrorStack) -> Self {
        Error::OpenSSLStack(e)
    }
}

impl std::convert::From<bs58::decode::DecodeError> for Error {
    fn from(e: bs58::decode::DecodeError) -> Self {
        Error::Base58Decode(e)
    }
}

pub type Result<A> = std::result::Result<A, Error>;

impl std::fmt::Display for Error {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(fmt, "{:?}", self)
        match *self {
            Error::IO(ref e) => e.fmt(fmt),
            Error::Sanakirja(ref e) => e.fmt(fmt),
            Error::Bincode(ref e) => e.fmt(fmt),
            Error::Utf8(ref e) => e.fmt(fmt),
            Error::Serde(ref e) => e.fmt(fmt),
            Error::OpenSSL(ref e) => e.fmt(fmt),
            Error::OpenSSLStack(ref e) => e.fmt(fmt),
            Error::Base58Decode(ref e) => e.fmt(fmt),
            Error::Failure(ref e) => e.fmt(fmt),
            Error::AlreadyAdded => write!(fmt, "Already added"),
            Error::FileNotInRepo(ref file) => write!(fmt, "File {:?} not tracked", file),
            Error::NoDb(ref e) => write!(fmt, "Table missing: {:?}", e),
            Error::WrongHash => write!(fmt, "Wrong hash"),
            Error::EOF => write!(fmt, "EOF"),
            Error::WrongPatchSignature => write!(fmt, "Wrong patch signature"),
            Error::BranchNameAlreadyExists(ref name) => {
                write!(fmt, "Branch {:?} already exists", name)
            }
            Error::WrongFileHeader(ref h) => write!(
                fmt,
                "Wrong file header (possible branch corruption): {:?}",
                h
            ),
            Error::FileNameCount(ref f) => {
                write!(fmt, "Name {:?} doesn't have exactly one child", f)
            }
            Error::MissingDependency(ref f) => write!(fmt, "Missing dependency: {:?}", f),
            Error::PatchNotOnBranch(ref f) => {
                write!(fmt, "The patch is not on this branch {:?}", f)
            }
            Error::CannotAddDotPijul => write!(fmt, "Cannot add a file or directory name .pijul"),
            Error::KeyIsEncrypted => write!(fmt, "Key is encrypted"),
        }
    }
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171

172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310


























































































































































































































































































use std;
use std::path::PathBuf;
#[cfg(unix)]
use {hex, libpijul, regex, reqwest, term, thrussh, thrussh_config, thrussh_keys, toml};
#[cfg(windows)]
#[cfg(windows)]
#[cfg(windows)]
#[cfg(windows)]
use {hex, libpijul, regex, reqwest, term, thrussh, thrussh_config, thrussh_keys, toml};

#[derive(Debug)]
pub enum Error {
    IO(std::io::Error),
    Term(term::Error),
    Repository(libpijul::Error),
    UTF8(std::string::FromUtf8Error),
    Hex(hex::FromHexError),
    SSH(thrussh::Error),
    SSHKeys(thrussh_keys::Error),
    Reqwest(reqwest::Error),
    TomlDe(toml::de::Error),
    TomlSer(toml::ser::Error),
    StripPrefix(std::path::StripPrefixError),
    Regex(regex::Error),
    ThrusshConfig(thrussh_config::Error),
    Failure(failure::Error),
    HookFailed {
        cmd: String,
    },
    InARepository {
        path: std::path::PathBuf,
    },
    NotInARepository,
    MissingRemoteRepository,
    InvalidPath {
        path: PathBuf,
    },
    FileNotInRepository {
        path: String,
    },
    WrongHash,
    BranchAlreadyExists,
    CannotDeleteCurrentBranch,
    NoSuchBranch,
    IsDirectory,
    CannotParseRemote,
    WillNotOverwriteKeyFile {
        path: std::path::PathBuf,
    },
    BranchDoesNotHavePatch {
        branch_name: String,
        patch: libpijul::Hash,
    },
    PatchNotFound {
        repo_root: String,
        patch_hash: libpijul::Hash,
    },
    PatchIsDependedUpon {
        hash: libpijul::Hash,
        dependent: libpijul::Hash,
    },
    SshKeyNotFound {
        path: PathBuf,
    },
    NoHomeDir,
    ExtraDepNotOnBranch {
        hash: libpijul::Hash,
    },
    PendingChanges,
    EmptyPatchName,
    InvalidPatchName,
    CannotSpawnEditor {
        editor: String,
        cause: String,
    },
    InvalidDate {
        date: String,
    },
    PartialPullOverHttp,
    UnknownHost {
        host: String,
    },
    NoAuthor,
    NonFastForwardPush,
    NotSigningAuthor,
    EmptyPassword,
}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match *self {
            Error::IO(ref e) => e.fmt(f),
            Error::Term(ref e) => e.fmt(f),
            Error::Repository(ref e) => e.fmt(f),
            Error::UTF8(ref e) => e.fmt(f),
            Error::Hex(ref e) => e.fmt(f),
            Error::SSH(ref e) => e.fmt(f),
            Error::SSHKeys(ref e) => e.fmt(f),
            Error::Reqwest(ref e) => e.fmt(f),
            Error::TomlDe(ref e) => e.fmt(f),
            Error::TomlSer(ref e) => e.fmt(f),
            Error::StripPrefix(ref e) => e.fmt(f),
            Error::Regex(ref e) => e.fmt(f),
            Error::ThrusshConfig(ref e) => e.fmt(f),
            Error::Failure(ref e) => e.fmt(f),
            Error::HookFailed { ref cmd } => write!(f, "Hook failed: {}", cmd),
            Error::InARepository { ref path } => write!(f, "In a repository: {:?}", path),
            Error::NotInARepository => write!(f, "Not in a repository"),
            Error::MissingRemoteRepository => write!(f, "Missing remote repository"),
            Error::InvalidPath { ref path } => write!(f, "Invalid path: {:?}", path),
            Error::FileNotInRepository { ref path } => write!(f, "File not in repository: {:?}", path),
            Error::WrongHash => write!(f, "Wrong hash"),
            Error::BranchAlreadyExists => write!(f, "Branch already exists"),
            Error::CannotDeleteCurrentBranch => write!(f, "Cannot delete current branch"),
            Error::NoSuchBranch => write!(f, "No such branch"),
            Error::IsDirectory => write!(f, "Is a directory"),
            Error::CannotParseRemote => write!(f, "Cannot parse remote address"),
            Error::WillNotOverwriteKeyFile { ref path } => write!(f, "Will not overwrite key file {:?}", path),
            Error::BranchDoesNotHavePatch { ref branch_name, ref patch } => write!(f, "Branch {:?} does not have patch {}", branch_name, patch.to_base58()),
            Error::PatchNotFound { ref repo_root, ref patch_hash } => write!(f, "Patch {} not found in repository {:?}", patch_hash.to_base58(), repo_root),
            Error::PatchIsDependedUpon { ref dependent, ref hash } => write!(f, "Patch {} depends on {:?}", dependent.to_base58(), hash.to_base58()),
            Error::SshKeyNotFound { ref path } => write!(f, "SSH key not found in: {:?}", path),
            Error::NoHomeDir => write!(f, "No home dir"),
            Error::ExtraDepNotOnBranch { ref hash } => write!(f, "Extra dependencies can only be added if they are on the same branch as the current record: {:?}", hash),
            Error::PendingChanges => write!(f, "There are pending changes in the repository."),
            Error::EmptyPatchName => write!(f, "Empty patch name"),
            Error::CannotSpawnEditor { ref editor, ref cause } => write!(f, "Cannot spawn editor {:?} (error: {:?})", editor, cause),
            Error::InvalidPatchName => write!(f, "Invalid patch name"),
            Error::CannotSpawnEditor { ref editor, ref cause } => write!(f, "Cannot start editor {:?} ({:?})", editor, cause),
            Error::InvalidDate { ref date } => write!(f, "Invalid date: {:?}", date),
            Error::PartialPullOverHttp => write!(f, "Partial pull over HTTP is not (yet) supported"),
            Error::UnknownHost { ref host } => write!(f, "Unknown host: {}", host),
            Error::NoAuthor => write!(f, "No authors were given"),
            Error::NonFastForwardPush => write!(f, "This push is not fast-forward. If this is really what you mean, use --force."),
            Error::NotSigningAuthor => write!(f, "The user id for the signing key doesn't match the author. Authors must be of the form `rms@xyzcorp.com` or `Robert M. Smith <rms@xyzcorp.com>`"),
            Error::EmptyPassword => write!(f, "Empty password"),
        }
    }
}

impl std::error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::IO(ref e) => e.description(),
            Error::Term(ref e) => e.description(),
            Error::Repository(ref e) => e.description(),
            Error::UTF8(ref e) => e.description(),
            Error::Hex(ref e) => e.description(),
            Error::SSH(ref e) => e.description(),
            Error::SSHKeys(ref e) => e.description(),
            Error::Reqwest(ref e) => e.description(),
            Error::TomlDe(ref e) => e.description(),
            Error::TomlSer(ref e) => e.description(),
            Error::StripPrefix(ref e) => e.description(),
            Error::Regex(ref e) => e.description(),
            Error::ThrusshConfig(ref e) => e.description(),
            Error::Failure(ref e) => e.name().unwrap_or("Unknown failure"),
            Error::HookFailed { .. } => "Hook failed",
            Error::InARepository { .. } => "In a repository",
            Error::NotInARepository => "Not in a repository",
            Error::MissingRemoteRepository => "Missing remote repository",
            Error::InvalidPath { .. } => "Invalid path",
            Error::FileNotInRepository { .. } => "File not in repository",
            Error::WrongHash => "Wrong hash",
            Error::BranchAlreadyExists => "Branch already exists",
            Error::CannotDeleteCurrentBranch => "Cannot delete current branch",
            Error::NoSuchBranch => "No such branch",
            Error::IsDirectory => "Is a directory",
            Error::CannotParseRemote => "Cannot parse remote address",
            Error::WillNotOverwriteKeyFile { .. } => "Will not overwrite key file",
            Error::BranchDoesNotHavePatch { .. } => "Branch does not have patch",
            Error::PatchNotFound { .. } => "Patch not found in repository",
            Error::PatchIsDependedUpon { .. } => "Patch depended upon",
            Error::SshKeyNotFound { .. } => "SSH key not found",
            Error::NoHomeDir => "No home dir",
            Error::ExtraDepNotOnBranch { .. } => "Extra dependencies can only be added if they are on the same branch as the current record",
            Error::PendingChanges => "There are pending changes in the repository.",
            Error::EmptyPatchName => "Empty patch name",
            Error::CannotSpawnEditor { .. } => "Cannot spawn editor",
            Error::InvalidPatchName => "Invalid patch name",
            Error::CannotSpawnEditor { .. } => "Cannot start editor",
            Error::InvalidDate { .. } => "Invalid date",
            Error::PartialPullOverHttp => "Partial pull over HTTP is not (yet) supported",
            Error::UnknownHost { .. } => "Unknown host",
            Error::NoAuthor => "No authors were given",
            Error::NonFastForwardPush => "This push is not fast-forward",
            Error::NotSigningAuthor => "The user id for the signing key doesn't match the author. Authors must be of the form `rms@xyzcorp.com` or `Robert M. Smith <rms@xyzcorp.com>`",
            Error::EmptyPassword => "Empty password",
        }
    }

    fn cause(&self) -> Option<&std::error::Error> {
        match *self {
            Error::IO(ref e) => Some(e),
            Error::Term(ref e) => Some(e),
            Error::Repository(ref e) => Some(e),
            Error::UTF8(ref e) => Some(e),
            Error::Hex(ref e) => Some(e),
            Error::SSH(ref e) => Some(e),
            Error::SSHKeys(ref e) => Some(e),
            Error::Reqwest(ref e) => Some(e),
            Error::TomlDe(ref e) => Some(e),
            Error::TomlSer(ref e) => Some(e),
            Error::StripPrefix(ref e) => Some(e),
            Error::Regex(ref e) => Some(e),
            Error::ThrusshConfig(ref e) => Some(e),
            _ => None,
        }
    }
}

impl From<std::io::Error> for Error {
    fn from(err: std::io::Error) -> Error {
        Error::IO(err)
    }
}

impl From<failure::Error> for Error {
    fn from(err: failure::Error) -> Error {
        Error::Failure(err)
    }
}

impl From<term::Error> for Error {
    fn from(err: term::Error) -> Error {
        Error::Term(err)
    }
}

impl From<libpijul::Error> for Error {
    fn from(err: libpijul::Error) -> Error {
        Error::Repository(err)
    }
}

impl From<std::string::FromUtf8Error> for Error {
    fn from(err: std::string::FromUtf8Error) -> Error {
        Error::UTF8(err)
    }
}

impl From<hex::FromHexError> for Error {
    fn from(err: hex::FromHexError) -> Error {
        Error::Hex(err)
    }
}

impl From<thrussh::Error> for Error {
    fn from(err: thrussh::Error) -> Error {
        Error::SSH(err)
    }
}

impl From<thrussh_keys::Error> for Error {
    fn from(err: thrussh_keys::Error) -> Error {
        Error::SSHKeys(err)
    }
}

impl From<reqwest::Error> for Error {
    fn from(err: reqwest::Error) -> Error {
        Error::Reqwest(err)
    }
}

impl From<toml::de::Error> for Error {
    fn from(err: toml::de::Error) -> Error {
        Error::TomlDe(err)
    }
}

impl From<toml::ser::Error> for Error {
    fn from(err: toml::ser::Error) -> Error {
        Error::TomlSer(err)
    }
}

impl From<std::path::StripPrefixError> for Error {
    fn from(err: std::path::StripPrefixError) -> Error {
        Error::StripPrefix(err)
    }
}

impl From<regex::Error> for Error {
    fn from(err: regex::Error) -> Error {
        Error::Regex(err)
    }
}

impl From<thrussh_config::Error> for Error {
    fn from(err: thrussh_config::Error) -> Error {
        Error::ThrusshConfig(err)
    }
}

impl Error {
    pub fn lacks_space(&self) -> bool {
        match *self {
            Error::Repository(ref r) => r.lacks_space(),
            _ => false,
        }
    }
}

impl From<thrussh::HandlerError<Error>> for Error {
    fn from(err: thrussh::HandlerError<Error>) -> Error {
        match err {
            thrussh::HandlerError::Handler(e) => e,
            thrussh::HandlerError::Error(e) => Error::SSH(e),
        }
    }
}

impl From<Error> for thrussh::HandlerError<Error> {
    fn from(e: Error) -> thrussh::HandlerError<Error> {
        thrussh::HandlerError::Handler(e)
    }
}

/*
#[cfg(windows)]
#[cfg(windows)]
error_chain!{
    foreign_links {
        IO(std::io::Error);
        Term(term::Error);
        Repository(libpijul::Error);
        UTF8(std::string::FromUtf8Error);
        Hex(hex::FromHexError);
        SSH(thrussh::Error);
        SSHKeys(thrussh_keys::Error);
        Reqwest(reqwest::Error);
        TomlDe(toml::de::Error);
        TomlSer(toml::ser::Error);
        StripPrefix(std::path::StripPrefixError);
        Regex(regex::Error);
        ThrusshConfig(thrussh_config::Error);
    }
    }
    errors {
        HookFailed(cmd: String) {
            description("Failure hook")
                display("Hook failed: {}", cmd)
        }
        InARepository(path: std::path::PathBuf) {
            description("Inside repository")
                display("Inside repository {:?}", path)
        }
        NotInARepository {
            description("Not in a repository")
        }
        MissingRemoteRepository {
            description("Missing remote repository")
        }
        InvalidPath(path: String) {
            description("Invalid path")
                display("Invalid path {}", path)
        }
        FileNotInRepository(path: String) {
            description("File not in repository")
                display("File not in repository: {}", path)
        }
        WrongHash {
        WrongHash {
            description("Wrong hash")
        }
        BranchAlreadyExists {
            description("That branch already exists")
        }
        CannotDeleteCurrentBranch {
            description("Cannot delete current branch")
        }
        NoSuchBranch {
            description("No such branch")
                display("That branch does not exist. You can create it using pijul fork.")
        }
        IsDirectory {
            description("Is a directory")
        }
        CannotParseRemote {
            description("Cannot parse remote address")
        }
        WillNotOverwriteKeyFile(path: std::path::PathBuf) {
            description("Refusing to overwrite key file")
                display("Refusing to overwrite key file {:?}", path)
        }
        BranchDoesNotHavePatch(branch_name: String, patch: libpijul::Hash) {
            description("This branch does not have this patch")
                display("Branch \"{}\" does not have patch {}",
                        branch_name,
                        patch.to_base58())
        }
        PatchNotFound(repo_root: String, patch_hash: libpijul::Hash) {
            description("Patch not found")
            description("Patch not found")
                display("Patch {} not found in repository {:?}",
                        patch_hash.to_base58(),
                        repo_root)
        }
        SshKeyNotFound(path: PathBuf) {
            description("SSH key not found")
                display("SSH key not found in {:?}", path)
        }
        NoHomeDir {
            description("No home dir")
                display("No home dir")
        }
        ExtraDepNotOnBranch(hash: libpijul::Hash) {
            description("Extra dependencies can only be added if they are on the same branch as the current record.")
            display("Extra dependencies can only be added if they are on the same branch as the current record: {:?}", hash.to_base58())
        }
        PendingChanges {
            description("Pending Change in the Repository")
                display("There are pending changes in the repository. You need to revert or record them if you want to continue.")
        }
        EmptyPatchName {
            description("Empty patch name")
                display("You have provided an empty patch name.")
        }
        CannotSpawnEditor(editor: String, cause: String) {
            description("Cannot spawn editor command")
            display("Cannot start editor \"{}\" because: {}", editor, cause)
        }
        InvalidDate(date:  String) {
            description("Invalid date")
                display("“{}” is not a valid date", date)

        }
        PartialPullOverHttp {
            description("Partial pull over HTTP is not (yet) supported.")
                display("Partial pull over HTTP is not (yet) supported.")

        }
        UnknownHost(host: String) {
            description("Unknown host: {}", host)
                display("Unknown host.")
        }
        NoAuthor {
            description("No authors were given for this patch")
                display("No authors were given for this patch")
        }
    }
    }
    }
    }
}

#[cfg(unix)]
error_chain!{
    foreign_links {
        IO(std::io::Error);
        Term(term::Error);
        Repository(libpijul::Error);
        UTF8(std::string::FromUtf8Error);
        Hex(hex::FromHexError);
        SSH(thrussh::Error);
        SSHKeys(thrussh_keys::Error);
        Reqwest(reqwest::Error);
        TomlDe(toml::de::Error);
        TomlSer(toml::ser::Error);
        StripPrefix(std::path::StripPrefixError);
        Regex(regex::Error);
        ThrusshConfig(thrussh_config::Error);
    }
    }
    errors {
        HookFailed(cmd: String) {
            description("Failure hook")
                display("Hook failed: {}", cmd)
        }
        InARepository(path: std::path::PathBuf) {
            description("Inside repository")
                display("Inside repository {:?}", path)
        }
        NotInARepository {
            description("Not in a repository")
        }
        MissingRemoteRepository {
            description("Missing remote repository")
        }
        InvalidPath(path: String) {
            description("Invalid path")
                display("Invalid path {}", path)
        }
        FileNotInRepository(path: String) {
            description("File not in repository")
                display("File not in repository: {}", path)
        }
        }
        WrongHash {
            description("Wrong hash")
        }
        BranchAlreadyExists {
            description("That branch already exists")
        }
        CannotDeleteCurrentBranch {
            description("Cannot delete current branch")
        }
        NoSuchBranch {
            description("No such branch")
                display("That branch does not exist. You can create it using pijul fork.")
        }
        IsDirectory {
            description("Is a directory")
        }
        CannotParseRemote {
            description("Cannot parse remote address")
        }
        WillNotOverwriteKeyFile(path: std::path::PathBuf) {
            description("Refusing to overwrite key file")
                display("Refusing to overwrite key file {:?}", path)
        }
        BranchDoesNotHavePatch(branch_name: String, patch: libpijul::Hash) {
            description("This branch does not have this patch")
                display("Branch \"{}\" does not have patch {}",
                        branch_name,
                        patch.to_base58())
        }
        PatchNotFound(repo_root: String, patch_hash: libpijul::Hash) {
            description("Patch not found")
            description("Patch not found")
                display("Patch {} not found in repository {:?}",
                        patch_hash.to_base58(),
                        repo_root)
        }
        SshKeyNotFound(path: PathBuf) {
            description("SSH key not found")
                display("SSH key not found in {:?}", path)
        }
        NoHomeDir {
            description("No home dir")
                display("No home dir")
        }
        ExtraDepNotOnBranch(hash: libpijul::Hash) {
            description("Extra dependencies can only be added if they are on the same branch as the current record.")
            display("Extra dependencies can only be added if they are on the same branch as the current record: {:?}", hash.to_base58())
        }
        PendingChanges {
            description("Pending Change in the Repository")
                display("There are pending changes in the repository. You need to revert or record them if you want to continue.")
        }
        EmptyPatchName {
            description("Empty patch name")
                display("You have provided an empty patch name.")
        }
        CannotSpawnEditor(editor: String, cause: String) {
            description("Cannot spawn editor command")
            display("Cannot start editor \"{}\" because: {}", editor, cause)
        }
        InvalidDate(date:  String) {
            description("Invalid date")
                display("“{}” is not a valid date", date)

        }
        UnknownHost(host: String) {
            description("Unknown host.")
                display("Unknown host: {}", host)
        }
        }
        PartialPullOverHttp {
            description("Partial pull over HTTP is not (yet) supported.")
                display("Partial pull over HTTP is not (yet) supported.")

        }
        NoAuthor {
            description("No authors were given for this patch")
                display("No authors were given for this patch")
        }
        }
    }
    }
}

impl Error {
    pub fn lacks_space(&self) -> bool {
    pub fn lacks_space(&self) -> bool {
        match self.0 {
            ErrorKind::Repository(ref r) => r.lacks_space(),
            ErrorKind::Repository(ref r) => r.lacks_space(),
            _ => false,
        }
        }
    }
}

impl From<thrussh::HandlerError<Error>> for Error {
    fn from(err: thrussh::HandlerError<Error>) -> Error {
        match err {
            thrussh::HandlerError::Handler(e) => e,
            thrussh::HandlerError::Error(e) => ErrorKind::SSH(e).into(),
        }
        }
    }
}
impl From<Error> for thrussh::HandlerError<Error> {
    fn from(e: Error) -> thrussh::HandlerError<Error> {
        thrussh::HandlerError::Handler(e)
    }
}
*/
16
17

18
19
    pijul add --repository a file.txt
    run pijul record -a -A me --repository a
    assert_failure "Cannot start editor \"\" because: No such file or directory"
    assert_failure "Cannot start editor \"\" (\"No such file or directory (os error 2)\")"
}
17
18

19
20
    pijul add --repository a file.txt
    run pijul record -a -A me --repository a
    assert_failure "Cannot start editor \"../editor.sh\" because: Permission denied"
    assert_failure "Cannot start editor \"../editor.sh\" (\"Permission denied (os error 13)\")"
}
16
17

18
19
    pijul add --repository a file.txt
    run pijul record -a -A me --repository a
    assert_failure "Cannot start editor \" \" because: No such file or directory"
    assert_failure "Cannot start editor \" \" (\"No such file or directory (os error 2)\")"
}