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
mod file_index;
mod git_index;

use git;
use objects::Objects;
pub use reproto_core::{RpPackage, Version, VersionReq};
pub use self::file_index::init_file_index;
use self::git_index::*;
use sha256::Checksum;
use std::path::{Path, PathBuf};
use std::rc::Rc;
use url::Url;

/// Configuration file for objects backends.
pub struct IndexConfig {
    /// Root path when checking out local repositories.
    pub repos: Option<PathBuf>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Deployment {
    pub version: Version,
    pub object: Checksum,
}

impl Deployment {
    pub fn new(version: Version, object: Checksum) -> Deployment {
        Deployment {
            version: version,
            object: object,
        }
    }
}

use errors::*;

pub trait Index {
    fn resolve(&self,
               package: &RpPackage,
               version_req: Option<&VersionReq>)
               -> Result<Vec<Deployment>>;

    fn put_version(&self,
                   checksum: &Checksum,
                   package: &RpPackage,
                   version: &Version,
                   force: bool)
                   -> Result<()>;

    fn get_deployments(&self, package: &RpPackage, version: &Version) -> Result<Vec<Deployment>>;

    /// Get an objects URL as configured in the index.
    ///
    /// If relative, will cause objects to be loaded from the same repository as the index.
    fn objects_url(&self) -> Result<&str>;

    /// Load objects relative to the index repository.
    fn objects_from_index(&self, relative_path: &Path) -> Result<Box<Objects>>;

    /// Update local caches related to the index.
    fn update(&self) -> Result<()> {
        Ok(())
    }
}

pub fn index_from_file(url: &Url) -> Result<Box<Index>> {
    let path = Path::new(url.path());

    if !path.is_dir() {
        return Err(format!("no such directory: {}", path.display()).into());
    }

    Ok(Box::new(file_index::FileIndex::new(&path)?))
}

pub fn index_from_git<'a, I>(config: IndexConfig, scheme: I, url: &'a Url) -> Result<Box<Index>>
    where I: IntoIterator<Item = &'a str>
{
    let mut scheme = scheme.into_iter();

    let sub_scheme = scheme.next()
        .ok_or_else(|| format!("invalid scheme ({}), expected git+scheme", url.scheme()))?;

    let repos = config.repos.ok_or_else(|| "repos: not specified")?;

    let git_repo = git::setup_git_repo(&repos, sub_scheme, url)?;
    let file_objects = file_index::FileIndex::new(git_repo.path())?;

    let git_repo = Rc::new(git_repo);
    let index = GitIndex::new(url.clone(), git_repo, file_objects);

    Ok(Box::new(index))
}

pub fn index_from_url(config: IndexConfig, url: &Url) -> Result<Box<Index>> {
    let mut scheme = url.scheme().split("+");
    let first = scheme.next().ok_or_else(|| format!("invalid scheme: {}", url))?;

    match first {
        "file" => index_from_file(url),
        "git" => index_from_git(config, scheme, url),
        scheme => Err(format!("unsupported scheme ({}): {}", scheme, url).into()),
    }
}