mirror of
https://github.com/deps-rs/deps.rs.git
synced 2024-11-22 02:16:30 +00:00
query crate versions
This commit is contained in:
parent
95228976e0
commit
f82e3d0ef6
8 changed files with 214 additions and 0 deletions
16
Cargo.toml
Normal file
16
Cargo.toml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
[package]
|
||||||
|
name = "shiny-robots"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Sam Rijs <srijs@airpost.net>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
futures = "0.1.18"
|
||||||
|
hyper = "0.11.15"
|
||||||
|
hyper-tls = "0.1.2"
|
||||||
|
serde = "1.0.27"
|
||||||
|
serde_derive = "1.0.27"
|
||||||
|
serde_json = "1.0.9"
|
||||||
|
slog = "2.1.1"
|
||||||
|
slog-json = "2.2.0"
|
||||||
|
tokio-core = "0.1.12"
|
||||||
|
tokio-service = "0.1.0"
|
74
src/main.rs
Normal file
74
src/main.rs
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
#![feature(ascii_ctype)]
|
||||||
|
#![feature(conservative_impl_trait)]
|
||||||
|
|
||||||
|
extern crate futures;
|
||||||
|
extern crate hyper;
|
||||||
|
extern crate hyper_tls;
|
||||||
|
#[macro_use] extern crate serde_derive;
|
||||||
|
extern crate serde;
|
||||||
|
extern crate serde_json;
|
||||||
|
#[macro_use] extern crate slog;
|
||||||
|
extern crate slog_json;
|
||||||
|
extern crate tokio_core;
|
||||||
|
extern crate tokio_service;
|
||||||
|
|
||||||
|
mod models;
|
||||||
|
mod robots;
|
||||||
|
mod serve;
|
||||||
|
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
use futures::{Future, Stream};
|
||||||
|
use hyper::Client;
|
||||||
|
use hyper::server::Http;
|
||||||
|
use hyper_tls::HttpsConnector;
|
||||||
|
use slog::Drain;
|
||||||
|
use tokio_core::reactor::Core;
|
||||||
|
|
||||||
|
use self::serve::Serve;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let logger = slog::Logger::root(
|
||||||
|
Mutex::new(slog_json::Json::default(std::io::stderr())).map(slog::Fuse),
|
||||||
|
o!("version" => env!("CARGO_PKG_VERSION"))
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut core = Core::new()
|
||||||
|
.expect("failed to create event loop");
|
||||||
|
|
||||||
|
let handle = core.handle();
|
||||||
|
|
||||||
|
let connector = HttpsConnector::new(4, &handle)
|
||||||
|
.expect("failed to create https connector");
|
||||||
|
|
||||||
|
let client = Client::configure()
|
||||||
|
.connector(connector)
|
||||||
|
.build(&core.handle());
|
||||||
|
|
||||||
|
let addr = "0.0.0.0:8080".parse::<SocketAddr>()
|
||||||
|
.expect("failed to parse socket addr");
|
||||||
|
|
||||||
|
let http = Http::new();
|
||||||
|
|
||||||
|
let serve_logger = logger.clone();
|
||||||
|
let serve = http.serve_addr_handle(&addr, &handle, move || {
|
||||||
|
Ok(Serve {
|
||||||
|
client: client.clone(),
|
||||||
|
logger: serve_logger.clone()
|
||||||
|
})
|
||||||
|
}).expect("failed to bind server");
|
||||||
|
|
||||||
|
let serving = serve.for_each(move |conn| {
|
||||||
|
let conn_logger = logger.clone();
|
||||||
|
handle.spawn(conn.then(move |res| {
|
||||||
|
if let Err(err) = res {
|
||||||
|
info!(conn_logger, "server connection error: {}", err)
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}));
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
core.run(serving).expect("server failed");
|
||||||
|
}
|
28
src/models/crates.rs
Normal file
28
src/models/crates.rs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
pub struct CrateName(String);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CrateNameValidationError;
|
||||||
|
|
||||||
|
impl AsRef<str> for CrateName {
|
||||||
|
fn as_ref(&self) -> &str {
|
||||||
|
self.0.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for CrateName {
|
||||||
|
type Err = CrateNameValidationError;
|
||||||
|
|
||||||
|
fn from_str(input: &str) -> Result<CrateName, CrateNameValidationError> {
|
||||||
|
let is_valid = input.chars().all(|c| {
|
||||||
|
c.is_ascii_alphanumeric() || c == '_' || c == '-'
|
||||||
|
});
|
||||||
|
|
||||||
|
if !is_valid {
|
||||||
|
Err(CrateNameValidationError)
|
||||||
|
} else {
|
||||||
|
Ok(CrateName(input.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
src/models/mod.rs
Normal file
1
src/models/mod.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub mod crates;
|
52
src/robots/crates.rs
Normal file
52
src/robots/crates.rs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
use futures::{Future, Stream, IntoFuture, future};
|
||||||
|
use hyper::{Error as HyperError, Method, Request, Response, StatusCode};
|
||||||
|
use hyper::error::UriError;
|
||||||
|
use tokio_service::Service;
|
||||||
|
use serde_json;
|
||||||
|
|
||||||
|
use ::models::crates::CrateName;
|
||||||
|
|
||||||
|
const CRATES_API_BASE_URI: &'static str = "https://crates.io/api/v1";
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct CratesVersion {
|
||||||
|
num: String,
|
||||||
|
yanked: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct QueryCratesVersionsResponse {
|
||||||
|
versions: Vec<CratesVersion>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum QueryCratesVersionsError {
|
||||||
|
Uri(UriError),
|
||||||
|
Status(StatusCode),
|
||||||
|
Transport(HyperError),
|
||||||
|
Decode(serde_json::Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn query_crates_versions<S>(service: S, crate_name: &CrateName) ->
|
||||||
|
impl Future<Item=QueryCratesVersionsResponse, Error=QueryCratesVersionsError>
|
||||||
|
where S: Service<Request=Request, Response=Response, Error=HyperError>
|
||||||
|
{
|
||||||
|
let uri_future = format!("{}/crates/{}/versions", CRATES_API_BASE_URI, crate_name.as_ref())
|
||||||
|
.parse().into_future().map_err(QueryCratesVersionsError::Uri);
|
||||||
|
|
||||||
|
uri_future.and_then(move |uri| {
|
||||||
|
let request = Request::new(Method::Get, uri);
|
||||||
|
|
||||||
|
service.call(request).map_err(QueryCratesVersionsError::Transport).and_then(|response| {
|
||||||
|
let status = response.status();
|
||||||
|
if !status.is_success() {
|
||||||
|
future::Either::A(future::err(QueryCratesVersionsError::Status(status)))
|
||||||
|
} else {
|
||||||
|
let body_future = response.body().concat2().map_err(QueryCratesVersionsError::Transport);
|
||||||
|
let decode_future = body_future
|
||||||
|
.and_then(|body| serde_json::from_slice(&body).map_err(QueryCratesVersionsError::Decode));
|
||||||
|
future::Either::B(decode_future)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
0
src/robots/github.rs
Normal file
0
src/robots/github.rs
Normal file
1
src/robots/mod.rs
Normal file
1
src/robots/mod.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub mod crates;
|
42
src/serve.rs
Normal file
42
src/serve.rs
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
use futures::{Future, future};
|
||||||
|
use hyper::{Client, Error as HyperError, Request, Response, StatusCode};
|
||||||
|
use hyper::client::HttpConnector;
|
||||||
|
use hyper_tls::HttpsConnector;
|
||||||
|
use slog::Logger;
|
||||||
|
use tokio_service::Service;
|
||||||
|
|
||||||
|
use ::robots::crates::query_crates_versions;
|
||||||
|
|
||||||
|
pub struct Serve {
|
||||||
|
pub client: Client<HttpsConnector<HttpConnector>>,
|
||||||
|
pub logger: Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Service for Serve {
|
||||||
|
type Request = Request;
|
||||||
|
type Response = Response;
|
||||||
|
type Error = HyperError;
|
||||||
|
type Future = Box<Future<Item=Response, Error=HyperError>>;
|
||||||
|
|
||||||
|
fn call(&self, req: Request) -> Self::Future {
|
||||||
|
let crate_name = "hyper".parse().unwrap();
|
||||||
|
|
||||||
|
let future = query_crates_versions(self.client.clone(), &crate_name).then(|result| {
|
||||||
|
match result {
|
||||||
|
Err(err) => {
|
||||||
|
let mut response = Response::new();
|
||||||
|
response.set_status(StatusCode::InternalServerError);
|
||||||
|
response.set_body(format!("{:?}", err));
|
||||||
|
future::Either::A(future::ok(response))
|
||||||
|
},
|
||||||
|
Ok(crates_response) => {
|
||||||
|
let mut response = Response::new();
|
||||||
|
response.set_body(format!("{:?}", crates_response));
|
||||||
|
future::Either::B(future::ok(response))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Box::new(future)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue