Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for filtering by language type #208

Merged
merged 9 commits into from
May 3, 2023
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,17 @@ Here is an example showing total number of code.
[![](https://tokei.rs/b1/github/XAMPPRocky/tokei?category=code)](https://github.com/XAMPPRocky/tokei).
```

## Type

You can choose to count lines only for specific language type(s), by using the `?type=` query
string. Languages are to be separated by a comma.
Here is an example showing total number of lines for JSON, Rust, and Markdown.
[![lines of json, rust, and markdown](https://tokei.rs/b1/github/XAMPPRocky/tokei?type=JSON,Rust,Markdown)](https://github.com/XAMPPRocky/tokei).

```sh
[![](https://tokei.rs/b1/github/XAMPPRocky/tokei?type=JSON,Rust,Markdown)](https://github.com/XAMPPRocky/tokei).
```

## Label

You can customize the badge label by using the `?label=` query string. For example, [![custom label](https://tokei.rs/b1/github/XAMPPRocky/tokei?category=code&label=custom%20label)](https://github.com/XAMPPRocky/tokei).
Expand Down
49 changes: 34 additions & 15 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ use actix_web::{
},
web, App, HttpRequest, HttpResponse, HttpServer,
};
use cached::Cached;
use cached::{Cached, Return};
use csscolorparser::parse;
use once_cell::sync::Lazy;
use rsbadges::{Badge, Style};
use std::collections::HashSet;
use tempfile::TempDir;
use tokei::{Language, Languages};
use tokei::{Language, LanguageType, Languages};

const BILLION: usize = 1_000_000_000;
const BLANKS: &str = "blank lines";
Expand Down Expand Up @@ -88,6 +89,7 @@ struct BadgeQuery {
style: Option<String>,
color: Option<String>,
logo: Option<String>,
r#type: Option<String>,
}

#[get("/b1/{domain}/{user}/{repo}")]
Expand All @@ -105,6 +107,7 @@ async fn create_badge(
let style: String = query.style.unwrap_or_else(|| "plastic".to_owned());
let color: String = query.color.unwrap_or_else(|| BLUE.to_owned());
let logo: String = query.logo.unwrap_or_else(|| "".to_owned());
let r#type: String = query.r#type.unwrap_or_else(|| "".to_owned());

let content_type = if let Ok(accept) = Accept::parse(&request) {
if accept == Accept::json() {
Expand Down Expand Up @@ -152,18 +155,33 @@ async fn create_badge(
}
}

let entry = get_statistics(&url, &sha).map_err(actix_web::error::ErrorBadRequest)?;
let entry: Return<Vec<(LanguageType, Language)>> =
get_statistics(&url, &sha).map_err(actix_web::error::ErrorBadRequest)?;

if entry.was_cached {
log::info!("{}#{} Cache hit", url, sha);
}

let stats = entry.value;
let language_types: HashSet<LanguageType> = r#type
.split(',')
.filter_map(|s| str::parse::<LanguageType>(s).ok())
.into_iter()
.collect::<HashSet<LanguageType>>();

let mut stats = Language::new();
let languages: Vec<(LanguageType, Language)> = entry.value;

for (language_type, language) in &languages {
if language_types.is_empty() || language_types.contains(&language_type) {
stats += language.clone();
}
}

log::info!(
"{url}#{sha} - Lines {lines} Code {code} Comments {comments} Blanks {blanks}",
"{url}#{sha} - Languages {languages:#?} Lines {lines} Code {code} Comments {comments} Blanks {blanks}",
url = url,
sha = sha,
languages = languages,
lines = stats.lines(),
code = stats.code,
comments = stats.comments,
Expand Down Expand Up @@ -192,11 +210,14 @@ fn repo_identifier(url: &str, sha: &str) -> String {
name = "CACHE",
result = true,
with_cached_flag = true,
type = "cached::TimedSizedCache<String, cached::Return<Language>>",
type = "cached::TimedSizedCache<String, cached::Return<Vec<(LanguageType,Language)>>>",
create = "{ cached::TimedSizedCache::with_size_and_lifespan(1000, DAY_IN_SECONDS) }",
convert = r#"{ repo_identifier(url, _sha) }"#
)]
fn get_statistics(url: &str, _sha: &str) -> eyre::Result<cached::Return<Language>> {
fn get_statistics(
url: &str,
_sha: &str,
) -> eyre::Result<cached::Return<Vec<(LanguageType, Language)>>> {
log::info!("{} - Cloning", url);
let temp_dir = TempDir::new()?;
let temp_path = temp_dir.path().to_str().unwrap();
Expand All @@ -205,20 +226,18 @@ fn get_statistics(url: &str, _sha: &str) -> eyre::Result<cached::Return<Language
.args(["clone", url, temp_path, "--depth", "1"])
.output()?;

let mut stats = Language::new();
let mut languages = Languages::new();
log::info!("{} - Getting Statistics", url);
languages.get_statistics(&[temp_path], &[], &tokei::Config::default());

for (_, language) in languages {
stats += language;
}

for stat in &mut stats.reports {
stat.name = stat.name.strip_prefix(temp_path)?.to_owned();
let mut iter = languages.iter_mut();
while let Some((_, language)) = iter.next() {
for report in &mut language.reports {
report.name = report.name.strip_prefix(temp_path)?.to_owned();
}
}

Ok(cached::Return::new(stats))
Ok(cached::Return::new(languages.into_iter().collect()))
}

fn trim_and_float(num: usize, trim: usize) -> f64 {
Expand Down