diff --git a/src/error.rs b/src/error.rs index 07845cf..55f27b2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,6 +2,8 @@ use crate::P500; use actix_web::{HttpResponse, ResponseError}; use std::fmt; +pub(crate) type Result = std::result::Result; + #[derive(Debug)] pub(crate) enum Error { Badge(String), diff --git a/src/main.rs b/src/main.rs index 49c20f4..aad2c5d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,8 +13,8 @@ mod service; use crate::{ cache::CacheState, - error::Error, - service::{Bitbucket, GitHub, Gitlab, Service}, + error::{Error, Result}, + service::{Bitbucket, FormService, GitHub, Gitlab, Service}, }; use actix_web::{ error::ErrorBadRequest, @@ -27,6 +27,7 @@ use futures::{unsync::mpsc, Stream}; use git2::Repository; use number_prefix::{NumberPrefix, Prefixed, Standalone}; use std::{ + borrow::Cow, fs::create_dir_all, path::{Path, PathBuf}, process::Command, @@ -42,6 +43,13 @@ pub struct VersionInfo<'a> { pub version: &'a str, } +#[derive(Deserialize, Serialize)] +struct GeneratorForm<'a> { + service: FormService, + user: Cow<'a, str>, + repo: Cow<'a, str>, +} + const VERSION_INFO: VersionInfo = VersionInfo { commit: env!("VERGEN_SHA_SHORT"), version: env!("CARGO_PKG_VERSION"), @@ -106,14 +114,14 @@ struct Opt { workers: usize, } -fn pull(path: impl AsRef) -> Result<(), Error> { +fn pull(path: impl AsRef) -> Result<()> { let repo = Repository::open_bare(path)?; let mut origin = repo.find_remote("origin")?; origin.fetch(&["refs/heads/*:refs/heads/*"], None, None)?; Ok(()) } -fn hoc(repo: &str, repo_dir: &str, cache_dir: &str) -> Result<(u64, String), Error> { +fn hoc(repo: &str, repo_dir: &str, cache_dir: &str) -> Result<(u64, String)> { let repo_dir = format!("{}/{}", repo_dir, repo); let cache_dir = format!("{}/{}.json", cache_dir, repo); let cache_dir = Path::new(&cache_dir); @@ -164,7 +172,7 @@ fn hoc(repo: &str, repo_dir: &str, cache_dir: &str) -> Result<(u64, String), Err s.split_whitespace() .take(2) .map(str::parse::) - .filter_map(Result::ok) + .filter_map(std::result::Result::ok) .sum::() }) .sum(); @@ -175,7 +183,7 @@ fn hoc(repo: &str, repo_dir: &str, cache_dir: &str) -> Result<(u64, String), Err Ok((cache.count, head)) } -fn remote_exists(url: &str) -> Result { +fn remote_exists(url: &str) -> Result { Ok(CLIENT.head(url).send()?.status() == reqwest::StatusCode::OK) } @@ -195,10 +203,10 @@ fn handle_hoc_request( state: web::Data>, data: web::Path<(String, String)>, mapper: F, -) -> Result +) -> Result where T: Service, - F: Fn(HocResult) -> Result, + F: Fn(HocResult) -> Result, { hoc_request::(state, data).and_then(mapper) } @@ -206,7 +214,7 @@ where fn hoc_request( state: web::Data>, data: web::Path<(String, String)>, -) -> Result { +) -> Result { let repo = format!("{}/{}", data.0.to_lowercase(), data.1.to_lowercase()); let service_path = format!("{}/{}", T::domain(), repo); let path = format!("{}/{}", state.repos, service_path); @@ -242,7 +250,7 @@ fn hoc_request( fn calculate_hoc( state: web::Data>, data: web::Path<(String, String)>, -) -> Result { +) -> Result { let mapper = |r| match r { HocResult::NotFound => Ok(p404()), HocResult::Hoc { hoc_pretty, .. } => { @@ -275,7 +283,7 @@ fn calculate_hoc( fn overview( state: web::Data>, data: web::Path<(String, String)>, -) -> Result { +) -> Result { let mapper = |r| match r { HocResult::NotFound => Ok(p404()), HocResult::Hoc { @@ -317,6 +325,26 @@ fn index() -> HttpResponse { .body(INDEX.as_slice()) } +#[post("/generate")] +fn generate(params: web::Form) -> Result { + let repo = format!("{}/{}", params.user, params.repo); + let mut buf = Vec::new(); + templates::generate( + &mut buf, + VERSION_INFO, + &OPT.domain, + params.service.url(), + params.service.service(), + &repo, + )?; + let (tx, rx_body) = mpsc::unbounded(); + let _ = tx.unbounded_send(Bytes::from(buf)); + + Ok(HttpResponse::Ok() + .content_type("text/html") + .streaming(rx_body.map_err(|_| ErrorBadRequest("bad request")))) +} + fn p404() -> HttpResponse { HttpResponse::NotFound() .content_type("text/html") @@ -343,6 +371,7 @@ fn main() -> std::io::Result<()> { .wrap(middleware::Logger::default()) .service(index) .service(css) + .service(generate) .service(web::resource("/github/{user}/{repo}").to(calculate_hoc::)) .service(web::resource("/gitlab/{user}/{repo}").to(calculate_hoc::)) .service(web::resource("/bitbucket/{user}/{repo}").to(calculate_hoc::)) diff --git a/src/service.rs b/src/service.rs index f234ff3..b9300e2 100644 --- a/src/service.rs +++ b/src/service.rs @@ -4,6 +4,34 @@ pub(crate) trait Service { fn commit_url(repo: &str, commit_ref: &str) -> String; } +#[derive(Deserialize, Serialize)] +pub(crate) enum FormService { + #[serde(rename = "github")] + GitHub, + #[serde(rename = "gitlab")] + Gitlab, + #[serde(rename = "bitbucket")] + Bitbucket, +} + +impl FormService { + pub(crate) fn url(&self) -> &str { + match self { + FormService::GitHub => "github.com", + FormService::Gitlab => "gitlab.com", + FormService::Bitbucket => "bitbucket.org", + } + } + + pub(crate) fn service(&self) -> &str { + match self { + FormService::GitHub => "github", + FormService::Gitlab => "gitlab", + FormService::Bitbucket => "bitbucket", + } + } +} + pub(crate) struct GitHub; impl Service for GitHub { diff --git a/templates/generate.rs.html b/templates/generate.rs.html new file mode 100644 index 0000000..86b2c03 --- /dev/null +++ b/templates/generate.rs.html @@ -0,0 +1,23 @@ +@use super::base; +@use crate::VersionInfo; + +@(version_info: VersionInfo, domain: &str, url: &str, service: &str, path: &str) + +@:base("Hits-of-Code Badges", "Badge Generator", { + +

+Here is the markdown for the badge for @url/@path +

+ +
+[![Hits-of-Code](https://@domain/@service/@path)](https://@domain/view/@service/@path)
+
+ +

+It will be rendered like this +

+ +
+example badge
+
+}, version_info) diff --git a/templates/index.rs.html b/templates/index.rs.html index ec004cc..adbcd91 100644 --- a/templates/index.rs.html +++ b/templates/index.rs.html @@ -45,6 +45,21 @@ would render this badge: alt="example badge" /> +

Badge Generator

+ +
+ + + + + + +
+

Source Code