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
use crate::index::{CloneOptions, LAST_SEEN_REFNAME};
use crate::Index;
use git_repository as git;
use std::path::Path;

/// The error returned by various initialization methods.
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
    #[error(transparent)]
    Clone(#[from] git2::Error),
    #[error(transparent)]
    Open(#[from] git::open::Error),
}

/// Initialization
impl Index {
    /// Return a new `Index` instance from the given `path`, which should contain a bare or non-bare
    /// clone of the `crates.io` index.
    /// If the directory does not contain the repository or does not exist, it will be cloned from
    /// the official location automatically (with complete history).
    ///
    /// An error will occour if the repository exists and the remote URL does not match the given repository URL.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use crates_index_diff::{Index, index};
    ///
    /// # let path = tempdir::TempDir::new("index").unwrap();
    /// let mut options = index::CloneOptions {
    ///   repository_url: "https://github.com/rust-lang/staging.crates.io-index".into(),
    ///   ..Default::default()
    /// };
    ///
    ///
    /// let index = Index::from_path_or_cloned_with_options(path, options)?;
    /// # Ok::<(), crates_index_diff::index::init::Error>(())
    /// ```
    /// Or to access a private repository, use fetch options.
    ///
    /// ```no_run
    /// use crates_index_diff::{index, Index};
    /// let fo = {
    ///     let mut fo = git2::FetchOptions::new();
    ///     fo.remote_callbacks({
    ///         let mut callbacks = git2::RemoteCallbacks::new();
    ///         callbacks.credentials(|_url, username_from_url, _allowed_types| {
    ///             git2::Cred::ssh_key_from_memory(
    ///                 username_from_url.unwrap(),
    ///                 None,
    ///                 &std::env::var("PRIVATE_KEY").unwrap(),
    ///                 None,
    ///             )
    ///         });
    ///         callbacks
    ///     });
    ///     fo
    /// };
    /// Index::from_path_or_cloned_with_options(
    ///     "index",
    ///     index::CloneOptions {
    ///         repository_url: "git@github.com:private-index/goes-here.git".into(),
    ///         fetch_options: Some(fo),
    ///     },
    /// ).unwrap();
    /// ```
    pub fn from_path_or_cloned_with_options(
        path: impl AsRef<Path>,
        CloneOptions {
            repository_url,
            fetch_options,
        }: CloneOptions<'_>,
    ) -> Result<Index, Error> {
        let repo = git2::Repository::open(path.as_ref()).or_else(|err| {
            if err.class() == git2::ErrorClass::Repository {
                let mut builder = git2::build::RepoBuilder::new();
                if let Some(fo) = fetch_options {
                    builder.fetch_options(fo);
                }
                builder.bare(true).clone(&repository_url, path.as_ref())
            } else {
                Err(err)
            }
        })?;

        let mut repo = git::open(repo.path())?.apply_environment();
        repo.object_cache_size_if_unset(4 * 1024 * 1024);
        Ok(Index {
            repo,
            remote_name: "origin",
            branch_name: "master",
            seen_ref_name: LAST_SEEN_REFNAME,
        })
    }

    /// Return a new `Index` instance from the given `path`, which should contain a bare or non-bare
    /// clone of the `crates.io` index.
    /// If the directory does not contain the repository or does not exist, it will be cloned from
    /// the official location automatically (with complete history).
    pub fn from_path_or_cloned(path: impl AsRef<Path>) -> Result<Index, Error> {
        Index::from_path_or_cloned_with_options(path, CloneOptions::default())
    }
}