Skip to content

Commit

Permalink
Add support for filtering by language type (#208)
Browse files Browse the repository at this point in the history
* Add support for filtering by language type.

* Add some comments.

* Make caching insensitive to duplicate languages.

* Make language_types immutable

* Cache only git SHA

* Revert naming of sha_tag

* Log should display only languages that exist in repo

* Remove inaccurate statement
  • Loading branch information
elliotwutingfeng authored May 3, 2023
1 parent d83446c commit f97d65b
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 15 deletions.
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

0 comments on commit f97d65b

Please sign in to comment.