mirror of
https://github.com/deps-rs/deps.rs.git
synced 2024-11-21 18:06: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