2016-03-14 22:45:05 +00:00
|
|
|
use std::collections::HashMap;
|
2015-02-27 01:04:25 +00:00
|
|
|
use std::fs::{self, File};
|
|
|
|
use std::io::prelude::*;
|
|
|
|
use std::path::{PathBuf, Path};
|
2014-10-23 05:05:30 +00:00
|
|
|
|
2015-02-27 01:04:25 +00:00
|
|
|
use flate2::Compression::Default;
|
|
|
|
use flate2::write::GzEncoder;
|
2014-10-23 05:05:30 +00:00
|
|
|
use git2;
|
2015-03-26 18:17:44 +00:00
|
|
|
use rustc_serialize::hex::ToHex;
|
2016-03-14 22:45:05 +00:00
|
|
|
use rustc_serialize::json::ToJson;
|
2016-01-20 17:07:47 +00:00
|
|
|
use tar::{Builder, Header};
|
2014-10-23 05:05:30 +00:00
|
|
|
use url::Url;
|
|
|
|
|
|
|
|
use support::paths;
|
|
|
|
use support::git::repo;
|
|
|
|
use cargo::util::Sha256;
|
|
|
|
|
2015-02-27 01:04:25 +00:00
|
|
|
pub fn registry_path() -> PathBuf { paths::root().join("registry") }
|
|
|
|
pub fn registry() -> Url { Url::from_file_path(&*registry_path()).ok().unwrap() }
|
|
|
|
pub fn dl_path() -> PathBuf { paths::root().join("dl") }
|
|
|
|
pub fn dl_url() -> Url { Url::from_file_path(&*dl_path()).ok().unwrap() }
|
2014-10-23 05:05:30 +00:00
|
|
|
|
2015-11-11 02:08:06 +00:00
|
|
|
pub struct Package {
|
|
|
|
name: String,
|
|
|
|
vers: String,
|
2016-03-14 22:45:05 +00:00
|
|
|
deps: Vec<Dependency>,
|
2015-11-11 02:08:06 +00:00
|
|
|
files: Vec<(String, String)>,
|
|
|
|
yanked: bool,
|
2016-03-14 22:45:05 +00:00
|
|
|
features: HashMap<String, Vec<String>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Dependency {
|
|
|
|
name: String,
|
|
|
|
vers: String,
|
|
|
|
kind: String,
|
|
|
|
target: Option<String>,
|
|
|
|
features: Vec<String>,
|
2015-11-11 02:08:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn init() {
|
2014-11-21 17:36:18 +00:00
|
|
|
let config = paths::home().join(".cargo/config");
|
2015-02-27 01:04:25 +00:00
|
|
|
fs::create_dir_all(config.parent().unwrap()).unwrap();
|
2015-11-11 02:08:06 +00:00
|
|
|
if fs::metadata(&config).is_ok() {
|
|
|
|
return
|
|
|
|
}
|
2015-02-27 01:04:25 +00:00
|
|
|
File::create(&config).unwrap().write_all(format!(r#"
|
2014-10-23 05:05:30 +00:00
|
|
|
[registry]
|
|
|
|
index = "{reg}"
|
|
|
|
token = "api-token"
|
2015-02-27 01:04:25 +00:00
|
|
|
"#, reg = registry()).as_bytes()).unwrap();
|
2014-10-23 05:05:30 +00:00
|
|
|
|
|
|
|
// Init a new registry
|
|
|
|
repo(®istry_path())
|
2015-03-26 18:17:44 +00:00
|
|
|
.file("config.json", &format!(r#"
|
2014-10-23 05:05:30 +00:00
|
|
|
{{"dl":"{}","api":""}}
|
2015-03-26 18:17:44 +00:00
|
|
|
"#, dl_url()))
|
2014-10-23 05:05:30 +00:00
|
|
|
.build();
|
|
|
|
}
|
|
|
|
|
2015-11-11 02:08:06 +00:00
|
|
|
impl Package {
|
|
|
|
pub fn new(name: &str, vers: &str) -> Package {
|
|
|
|
init();
|
|
|
|
Package {
|
|
|
|
name: name.to_string(),
|
|
|
|
vers: vers.to_string(),
|
|
|
|
deps: Vec::new(),
|
|
|
|
files: Vec::new(),
|
|
|
|
yanked: false,
|
2016-03-14 22:45:05 +00:00
|
|
|
features: HashMap::new(),
|
2015-11-11 02:08:06 +00:00
|
|
|
}
|
2014-10-23 05:05:30 +00:00
|
|
|
}
|
|
|
|
|
2015-11-11 02:08:06 +00:00
|
|
|
pub fn file(&mut self, name: &str, contents: &str) -> &mut Package {
|
|
|
|
self.files.push((name.to_string(), contents.to_string()));
|
|
|
|
self
|
|
|
|
}
|
2014-10-23 05:05:30 +00:00
|
|
|
|
2015-11-11 02:08:06 +00:00
|
|
|
pub fn dep(&mut self, name: &str, vers: &str) -> &mut Package {
|
2016-03-14 22:45:05 +00:00
|
|
|
self.full_dep(name, vers, None, "normal", &[])
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn feature_dep(&mut self,
|
|
|
|
name: &str,
|
|
|
|
vers: &str,
|
|
|
|
features: &[&str]) -> &mut Package {
|
|
|
|
self.full_dep(name, vers, None, "normal", features)
|
2016-01-26 00:54:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn target_dep(&mut self,
|
|
|
|
name: &str,
|
|
|
|
vers: &str,
|
|
|
|
target: &str) -> &mut Package {
|
2016-03-14 22:45:05 +00:00
|
|
|
self.full_dep(name, vers, Some(target), "normal", &[])
|
2015-11-11 02:08:06 +00:00
|
|
|
}
|
2014-10-23 19:21:08 +00:00
|
|
|
|
2015-11-11 02:08:06 +00:00
|
|
|
pub fn dev_dep(&mut self, name: &str, vers: &str) -> &mut Package {
|
2016-03-14 22:45:05 +00:00
|
|
|
self.full_dep(name, vers, None, "dev", &[])
|
|
|
|
}
|
|
|
|
|
|
|
|
fn full_dep(&mut self,
|
|
|
|
name: &str,
|
|
|
|
vers: &str,
|
|
|
|
target: Option<&str>,
|
|
|
|
kind: &str,
|
|
|
|
features: &[&str]) -> &mut Package {
|
|
|
|
self.deps.push(Dependency {
|
|
|
|
name: name.to_string(),
|
|
|
|
vers: vers.to_string(),
|
|
|
|
kind: kind.to_string(),
|
|
|
|
target: target.map(|s| s.to_string()),
|
|
|
|
features: features.iter().map(|s| s.to_string()).collect(),
|
|
|
|
});
|
2015-11-11 02:08:06 +00:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn yanked(&mut self, yanked: bool) -> &mut Package {
|
|
|
|
self.yanked = yanked;
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn publish(&self) {
|
|
|
|
self.make_archive();
|
2014-10-23 05:05:30 +00:00
|
|
|
|
2015-11-11 02:08:06 +00:00
|
|
|
// Figure out what we're going to write into the index
|
2016-03-14 22:45:05 +00:00
|
|
|
let deps = self.deps.iter().map(|dep| {
|
|
|
|
let mut map = HashMap::new();
|
|
|
|
map.insert("name".to_string(), dep.name.to_json());
|
|
|
|
map.insert("req".to_string(), dep.vers.to_json());
|
|
|
|
map.insert("features".to_string(), dep.features.to_json());
|
|
|
|
map.insert("default_features".to_string(), false.to_json());
|
|
|
|
map.insert("target".to_string(), dep.target.to_json());
|
|
|
|
map.insert("optional".to_string(), false.to_json());
|
|
|
|
map.insert("kind".to_string(), dep.kind.to_json());
|
|
|
|
map
|
|
|
|
}).collect::<Vec<_>>();
|
2015-11-11 02:08:06 +00:00
|
|
|
let cksum = {
|
|
|
|
let mut c = Vec::new();
|
|
|
|
File::open(&self.archive_dst()).unwrap()
|
|
|
|
.read_to_end(&mut c).unwrap();
|
|
|
|
cksum(&c)
|
|
|
|
};
|
2016-03-14 22:45:05 +00:00
|
|
|
let mut dep = HashMap::new();
|
|
|
|
dep.insert("name".to_string(), self.name.to_json());
|
|
|
|
dep.insert("vers".to_string(), self.vers.to_json());
|
|
|
|
dep.insert("deps".to_string(), deps.to_json());
|
|
|
|
dep.insert("cksum".to_string(), cksum.to_json());
|
|
|
|
dep.insert("features".to_string(), self.features.to_json());
|
|
|
|
dep.insert("yanked".to_string(), self.yanked.to_json());
|
|
|
|
let line = dep.to_json().to_string();
|
|
|
|
|
2015-11-11 02:08:06 +00:00
|
|
|
let file = match self.name.len() {
|
|
|
|
1 => format!("1/{}", self.name),
|
|
|
|
2 => format!("2/{}", self.name),
|
|
|
|
3 => format!("3/{}/{}", &self.name[..1], self.name),
|
|
|
|
_ => format!("{}/{}/{}", &self.name[0..2], &self.name[2..4], self.name),
|
|
|
|
};
|
|
|
|
|
|
|
|
// Write file/line in the index
|
|
|
|
let dst = registry_path().join(&file);
|
2015-02-27 01:04:25 +00:00
|
|
|
let mut prev = String::new();
|
|
|
|
let _ = File::open(&dst).and_then(|mut f| f.read_to_string(&mut prev));
|
|
|
|
fs::create_dir_all(dst.parent().unwrap()).unwrap();
|
|
|
|
File::create(&dst).unwrap()
|
2015-11-11 02:08:06 +00:00
|
|
|
.write_all((prev + &line[..] + "\n").as_bytes()).unwrap();
|
|
|
|
|
|
|
|
// Add the new file to the index
|
|
|
|
let repo = git2::Repository::open(®istry_path()).unwrap();
|
|
|
|
let mut index = repo.index().unwrap();
|
|
|
|
index.add_path(Path::new(&file)).unwrap();
|
|
|
|
index.write().unwrap();
|
|
|
|
let id = index.write_tree().unwrap();
|
|
|
|
|
|
|
|
// Commit this change
|
|
|
|
let tree = repo.find_tree(id).unwrap();
|
|
|
|
let sig = repo.signature().unwrap();
|
|
|
|
let parent = repo.refname_to_id("refs/heads/master").unwrap();
|
|
|
|
let parent = repo.find_commit(parent).unwrap();
|
|
|
|
repo.commit(Some("HEAD"), &sig, &sig,
|
|
|
|
"Another commit", &tree,
|
|
|
|
&[&parent]).unwrap();
|
2014-10-23 05:05:30 +00:00
|
|
|
}
|
|
|
|
|
2015-11-11 02:08:06 +00:00
|
|
|
fn make_archive(&self) {
|
|
|
|
let mut manifest = format!(r#"
|
|
|
|
[package]
|
|
|
|
name = "{}"
|
|
|
|
version = "{}"
|
|
|
|
authors = []
|
|
|
|
"#, self.name, self.vers);
|
2016-03-14 22:45:05 +00:00
|
|
|
for dep in self.deps.iter() {
|
|
|
|
let target = match dep.target {
|
|
|
|
None => String::new(),
|
|
|
|
Some(ref s) => format!("target.{}.", s),
|
2016-01-26 00:54:10 +00:00
|
|
|
};
|
2016-03-14 22:45:05 +00:00
|
|
|
let kind = match &dep.kind[..] {
|
2015-11-11 02:08:06 +00:00
|
|
|
"build" => "build-",
|
|
|
|
"dev" => "dev-",
|
|
|
|
_ => ""
|
2016-01-26 00:54:10 +00:00
|
|
|
};
|
|
|
|
manifest.push_str(&format!(r#"
|
|
|
|
[{}{}dependencies.{}]
|
|
|
|
version = "{}"
|
2016-03-14 22:45:05 +00:00
|
|
|
"#, target, kind, dep.name, dep.vers));
|
2015-11-11 02:08:06 +00:00
|
|
|
}
|
2014-10-23 05:05:30 +00:00
|
|
|
|
2015-11-11 02:08:06 +00:00
|
|
|
let dst = self.archive_dst();
|
|
|
|
fs::create_dir_all(dst.parent().unwrap()).unwrap();
|
|
|
|
let f = File::create(&dst).unwrap();
|
2016-01-20 17:07:47 +00:00
|
|
|
let mut a = Builder::new(GzEncoder::new(f, Default));
|
|
|
|
self.append(&mut a, "Cargo.toml", &manifest);
|
2016-01-15 14:51:28 +00:00
|
|
|
if self.files.is_empty() {
|
2016-01-20 17:07:47 +00:00
|
|
|
self.append(&mut a, "src/lib.rs", "");
|
2015-11-11 02:08:06 +00:00
|
|
|
} else {
|
|
|
|
for &(ref name, ref contents) in self.files.iter() {
|
2016-01-20 17:07:47 +00:00
|
|
|
self.append(&mut a, name, contents);
|
2015-11-11 02:08:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-20 17:07:47 +00:00
|
|
|
fn append<W: Write>(&self, ar: &mut Builder<W>, file: &str, contents: &str) {
|
|
|
|
let mut header = Header::new_ustar();
|
2015-11-11 02:08:06 +00:00
|
|
|
header.set_size(contents.len() as u64);
|
|
|
|
header.set_path(format!("{}-{}/{}", self.name, self.vers, file)).unwrap();
|
|
|
|
header.set_cksum();
|
|
|
|
|
2016-01-20 17:07:47 +00:00
|
|
|
ar.append(&header, contents.as_bytes()).unwrap();
|
2015-11-11 02:08:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn archive_dst(&self) -> PathBuf {
|
|
|
|
dl_path().join(&self.name).join(&self.vers).join("download")
|
|
|
|
}
|
2014-10-23 05:05:30 +00:00
|
|
|
}
|
|
|
|
|
2015-11-11 02:08:06 +00:00
|
|
|
fn cksum(s: &[u8]) -> String {
|
2014-10-23 05:05:30 +00:00
|
|
|
let mut sha = Sha256::new();
|
|
|
|
sha.update(s);
|
|
|
|
sha.finish().to_hex()
|
|
|
|
}
|