mirror of
https://github.com/deps-rs/deps.rs.git
synced 2024-11-22 02:16:30 +00:00
add popular crates to index
This commit is contained in:
parent
e0352539bd
commit
37a11c17b5
4 changed files with 135 additions and 34 deletions
|
@ -21,7 +21,7 @@ use ::utils::cache::Cache;
|
|||
use ::models::repo::{Repository, RepoPath};
|
||||
use ::models::crates::{CrateName, CratePath, CrateRelease, AnalyzedDependencies};
|
||||
|
||||
use ::interactors::crates::QueryCrate;
|
||||
use ::interactors::crates::{QueryCrate, GetPopularCrates};
|
||||
use ::interactors::RetrieveFileAtPath;
|
||||
use ::interactors::github::{GetPopularRepos};
|
||||
|
||||
|
@ -36,6 +36,7 @@ pub struct Engine {
|
|||
logger: Logger,
|
||||
|
||||
query_crate: Arc<Cache<QueryCrate<HttpClient>>>,
|
||||
get_popular_crates: Arc<Cache<GetPopularCrates<HttpClient>>>,
|
||||
get_popular_repos: Arc<Cache<GetPopularRepos<HttpClient>>>,
|
||||
retrieve_file_at_path: Arc<RetrieveFileAtPath<HttpClient>>
|
||||
}
|
||||
|
@ -43,12 +44,14 @@ pub struct Engine {
|
|||
impl Engine {
|
||||
pub fn new(client: Client<HttpsConnector<HttpConnector>>, logger: Logger) -> Engine {
|
||||
let query_crate = Cache::new(QueryCrate(client.clone()), Duration::from_secs(300), 500);
|
||||
let get_popular_crates = Cache::new(GetPopularCrates(client.clone()), Duration::from_secs(10), 1);
|
||||
let get_popular_repos = Cache::new(GetPopularRepos(client.clone()), Duration::from_secs(10), 1);
|
||||
|
||||
Engine {
|
||||
client: client.clone(), logger,
|
||||
|
||||
query_crate: Arc::new(query_crate),
|
||||
get_popular_crates: Arc::new(get_popular_crates),
|
||||
get_popular_repos: Arc::new(get_popular_repos),
|
||||
retrieve_file_at_path: Arc::new(RetrieveFileAtPath(client))
|
||||
}
|
||||
|
@ -84,6 +87,13 @@ impl Engine {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn get_popular_crates(&self) ->
|
||||
impl Future<Item=Vec<CratePath>, Error=Error>
|
||||
{
|
||||
self.get_popular_crates.call(())
|
||||
.from_err().map(|crates| crates.clone())
|
||||
}
|
||||
|
||||
pub fn analyze_repo_dependencies(&self, repo_path: RepoPath) ->
|
||||
impl Future<Item=AnalyzeDependenciesOutcome, Error=Error>
|
||||
{
|
||||
|
@ -180,7 +190,8 @@ lazy_static! {
|
|||
RepoPath::from_parts("github", "google", "xi-editor"),
|
||||
RepoPath::from_parts("github", "lk-geimfari", "awesomo"),
|
||||
RepoPath::from_parts("github", "redox-os", "tfs"),
|
||||
RepoPath::from_parts("github", "carols10cents", "rustlings")
|
||||
RepoPath::from_parts("github", "carols10cents", "rustlings"),
|
||||
RepoPath::from_parts("github", "rust-unofficial", "awesome-rust")
|
||||
].into_iter().collect::<Result<HashSet<_>, _>>().unwrap()
|
||||
};
|
||||
}
|
||||
|
|
|
@ -7,9 +7,10 @@ use tokio_service::Service;
|
|||
use semver::{Version, VersionReq};
|
||||
use serde_json;
|
||||
|
||||
use ::models::crates::{CrateName, CrateRelease, CrateDeps, CrateDep};
|
||||
use ::models::crates::{CrateName, CrateRelease, CrateDeps, CrateDep, CratePath};
|
||||
|
||||
const CRATES_INDEX_BASE_URI: &str = "https://raw.githubusercontent.com/rust-lang/crates.io-index";
|
||||
const CRATES_API_BASE_URI: &str = "https://crates.io/api/v1";
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct RegistryPackageDep {
|
||||
|
@ -109,3 +110,59 @@ impl<S> Service for QueryCrate<S>
|
|||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct SummaryResponseDetail {
|
||||
name: String,
|
||||
max_version: Version
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct SummaryResponse {
|
||||
most_downloaded: Vec<SummaryResponseDetail>
|
||||
}
|
||||
|
||||
fn convert_summary(response: SummaryResponse) -> Result<Vec<CratePath>, Error> {
|
||||
response.most_downloaded.into_iter().map(|detail| {
|
||||
let name = detail.name.parse()?;
|
||||
Ok(CratePath { name, version: detail.max_version })
|
||||
}).collect()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GetPopularCrates<S>(pub S);
|
||||
|
||||
impl<S> Service for GetPopularCrates<S>
|
||||
where S: Service<Request=Request, Response=Response, Error=HyperError> + Clone + 'static,
|
||||
S::Future: 'static
|
||||
{
|
||||
type Request = ();
|
||||
type Response = Vec<CratePath>;
|
||||
type Error = Error;
|
||||
type Future = Box<Future<Item=Self::Response, Error=Self::Error>>;
|
||||
|
||||
fn call(&self, _req: ()) -> Self::Future {
|
||||
let service = self.0.clone();
|
||||
|
||||
let uri_future = format!("{}/summary", CRATES_API_BASE_URI)
|
||||
.parse::<Uri>().into_future().from_err();
|
||||
|
||||
Box::new(uri_future.and_then(move |uri| {
|
||||
let request = Request::new(Method::Get, uri.clone());
|
||||
|
||||
service.call(request).from_err().and_then(move |response| {
|
||||
let status = response.status();
|
||||
if !status.is_success() {
|
||||
future::Either::A(future::err(format_err!("Status code {} for URI {}", status, uri)))
|
||||
} else {
|
||||
let body_future = response.body().concat2().from_err();
|
||||
let decode_future = body_future.and_then(|body| {
|
||||
let summary = serde_json::from_slice::<SummaryResponse>(&body)?;
|
||||
convert_summary(summary)
|
||||
});
|
||||
future::Either::B(decode_future)
|
||||
}
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -113,18 +113,20 @@ impl Server {
|
|||
fn index(&self, _req: Request, _params: Params, logger: Logger) ->
|
||||
impl Future<Item=Response, Error=HyperError>
|
||||
{
|
||||
self.engine.get_popular_repos().then(move |popular_result| {
|
||||
match popular_result {
|
||||
Err(err) => {
|
||||
error!(logger, "error: {}", err);
|
||||
let mut response = views::html::error::render("Could not retrieve popular repositories", "");
|
||||
response.set_status(StatusCode::InternalServerError);
|
||||
future::ok(response)
|
||||
},
|
||||
Ok(popular) =>
|
||||
future::ok(views::html::index::render(popular))
|
||||
}
|
||||
})
|
||||
self.engine.get_popular_repos()
|
||||
.join(self.engine.get_popular_crates())
|
||||
.then(move |popular_result| {
|
||||
match popular_result {
|
||||
Err(err) => {
|
||||
error!(logger, "error: {}", err);
|
||||
let mut response = views::html::error::render("Could not retrieve popular items", "");
|
||||
response.set_status(StatusCode::InternalServerError);
|
||||
future::ok(response)
|
||||
},
|
||||
Ok((popular_repos, popular_crates)) =>
|
||||
future::ok(views::html::index::render(popular_repos, popular_crates))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn repo_status(&self, _req: Request, params: Params, logger: Logger, format: StatusFormat) ->
|
||||
|
|
|
@ -2,28 +2,59 @@ use hyper::Response;
|
|||
use maud::{Markup, html};
|
||||
|
||||
use ::models::repo::Repository;
|
||||
use ::models::crates::CratePath;
|
||||
|
||||
fn popular_table(popular: Vec<Repository>) -> Markup {
|
||||
fn popular_table(popular_repos: Vec<Repository>, popular_crates: Vec<CratePath>) -> Markup {
|
||||
html! {
|
||||
h2 class="title is-3" "Popular Repositories"
|
||||
div class="columns" {
|
||||
div class="column" {
|
||||
h2 class="title is-3" "Popular Repositories"
|
||||
|
||||
table class="table is-fullwidth is-striped is-hoverable" {
|
||||
thead {
|
||||
tr {
|
||||
th "Repository"
|
||||
th class="has-text-right" "Status"
|
||||
}
|
||||
}
|
||||
tbody {
|
||||
@for repo in popular {
|
||||
tr {
|
||||
td {
|
||||
a href=(format!("{}/repo/{}/{}/{}", &super::SELF_BASE_URL as &str, repo.path.site.as_ref(), repo.path.qual.as_ref(), repo.path.name.as_ref())) {
|
||||
(format!("{} / {}", repo.path.qual.as_ref(), repo.path.name.as_ref()))
|
||||
table class="table is-fullwidth is-striped is-hoverable" {
|
||||
thead {
|
||||
tr {
|
||||
th "Repository"
|
||||
th class="has-text-right" "Status"
|
||||
}
|
||||
}
|
||||
tbody {
|
||||
@for repo in popular_repos.into_iter().take(10) {
|
||||
tr {
|
||||
td {
|
||||
a href=(format!("{}/repo/{}/{}/{}", &super::SELF_BASE_URL as &str, repo.path.site.as_ref(), repo.path.qual.as_ref(), repo.path.name.as_ref())) {
|
||||
(format!("{} / {}", repo.path.qual.as_ref(), repo.path.name.as_ref()))
|
||||
}
|
||||
}
|
||||
td class="has-text-right" {
|
||||
img src=(format!("{}/repo/{}/{}/{}/status.svg", &super::SELF_BASE_URL as &str, repo.path.site.as_ref(), repo.path.qual.as_ref(), repo.path.name.as_ref()));
|
||||
}
|
||||
}
|
||||
}
|
||||
td class="has-text-right" {
|
||||
img src=(format!("{}/repo/{}/{}/{}/status.svg", &super::SELF_BASE_URL as &str, repo.path.site.as_ref(), repo.path.qual.as_ref(), repo.path.name.as_ref()));
|
||||
}
|
||||
}
|
||||
}
|
||||
div class="column" {
|
||||
h2 class="title is-3" "Popular Crates"
|
||||
|
||||
table class="table is-fullwidth is-striped is-hoverable" {
|
||||
thead {
|
||||
tr {
|
||||
th "Crate"
|
||||
th class="has-text-right" "Status"
|
||||
}
|
||||
}
|
||||
tbody {
|
||||
@for crate_path in popular_crates {
|
||||
tr {
|
||||
td {
|
||||
a href=(format!("{}/crate/{}/{}", &super::SELF_BASE_URL as &str, crate_path.name.as_ref(), crate_path.version)) {
|
||||
(format!("{}", crate_path.name.as_ref()))
|
||||
}
|
||||
}
|
||||
td class="has-text-right" {
|
||||
img src=(format!("{}/crate/{}/{}/status.svg", &super::SELF_BASE_URL as &str, crate_path.name.as_ref(), crate_path.version));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +63,7 @@ fn popular_table(popular: Vec<Repository>) -> Markup {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn render(popular: Vec<Repository>) -> Response {
|
||||
pub fn render(popular_repos: Vec<Repository>, popular_crates: Vec<CratePath>) -> Response {
|
||||
super::render_html("Keep your dependencies up-to-date", html! {
|
||||
section class="hero is-light" {
|
||||
div class="hero-head" (super::render_navbar())
|
||||
|
@ -48,7 +79,7 @@ pub fn render(popular: Vec<Repository>) -> Response {
|
|||
}
|
||||
}
|
||||
section class="section" {
|
||||
div class="container" (popular_table(popular))
|
||||
div class="container" (popular_table(popular_repos, popular_crates))
|
||||
}
|
||||
(super::render_footer(None))
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue