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
//! A small utility for making a local copy of all your projects from a variety
//! of various sources.
//!
//! Sources currently supported:
//!
//! - [GitHub](https://github.com/) ([Provider](./struct.GitHub.html))
//! - [GitLab](https://about.gitlab.com/) ([Provider](./struct.GitLab.html))
//!
//!
//! # Configuration
//!
//! Configuration is done via a `repo-backup.toml` file. By default the
//! `repo-backup` program will look for this in your home directory (as
//! `~/.repo-backup.toml`), but this can be overridden via the command line.
//!
//! The configuration file looks something like this:
//!
//! ```rust
//! # use repo_backup::Config;
//! # let src = r#"
//! [general]
//! dest-dir = "/srv"
//!
//! [github]
//! api-key = "your API key"
//! owned = true
//! starred = false
//!
//! [gitlab]
//! api-key = "your API key"
//! host = "gitlab.com"
//! organisations = true
//! owned = true
//! # "#;
//! # let example = Config::from_str(src).unwrap();
//! # assert_eq!(example, Config::example());
//! ```
//!
//! The only required table is `general`, with the others used to enable and
//! configure the corresponding [`Provider`].
//!
//! # Examples
//!
//! This crate is designed to be really easy to use as both an executable, *and*
//! a library.
//!
//! The [`Driver`] will:
//!
//! - Query each [`Provider`] in the [`Config`] for the available repositories, and
//! - Download each repository to [`dest_dir`].
//!
//! ```rust,no_run
//! # extern crate repo_backup;
//! # extern crate failure;
//! # use failure::Error;
//! use repo_backup::{Config, Driver};
//!
//! # fn run() -> Result<(), Error> {
//! let cfg = Config::from_file("/path/to/repo-backup.toml")?;
//! let driver = Driver::with_config(cfg);
//!
//! driver.run()?;
//! # Ok(())
//! # }
//! # fn main() { run().unwrap() }
//! ```
//!
//! Or if you want control over the list fetching and download process (e.g.
//! to add a couple extra git repos to the list or use your own [`Provider`]):
//!
//! ```rust,no_run
//! # extern crate repo_backup;
//! # extern crate failure;
//! # use failure::Error;
//! use repo_backup::{Config, Driver, Repo, Provider};
//!
//! struct MyCustomProvider;
//!
//! impl Provider for MyCustomProvider {
//!     fn name(&self) -> &str {
//!         "custom-provider"
//!     }
//!
//!     fn repositories(&self) -> Result<Vec<Repo>,  Error> {
//!         unimplemented!()
//!     }
//! }
//!
//! # fn run() -> Result<(), Error> {
//! let cfg = Config::from_file("/path/to/repo-backup.toml")?;
//! let driver = Driver::with_config(cfg);
//!
//! let providers: Vec<Box<Provider>> = vec![Box::new(MyCustomProvider)];
//! let mut repos = driver.get_repos_from_providers(&providers)?;
//!
//! let my_repo = Repo {
//!     name: String::from("My Repo"),
//!     owner: String::from("Michael-F-Bryan"),
//!     provider: String::from("custom"),
//!     url: String::from("http://my.git.server/Michael-F-Bryan/my_repo"),
//! };
//! repos.push(my_repo);
//!
//! driver.update_repos(&repos)?;
//! # Ok(())
//! # }
//! # fn main() { run().unwrap() }
//! ```
//!
//! [`Driver`]: struct.Driver.html
//! [`Provider`]: trait.Provider.html
//! [`Config`]: config/struct.Config.html
//! [`dest_dir`]: config/struct.General.html#structfield.dest_dir

#![deny(
    missing_docs,
    missing_debug_implementations,
    missing_copy_implementations,
    trivial_casts,
    trivial_numeric_casts,
    unsafe_code,
    unstable_features,
    unused_import_braces,
    unused_imports,
    unused_qualifications
)]

extern crate failure;
extern crate hyperx;
#[macro_use]
extern crate failure_derive;
extern crate gitlab;
#[macro_use]
extern crate log;
extern crate reqwest;
extern crate sec;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate toml;

#[macro_use]
mod utils;
pub mod config;
mod driver;
mod github;
mod gitlab_provider;

pub use config::Config;
pub use driver::{Driver, UpdateFailure};
pub use github::GitHub;
pub use gitlab_provider::GitLab;

use failure::{Error, SyncFailure};

/// A repository.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Repo {
    /// The repository's owner.
    pub owner: String,
    /// The name of the repository.
    pub name: String,
    /// Which provider this repository was retrieved from.
    pub provider: String,
    /// A URL which can be used when downloading the repo.
    pub url: String,
}

impl Repo {
    /// Get the repository's canonical name in `$provider/$owner/$name` form
    /// (e.g. `github/Michael-F-Bryan/repo-backup`).
    pub fn full_name(&self) -> String {
        format!("{}/{}/{}", self.provider, self.owner, self.name)
    }
}

/// A source of repositories.
pub trait Provider {
    /// The `Provider`'s name.
    fn name(&self) -> &str;

    /// Get a list of all the available repositories from this source.
    fn repositories(&self) -> Result<Vec<Repo>, Error>;
}

trait SyncResult<T, E> {
    fn sync(self) -> Result<T, SyncFailure<E>>
    where
        Self: Sized,
        E: ::std::error::Error + Send + 'static;
}

impl<T, E> SyncResult<T, E> for Result<T, E> {
    fn sync(self) -> Result<T, SyncFailure<E>>
    where
        Self: Sized,
        E: ::std::error::Error + Send + 'static,
    {
        self.map_err(SyncFailure::new)
    }
}