diff --git a/src/interactors/github.rs b/src/interactors/github.rs index 363605b..d97e321 100644 --- a/src/interactors/github.rs +++ b/src/interactors/github.rs @@ -11,46 +11,14 @@ use ::models::repo::{Repository, RepoPath}; const GITHUB_API_BASE_URI: &'static str = "https://api.github.com"; const GITHUB_USER_CONTENT_BASE_URI: &'static str = "https://raw.githubusercontent.com"; -#[derive(Debug, Clone)] -pub struct RetrieveFileAtPath(pub S); - -impl Service for RetrieveFileAtPath - where S: Service + Clone + 'static, - S::Future: 'static -{ - type Request = (RepoPath, RelativePathBuf); - type Response = String; - type Error = Error; - type Future = Box>; - - fn call(&self, req: Self::Request) -> Self::Future { - let service = self.0.clone(); - - let (repo_path, path) = req; - let path_str: &str = path.as_ref(); - let uri_future = format!("{}/{}/{}/HEAD/{}", - GITHUB_USER_CONTENT_BASE_URI, - repo_path.qual.as_ref(), - repo_path.name.as_ref(), - path_str - ).parse::().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| String::from_utf8(body.to_vec()).map_err(|err| err.into())); - future::Either::B(decode_future) - } - }) - })) - } +pub fn get_manifest_uri(repo_path: &RepoPath, path: &RelativePathBuf) -> Result { + let path_str: &str = path.as_ref(); + Ok(format!("{}/{}/{}/HEAD/{}", + GITHUB_USER_CONTENT_BASE_URI, + repo_path.qual.as_ref(), + repo_path.name.as_ref(), + path_str + ).parse::()?) } #[derive(Deserialize)] diff --git a/src/interactors/gitlab.rs b/src/interactors/gitlab.rs new file mode 100644 index 0000000..6947563 --- /dev/null +++ b/src/interactors/gitlab.rs @@ -0,0 +1,23 @@ +use hyper::Uri; +use relative_path::RelativePathBuf; + +use ::models::repo::RepoPath; + +const GITLAB_USER_CONTENT_BASE_URI: &'static str = "https://gitlab.com"; + +pub fn get_manifest_uri(repo_path: &RepoPath, path: &RelativePathBuf) -> Result { + let path_str: &str = path.as_ref(); + // gitlab will return a 308 if the Uri ends with, say, `.../raw/master//Cargo.toml`, so make + // sure that last slash isn't doubled + let slash_path = if path_str.starts_with("/") { + &path_str[1..] + } else { + path_str + }; + format!("{}/{}/{}/raw/master/{}", + GITLAB_USER_CONTENT_BASE_URI, + repo_path.qual.as_ref(), + repo_path.name.as_ref(), + slash_path + ).parse::() +} diff --git a/src/interactors/mod.rs b/src/interactors/mod.rs index bccf822..3224f8f 100644 --- a/src/interactors/mod.rs +++ b/src/interactors/mod.rs @@ -1,2 +1,58 @@ +use failure::Error; +use futures::{Future, IntoFuture, Stream, future}; +use hyper::{Error as HyperError, Method, Request, Response}; +use relative_path::RelativePathBuf; +use tokio_service::Service; + +use ::models::repo::{RepoSite, RepoPath}; + pub mod crates; pub mod github; +pub mod gitlab; + + +#[derive(Debug, Clone)] +pub struct RetrieveFileAtPath(pub S); + +impl Service for RetrieveFileAtPath + where S: Service + Clone + 'static, + S::Future: 'static +{ + type Request = (RepoPath, RelativePathBuf); + type Response = String; + type Error = Error; + type Future = Box>; + + fn call(&self, req: Self::Request) -> Self::Future { + let service = self.0.clone(); + + let (repo_path, path) = req; + let uri = match &repo_path.site { + &RepoSite::Github => { + github::get_manifest_uri(&repo_path, &path) + }, + &RepoSite::Gitlab => { + gitlab::get_manifest_uri(&repo_path, &path) + }, + }; + let uri_future = 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| String::from_utf8(body.to_vec()).map_err(|err| err.into())); + future::Either::B(decode_future) + } + }) + })) + } +} + +