mirror of
https://github.com/deps-rs/deps.rs.git
synced 2024-11-22 10:26: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::repo::{Repository, RepoPath};
|
||||||
use ::models::crates::{CrateName, CratePath, CrateRelease, AnalyzedDependencies};
|
use ::models::crates::{CrateName, CratePath, CrateRelease, AnalyzedDependencies};
|
||||||
|
|
||||||
use ::interactors::crates::QueryCrate;
|
use ::interactors::crates::{QueryCrate, GetPopularCrates};
|
||||||
use ::interactors::RetrieveFileAtPath;
|
use ::interactors::RetrieveFileAtPath;
|
||||||
use ::interactors::github::{GetPopularRepos};
|
use ::interactors::github::{GetPopularRepos};
|
||||||
|
|
||||||
|
@ -36,6 +36,7 @@ pub struct Engine {
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
|
|
||||||
query_crate: Arc<Cache<QueryCrate<HttpClient>>>,
|
query_crate: Arc<Cache<QueryCrate<HttpClient>>>,
|
||||||
|
get_popular_crates: Arc<Cache<GetPopularCrates<HttpClient>>>,
|
||||||
get_popular_repos: Arc<Cache<GetPopularRepos<HttpClient>>>,
|
get_popular_repos: Arc<Cache<GetPopularRepos<HttpClient>>>,
|
||||||
retrieve_file_at_path: Arc<RetrieveFileAtPath<HttpClient>>
|
retrieve_file_at_path: Arc<RetrieveFileAtPath<HttpClient>>
|
||||||
}
|
}
|
||||||
|
@ -43,12 +44,14 @@ pub struct Engine {
|
||||||
impl Engine {
|
impl Engine {
|
||||||
pub fn new(client: Client<HttpsConnector<HttpConnector>>, logger: Logger) -> 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 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);
|
let get_popular_repos = Cache::new(GetPopularRepos(client.clone()), Duration::from_secs(10), 1);
|
||||||
|
|
||||||
Engine {
|
Engine {
|
||||||
client: client.clone(), logger,
|
client: client.clone(), logger,
|
||||||
|
|
||||||
query_crate: Arc::new(query_crate),
|
query_crate: Arc::new(query_crate),
|
||||||
|
get_popular_crates: Arc::new(get_popular_crates),
|
||||||
get_popular_repos: Arc::new(get_popular_repos),
|
get_popular_repos: Arc::new(get_popular_repos),
|
||||||
retrieve_file_at_path: Arc::new(RetrieveFileAtPath(client))
|
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) ->
|
pub fn analyze_repo_dependencies(&self, repo_path: RepoPath) ->
|
||||||
impl Future<Item=AnalyzeDependenciesOutcome, Error=Error>
|
impl Future<Item=AnalyzeDependenciesOutcome, Error=Error>
|
||||||
{
|
{
|
||||||
|
@ -180,7 +190,8 @@ lazy_static! {
|
||||||
RepoPath::from_parts("github", "google", "xi-editor"),
|
RepoPath::from_parts("github", "google", "xi-editor"),
|
||||||
RepoPath::from_parts("github", "lk-geimfari", "awesomo"),
|
RepoPath::from_parts("github", "lk-geimfari", "awesomo"),
|
||||||
RepoPath::from_parts("github", "redox-os", "tfs"),
|
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()
|
].into_iter().collect::<Result<HashSet<_>, _>>().unwrap()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,10 @@ use tokio_service::Service;
|
||||||
use semver::{Version, VersionReq};
|
use semver::{Version, VersionReq};
|
||||||
use serde_json;
|
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_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)]
|
#[derive(Deserialize, Debug)]
|
||||||
struct RegistryPackageDep {
|
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,16 +113,18 @@ impl Server {
|
||||||
fn index(&self, _req: Request, _params: Params, logger: Logger) ->
|
fn index(&self, _req: Request, _params: Params, logger: Logger) ->
|
||||||
impl Future<Item=Response, Error=HyperError>
|
impl Future<Item=Response, Error=HyperError>
|
||||||
{
|
{
|
||||||
self.engine.get_popular_repos().then(move |popular_result| {
|
self.engine.get_popular_repos()
|
||||||
|
.join(self.engine.get_popular_crates())
|
||||||
|
.then(move |popular_result| {
|
||||||
match popular_result {
|
match popular_result {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!(logger, "error: {}", err);
|
error!(logger, "error: {}", err);
|
||||||
let mut response = views::html::error::render("Could not retrieve popular repositories", "");
|
let mut response = views::html::error::render("Could not retrieve popular items", "");
|
||||||
response.set_status(StatusCode::InternalServerError);
|
response.set_status(StatusCode::InternalServerError);
|
||||||
future::ok(response)
|
future::ok(response)
|
||||||
},
|
},
|
||||||
Ok(popular) =>
|
Ok((popular_repos, popular_crates)) =>
|
||||||
future::ok(views::html::index::render(popular))
|
future::ok(views::html::index::render(popular_repos, popular_crates))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,12 @@ use hyper::Response;
|
||||||
use maud::{Markup, html};
|
use maud::{Markup, html};
|
||||||
|
|
||||||
use ::models::repo::Repository;
|
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! {
|
html! {
|
||||||
|
div class="columns" {
|
||||||
|
div class="column" {
|
||||||
h2 class="title is-3" "Popular Repositories"
|
h2 class="title is-3" "Popular Repositories"
|
||||||
|
|
||||||
table class="table is-fullwidth is-striped is-hoverable" {
|
table class="table is-fullwidth is-striped is-hoverable" {
|
||||||
|
@ -15,7 +18,7 @@ fn popular_table(popular: Vec<Repository>) -> Markup {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tbody {
|
tbody {
|
||||||
@for repo in popular {
|
@for repo in popular_repos.into_iter().take(10) {
|
||||||
tr {
|
tr {
|
||||||
td {
|
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())) {
|
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())) {
|
||||||
|
@ -30,9 +33,37 @@ fn popular_table(popular: Vec<Repository>) -> Markup {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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! {
|
super::render_html("Keep your dependencies up-to-date", html! {
|
||||||
section class="hero is-light" {
|
section class="hero is-light" {
|
||||||
div class="hero-head" (super::render_navbar())
|
div class="hero-head" (super::render_navbar())
|
||||||
|
@ -48,7 +79,7 @@ pub fn render(popular: Vec<Repository>) -> Response {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
section class="section" {
|
section class="section" {
|
||||||
div class="container" (popular_table(popular))
|
div class="container" (popular_table(popular_repos, popular_crates))
|
||||||
}
|
}
|
||||||
(super::render_footer(None))
|
(super::render_footer(None))
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue