Skip to content

Commit 712dcb6

Browse files
committed
Get new crates from crates.io web API
Docs.rs is currently using crates.io-index git repository to resolve new crates. But method used in docs.rs isn't very stable and doesn't work all the time. Using crates.io web API will solve this issue and bring more stable way to resolve new crates. Fixes: #76, #60
1 parent 0dfb64c commit 712dcb6

File tree

6 files changed

+65
-98
lines changed

6 files changed

+65
-98
lines changed

Cargo.lock

-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ hyper = "0.9"
1818
semver = "0.5"
1919
slug = "=0.1.1"
2020
env_logger = "0.3"
21-
git2 = "0.4"
2221
magic = "0.12"
2322
iron = "0.3.1"
2423
router = "0.1.1"

src/docbuilder/queue.rs

+64-91
Original file line numberDiff line numberDiff line change
@@ -2,109 +2,55 @@
22
33

44
use super::DocBuilder;
5-
use rustc_serialize::json::Json;
6-
use git2;
5+
use rustc_serialize::json::{Json, Array};
6+
use hyper;
77
use db::connect_db;
88
use errors::*;
99

1010

1111
impl DocBuilder {
1212
/// Updates crates.io-index repository and adds new crates into build queue
13-
pub fn get_new_crates(&self) -> Result<()> {
14-
15-
let repo = try!(git2::Repository::open(&self.options.crates_io_index_path));
16-
17-
let old_tree = try!(self.head_tree(&repo));
18-
try!(self.update_repo(&repo));
19-
let new_tree = try!(self.head_tree(&repo));
20-
21-
let diff = try!(repo.diff_tree_to_tree(Some(&old_tree), Some(&new_tree), None));
22-
let conn = try!(connect_db());
23-
let mut line_n = 0;
24-
25-
try!(diff.print(git2::DiffFormat::Patch, |_, hunk, diffline| -> bool {
26-
let line = String::from_utf8_lossy(diffline.content()).into_owned();
27-
// crate strings starts with '{'
28-
// skip if line is not a crate string
29-
if line.chars().nth(0) != Some('{') {
30-
line_n = 0;
31-
return true;
32-
}
33-
34-
line_n += 1;
35-
36-
if match hunk {
37-
Some(hunk) => hunk.new_lines() != line_n,
38-
None => true,
39-
} {
40-
return true;
41-
}
42-
43-
let json = match Json::from_str(&line[..]) {
44-
Ok(j) => j,
45-
Err(err) => {
46-
error!("Failed to parse crate string: {}", err);
47-
// just continue even if we get an error for a crate
48-
return true;
13+
pub fn get_new_crates(&mut self) -> Result<()> {
14+
try!(self.load_database_cache());
15+
16+
let body = {
17+
use std::io::Read;
18+
let client = hyper::Client::new();
19+
let mut res = try!(client.get("https://crates.io/summary").send());
20+
let mut body = String::new();
21+
try!(res.read_to_string(&mut body));
22+
body
23+
};
24+
25+
let json = try!(Json::from_str(&body));
26+
27+
let crates = {
28+
let mut crates: Vec<(String, String)> = Vec::new();
29+
for section in ["just_updated", "new_crates"].iter() {
30+
match json.as_object()
31+
.and_then(|o| o.get(&section[..]))
32+
.and_then(|j| j.as_array())
33+
.map(get_crates_from_array) {
34+
Some(mut c) => crates.append(c.as_mut()),
35+
None => continue,
4936
}
50-
};
51-
52-
// check if a crate is yanked just in case
53-
if json.as_object()
54-
.and_then(|obj| obj.get("yanked"))
55-
.and_then(|y| y.as_boolean())
56-
.unwrap_or(true) {
57-
return true;
5837
}
38+
crates
39+
};
5940

60-
if let Some((krate, version)) = json.as_object()
61-
.map(|obj| {
62-
(obj.get("name")
63-
.and_then(|n| n.as_string()),
64-
obj.get("vers")
65-
.and_then(|n| n.as_string()))
66-
}) {
67-
68-
// Skip again if we can't get crate name and version
69-
if krate.is_none() || version.is_none() {
70-
return true;
71-
}
72-
73-
let _ = conn.execute("INSERT INTO queue (name, version) VALUES ($1, $2)",
74-
&[&krate.unwrap(), &version.unwrap()]);
41+
let conn = try!(connect_db());
42+
for (name, version) in crates {
43+
if self.db_cache.contains(&format!("{}-{}", name, version)[..]) {
44+
continue;
7545
}
76-
77-
true
78-
}));
79-
80-
Ok(())
81-
}
82-
83-
84-
fn update_repo(&self, repo: &git2::Repository) -> Result<()> {
85-
let mut remote = try!(repo.find_remote("origin"));
86-
try!(remote.fetch(&["refs/heads/*:refs/remotes/origin/*"], None, None));
87-
88-
// checkout master
89-
try!(repo.refname_to_id("refs/remotes/origin/master")
90-
.and_then(|oid| repo.find_object(oid, None))
91-
.and_then(|object| repo.reset(&object, git2::ResetType::Hard, None)));
46+
let _ = conn.execute("INSERT INTO queue (name, version) VALUES ($1, $2)",
47+
&[&name, &version]);
48+
}
9249

9350
Ok(())
9451
}
9552

9653

97-
fn head_tree<'a>(&'a self, repo: &'a git2::Repository) -> Result<git2::Tree> {
98-
repo.head()
99-
.ok()
100-
.and_then(|head| head.target())
101-
.ok_or(git2::Error::from_str("HEAD SHA1 not found"))
102-
.and_then(|oid| repo.find_commit(oid))
103-
.and_then(|commit| commit.tree())
104-
.or_else(|e| Err(ErrorKind::Git2Error(e).into()))
105-
}
106-
107-
10854
/// Builds packages from queue
10955
pub fn build_packages_queue(&mut self) -> Result<()> {
11056
let conn = try!(connect_db());
@@ -117,7 +63,7 @@ impl DocBuilder {
11763
match self.build_package(&name[..], &version[..]) {
11864
Ok(_) => {
11965
let _ = conn.execute("DELETE FROM queue WHERE id = $1", &[&id]);
120-
},
66+
}
12167
Err(e) => {
12268
error!("Failed to build package {}-{} from queue: {}",
12369
name,
@@ -132,6 +78,29 @@ impl DocBuilder {
13278
}
13379

13480

81+
/// Returns Vec<CRATE_NAME, CRATE_VERSION> from a summary array
82+
fn get_crates_from_array<'a>(crates: &'a Array) -> Vec<(String, String)> {
83+
let mut crates_vec: Vec<(String, String)> = Vec::new();
84+
for crte in crates {
85+
let name = match crte.as_object()
86+
.and_then(|o| o.get("id"))
87+
.and_then(|i| i.as_string())
88+
.map(|s| s.to_owned()) {
89+
Some(s) => s,
90+
None => continue,
91+
};
92+
let version = match crte.as_object()
93+
.and_then(|o| o.get("max_version"))
94+
.and_then(|v| v.as_string())
95+
.map(|s| s.to_owned()) {
96+
Some(s) => s,
97+
None => continue,
98+
};
99+
crates_vec.push((name, version));
100+
}
101+
crates_vec
102+
}
103+
135104

136105

137106

@@ -146,8 +115,12 @@ mod test {
146115
fn test_get_new_crates() {
147116
let _ = env_logger::init();
148117
let options = DocBuilderOptions::from_prefix(PathBuf::from("../cratesfyi-prefix"));
149-
let docbuilder = DocBuilder::new(options);
150-
assert!(docbuilder.get_new_crates().is_ok());
118+
let mut docbuilder = DocBuilder::new(options);
119+
let res = docbuilder.get_new_crates();
120+
if res.is_err() {
121+
error!("{:?}", res);
122+
}
123+
assert!(res.is_ok());
151124
}
152125

153126

src/errors.rs

-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ use rustc_serialize::json;
55
use postgres;
66
use cargo;
77
use hyper;
8-
use git2;
98
use magic::MagicError;
109

1110

@@ -23,7 +22,6 @@ error_chain! {
2322
postgres::error::ConnectError, PostgresConnectError;
2423
postgres::error::Error, PostgresError;
2524
hyper::Error, HyperError;
26-
git2::Error, Git2Error;
2725
MagicError, MagicError;
2826
Box<cargo::CargoError>, CargoError;
2927
}

src/lib.rs

-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ extern crate hyper;
1313
extern crate time;
1414
extern crate semver;
1515
extern crate slug;
16-
extern crate git2;
1716
extern crate magic;
1817
extern crate iron;
1918
extern crate router;

src/utils/daemon.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,7 @@ pub fn start_daemon() {
5151
loop {
5252
thread::sleep(Duration::from_secs(900));
5353
debug!("Checking new crates");
54-
let doc_builder = DocBuilder::new(opts());
55-
if let Err(e) = doc_builder.get_new_crates() {
54+
if let Err(e) = DocBuilder::new(opts()).get_new_crates() {
5655
error!("Failed to get new crates: {}", e);
5756
}
5857
}

0 commit comments

Comments
 (0)