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
//! Configuration for `repo-backup`.

use std::path::{Path, PathBuf};
use std::io::Read;
use std::fs::File;

use failure::{Error, ResultExt};
use toml;

/// The overall configuration struct.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Config {
    /// General configuration options.
    pub general: General,
    /// Settings specific to the `Github` provider.
    pub github: Option<GithubConfig>,
}

/// General settings used by `repo-backup`.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct General {
    /// The root directory to place all downloaded repositories.
    pub dest_dir: PathBuf,
}

/// Github-specific settings.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct GithubConfig {
    /// The API key to use. You will need to [create a new personal access
    /// token][new] and give it the `public_repo` permissions before you can
    /// fetch repos from GitHub.
    ///
    /// [new]: https://github.com/settings/tokens/new
    pub api_key: String,
    /// Should we download all starred repos? (default: true)
    #[serde(default = "always_true")]
    pub starred: bool,
    /// Should we download all owned repos? (default: true)
    #[serde(default = "always_true")]
    pub owned: bool,
}

fn always_true() -> bool {
    true
}

impl Config {
    /// Load a `Config` from some file on disk.
    pub fn from_file<P: AsRef<Path>>(file: P) -> Result<Config, Error> {
        let file = file.as_ref();
        debug!("Reading config from {}", file.display());

        let mut buffer = String::new();
        File::open(file)
            .with_context(|_| format!("Unable to open {}", file.display()))?
            .read_to_string(&mut buffer)
            .context("Reading config file failed")?;

        Config::from_str(&buffer)
    }

    /// Load the config directly from a source string.
    pub fn from_str(src: &str) -> Result<Config, Error> {
        toml::from_str(src)
            .context("Parsing config file failed")
            .map_err(Error::from)
    }

    /// Generate an example config.
    pub fn example() -> Config {
        Config {
            general: General {
                dest_dir: PathBuf::from("/srv"),
            },
            github: Some(GithubConfig {
                api_key: String::from("your API key"),
                owned: true,
                starred: false,
            }),
        }
    }

    /// Serialize the `Config` as TOML.
    pub fn as_toml(&self) -> String {
        match toml::to_string_pretty(self) {
            Ok(s) => s,
            Err(e) => {
                panic!("Serializing a Config should never fail. {}", e);
            }
        }
    }
}