Viewing:
// colors from https://raw.githubusercontent.com/ozh/github-colors/master/colors.json
use anyhow::{anyhow, Result};
use git2::Repository;
use lazy_static::lazy_static;
use log::warn;
use std::collections::{HashMap, HashSet};
use std::io::ErrorKind;
use tokei::{Config as TokeiConfig, LanguageType, Languages};
lazy_static! {
pub static ref LANGUAGE_COLORS: HashMap<LanguageType, &'static str> = HashMap::from([
(LanguageType::Abap, "#E8274B"),
(LanguageType::ActionScript, "#882B0F#"),
(LanguageType::Ada, "#02f88c"),
(LanguageType::Agda, "#315665"),
(LanguageType::Alex, "#123456"), // Find real color
(LanguageType::Alloy, "#64C800"),
(LanguageType::Arduino, "#123456"), // Find real color
(LanguageType::AsciiDoc, "#73a0c5"),
(LanguageType::Asn1, "#123456"), // Find real color
(LanguageType::Asp, "#123456"), // Find real color
(LanguageType::AspNet, "#9400ff"),
(LanguageType::Assembly, "#6E4C13"),
(LanguageType::AssemblyGAS, "#123456"), // Find real color
(LanguageType::Ats, "#1ac620"),
(LanguageType::AutoHotKey, "#6594b9"),
(LanguageType::Autoit, "#1C3552"),
(LanguageType::Automake, "#123456"), // Find real color
// (LanguageType::Awk, "#c30e9b"),
(LanguageType::Bash, "#89e051"),
(LanguageType::Batch, "#C1F12E"),
(LanguageType::Bean, "#123456"), // Find real color
(LanguageType::Bitbake, "#00bce4"),
(LanguageType::BrightScript, "#662D91"),
(LanguageType::C, "#555555"),
(LanguageType::Cabal, "#483465"),
(LanguageType::Cassius, "#123456"), // Find real color
(LanguageType::Ceylon, "#dfa535"),
(LanguageType::CHeader, "#123456"), // Find real color
(LanguageType::Clojure, "#db5855"),
(LanguageType::ClojureC, "#123456"), // Find real color
(LanguageType::ClojureScript, "#123456"), // Find real color
(LanguageType::CMake, "#DA3434"),
(LanguageType::Cobol, "#123456"), // Find real color
(LanguageType::CodeQL, "#140f46"),
(LanguageType::CoffeeScript, "#244776"),
(LanguageType::Cogent, "#123456"), // Find real color
(LanguageType::ColdFusion, "#ed2cd6"),
(LanguageType::ColdFusionScript, "#ed2cd6"),
(LanguageType::Coq, "#d0b68c"),
(LanguageType::Cpp, "#f34b7d"),
(LanguageType::CppHeader, "#123456"), // Find real color
(LanguageType::Crystal, "#000100"),
(LanguageType::CSharp, "#178600"),
(LanguageType::CShell, "#123456"), // Find real color
(LanguageType::Css, "#563d7c"),
// (LanguageType::CUDA, "#3A4E3A"),
(LanguageType::Cython, "#fedf5b"),
(LanguageType::D, "#ba595e"),
(LanguageType::Daml, "#123456"), // Find real color
(LanguageType::Dart, "#00B4AB"),
(LanguageType::DeviceTree, "#123456"), // Find real color
(LanguageType::Dhall, "#dfafff"),
(LanguageType::Dockerfile, "#384d54"),
(LanguageType::DotNetResource, "#123456"), // Find real color
(LanguageType::DreamMaker, "#123456"), // Find real color
(LanguageType::Dust, "#123456"), // Find real color
(LanguageType::Ebuild, "#9400ff"),
(LanguageType::Edn, "#123456"), // Find real color
(LanguageType::Elisp, "#c065db"),
(LanguageType::Elixir, "#6e4a7e"),
(LanguageType::Elm, "#60B5CC"),
(LanguageType::Elvish, "#55BB55"),
(LanguageType::EmacsDevEnv, "#123456"), // Find real color
(LanguageType::Emojicode, "#123456"), // Find real color
(LanguageType::Erlang, "#B83998"),
(LanguageType::Factor, "#636746"),
(LanguageType::FEN, "#123456"), // Find real color
(LanguageType::Fennel, "#fff3d7"),
(LanguageType::Fish, "#4aae47"),
(LanguageType::FlatBuffers, "#123456"), // Find real color
(LanguageType::ForgeConfig, "#123456"), // Find real color
(LanguageType::Forth, "#341708"),
(LanguageType::FortranLegacy, "#4d41b1"),
(LanguageType::FortranModern, "#4d41b1"),
(LanguageType::FreeMarker, "#0050b2"),
(LanguageType::FSharp, "#b845fc"),
(LanguageType::Fstar, "#572e30"),
(LanguageType::Futhark, "#5f021f"),
(LanguageType::GDB, "#123456"), // Find real color
(LanguageType::GdScript, "#355570"),
(LanguageType::Gherkin, "#5B2063"),
(LanguageType::Gleam, "#ffaff3"),
(LanguageType::Glsl, "#5686a5"),
(LanguageType::Gml, "#123456"), // Find real color
(LanguageType::Go, "#00ADD8"),
(LanguageType::Gohtml, "#123456"), // Find real color
(LanguageType::Graphql, "#e10098"),
(LanguageType::Groovy, "#4298b8"),
(LanguageType::Gwion, "#123456"), // Find real color
(LanguageType::Haml, "#ece2a9"),
(LanguageType::Hamlet, "#123456"), // Find real color
(LanguageType::Happy, "#123456"), // Find real color
(LanguageType::Handlebars, "#f7931e"),
(LanguageType::Haskell, "#5e5086"),
(LanguageType::Haxe, "#df7900"),
(LanguageType::Hcl, "#844FBA"),
(LanguageType::Hlsl, "#aace60"),
(LanguageType::HolyC, "#ffefaf"),
(LanguageType::Html, "#e34c26"),
(LanguageType::Hy, "#7790B2"),
(LanguageType::Idris, "#b30000"),
(LanguageType::Ini, "#d1dbe0"),
(LanguageType::Isabelle, "#FEFE00"),
(LanguageType::Jai, "#123456"), // Find real color
(LanguageType::Java, "#b07219"),
(LanguageType::JavaScript, "#f1e05a"),
(LanguageType::Jinja2, "#a52a22"),
(LanguageType::Json, "#292929"),
(LanguageType::Jsonnet, "#0064bd"),
(LanguageType::Jsx, "#f1e05a"),
(LanguageType::Julia, "#a270ba"),
(LanguageType::Julius, "#123456"), // Find real color
(LanguageType::Jupyter, "#DA5B0B"),
(LanguageType::K, "#123456"), // Find real color
(LanguageType::KakouneScript, "#6f8042"),
(LanguageType::Kotlin, "#A97BFF"),
(LanguageType::Ksh, "#123456"), // Find real color
(LanguageType::KvLanguage, "#1da6e0"),
(LanguageType::Lean, "#123456"), // Find real color
(LanguageType::Less, "#1d365d"),
(LanguageType::Liquid, "#67b8de"),
(LanguageType::LinkerScript, "#123456"), // Find real color
(LanguageType::Lisp, "#123456"), // Find real color
(LanguageType::LiveScript, "#499886"),
(LanguageType::LLVM, "#185619"),
(LanguageType::Logtalk, "#295b9a"),
(LanguageType::LolCode, "#cc9900"),
(LanguageType::Lua, "#000080"),
(LanguageType::Lucius, "#123456"), // Find real color
(LanguageType::M4, "#123456"), // Find real color
(LanguageType::Madlang, "#123456"), // Find real color
(LanguageType::Makefile, "#427819"),
(LanguageType::Markdown, "#083fa1"),
(LanguageType::Meson, "#007800"),
(LanguageType::Metal, "#8f14e9"),
(LanguageType::Mint, "#02b046"),
(LanguageType::Mlatu, "#123456"), // Find real color
(LanguageType::ModuleDef, "#123456"), // Find real color
(LanguageType::MoonScript, "#ff4585"),
(LanguageType::MsBuild, "#123456"), // Find real color
(LanguageType::Mustache, "#724b3b"),
(LanguageType::Nextflow, "#3ac486"),
(LanguageType::Nim, "#ffc200"),
(LanguageType::Nix, "#7e7eff"),
(LanguageType::NotQuitePerl, "#123456"), // Find real color
(LanguageType::ObjectiveC, "#438eff"),
(LanguageType::ObjectiveCpp, "#6866fb"),
(LanguageType::OCaml, "#3be133"),
(LanguageType::Odin, "#60AFFE"),
(LanguageType::OpenPolicyAgent, "#7d9199"),
(LanguageType::OpenType, "#123456"), // Find real color
(LanguageType::Org, "#77aa99"),
(LanguageType::Oz, "#fab738"),
(LanguageType::Pan, "#cc0000"),
(LanguageType::Pascal, "#E3F171"),
(LanguageType::Perl, "#0298c3"),
(LanguageType::Pest, "#123456"), // Find real color
(LanguageType::Php, "#4F5D95"),
(LanguageType::Poke, "#123456"), // Find real color
(LanguageType::Polly, "#123456"), // Find real color
(LanguageType::Pony, "#123456"), // Find real color
(LanguageType::PostCss, "#dc3a0c"),
(LanguageType::PowerShell, "#012456"),
(LanguageType::Processing, "#0096D8"),
(LanguageType::PSL, "#123456"), // Find real color
(LanguageType::Prolog, "#74283c"),
(LanguageType::Protobuf, "#123456"), // Find real color
(LanguageType::Pug, "#a86454"),
(LanguageType::Puppet, "#302B6D"),
(LanguageType::PureScript, "#1D222D"),
(LanguageType::Python, "#3572A5"),
(LanguageType::Q, "#0040cd"),
(LanguageType::Qcl, "#123456"), // Find real color
(LanguageType::Qml, "#44a51c"),
(LanguageType::R, "#198CE7"),
(LanguageType::Racket, "#3c5caa"),
(LanguageType::Rakefile, "#123456"), // Find real color
(LanguageType::Raku, "#0000fb"),
(LanguageType::Razor, "#123456"), // Find real color
(LanguageType::Renpy, "#ff7f7f"),
(LanguageType::ReScript, "#ed5051"),
(LanguageType::ReStructuredText, "#141414"),
(LanguageType::RON, "#123456"), // Find real color
(LanguageType::RPMSpecfile, "#123456"), // Find real color
(LanguageType::Ruby, "#701516"),
(LanguageType::RubyHtml, "#123456"), // Find real color
(LanguageType::Rust, "#dea584"),
(LanguageType::Sass, "#a53b70"),
(LanguageType::Scala, "#c22d40"),
(LanguageType::Scheme, "#1e4aec"),
(LanguageType::Scons, "#123456"), // Find real color
(LanguageType::Sh, "#89e051"),
(LanguageType::ShaderLab, "#222c37"),
(LanguageType::Solidity, "#AA6746"),
(LanguageType::SpecmanE, "#123456"), // Find real color
(LanguageType::Spice, "#123456"), // Find real color
(LanguageType::Sql, "#e38c00"),
(LanguageType::Sqf, "#3F3F3F"),
(LanguageType::SRecode, "#348a34"),
(LanguageType::Stan, "#b2011d"),
(LanguageType::Stratego, "#123456"), // Find real color
(LanguageType::Stylus, "#ff6347"),
(LanguageType::Svelte, "#ff3e00"),
(LanguageType::Svg, "#ff9900"),
(LanguageType::Swift, "#F0513"),
(LanguageType::Swig, "#123456"), // Find real color
(LanguageType::SystemVerilog, "#DAE1C2"),
(LanguageType::Tcl, "#e4cc98"),
(LanguageType::Tera, "#00004c"),
(LanguageType::Tex, "#3D6117"),
(LanguageType::Text, "#123456"), // Find real color
(LanguageType::Thrift, "#D12127"),
(LanguageType::Toml, "#9c4221"),
(LanguageType::Tsx, "#3178c6"),
(LanguageType::Ttcn, "#123456"), // Find real color
(LanguageType::Twig, "#c1d026"),
(LanguageType::TypeScript, "#3178c6"),
(LanguageType::UMPL, "#123456"), // Find real color
(LanguageType::Unison, "#123456"), // Find real color
(LanguageType::UnrealDeveloperMarkdown, "#123456"), // Find real color
(LanguageType::UnrealPlugin, "#123456"), // Find real color
(LanguageType::UnrealProject, "#123456"), // Find real color
(LanguageType::UnrealScript, "#a54c4d"),
(LanguageType::UnrealShader, "#123456"), // Find real color
(LanguageType::UnrealShaderHeader, "#123456"), // Find real color
(LanguageType::UrWeb, "#ccccee"),
(LanguageType::UrWebProject, "#123456"), // Find real color
(LanguageType::Vala, "#a56de2"),
(LanguageType::VB6, "#2c6353"),
(LanguageType::VBScript, "#15dcdc"),
(LanguageType::Velocity, "#507cff"),
(LanguageType::Verilog, "#b2b7f8"),
(LanguageType::VerilogArgsFile, "#123456"), // Find real color
(LanguageType::Vhdl, "#adb2cb"),
(LanguageType::VisualBasic, "#123456"), // Find real color
(LanguageType::VisualStudioProject, "#123456"), // Find real color
(LanguageType::VisualStudioSolution, "#123456"), // Find real color
(LanguageType::VimScript, "#199f4b"),
(LanguageType::Vue, "#41b883"),
(LanguageType::WebAssembly, "#04133b"),
(LanguageType::WenYan, "#123456"), // Find real color
(LanguageType::WGSL, "#123456"), // Find real color
(LanguageType::Wolfram, "#123456"), // Find real color
(LanguageType::Xaml, "#123456"), // Find real color
(LanguageType::XcodeConfig, "#123456"), // Find real color
(LanguageType::Xml, "#0060ac"),
(LanguageType::XSL, "#EB8CEB"),
(LanguageType::Xtend, "#24255d"),
(LanguageType::Yaml, "#cb171e"),
(LanguageType::ZenCode, "#00BCD1"),
(LanguageType::Zig, "#ec915c"),
(LanguageType::Zsh, "#123456"), // Find real color
]);
pub static ref DATA_LANGUAGES: HashSet<LanguageType> = vec![
LanguageType::Text,
LanguageType::Json,
LanguageType::RON,
LanguageType::Xml,
LanguageType::Yaml,
LanguageType::Toml,
]
.into_iter()
.collect();
pub static ref IGNORED_COUNT_EXTENTIONS: HashSet<&'static str> = vec![
".lock",
".mod",
".sum",
".yml",
".yaml",
".txt",
".ron",
".json",
".toml",
".xml",
// other non-code data files
".svg",
".pub",
".service",
// binary files
".ico",
".jpg",
".jpeg",
".webm",
".mp4",
".wav",
".pdf",
".ttf",
// not understood by tokei yet:
".kdl",
// e.g. typst
".typ",
].into_iter().collect();
}
#[derive(Clone, Debug)]
pub struct LanguageStats {
pub name: LanguageType,
pub total: usize,
pub percentage: f64,
}
#[derive(Clone, Debug)]
pub struct RepoLanguageStats {
pub languages: Vec<LanguageStats>,
pub total_loc: usize,
}
pub fn create_worktree(repo: &Repository, username: &str, repo_path: &str) -> Result<String> {
let head = repo.head()?;
let tree = head.peel_to_tree()?;
let tmpfs_mount = format!("/tmp/lilgit_{}_{}", username, repo_path);
let tmpfs_mount_clone = tmpfs_mount.clone();
// want to ignore error
match std::fs::remove_dir_all(&tmpfs_mount) {
Err(x) => {
if x.kind() != ErrorKind::NotFound {
return Err(anyhow!("unable to delete temp directory: {}", x));
};
}
_ => (),
};
std::fs::create_dir_all(&tmpfs_mount)?;
let mut i = 0;
tree.walk(
git2::TreeWalkMode::PreOrder,
move |parent_directory, entry| {
i += 1;
let kind = match entry.kind() {
Some(x) => x,
_ => return git2::TreeWalkResult::Skip,
};
let is_dir = kind == git2::ObjectType::Tree;
let entry_name = match entry.name() {
Some(x) => x,
_ => return git2::TreeWalkResult::Ok,
};
if kind != git2::ObjectType::Tree && kind != git2::ObjectType::Blob {
return git2::TreeWalkResult::Skip;
};
let file_path = format!(
"{}{}{}",
parent_directory,
entry_name,
if is_dir { "/" } else { "" }
);
if is_dir {
return git2::TreeWalkResult::Ok;
}
let extension: String = format!(
".{}",
std::path::Path::new(entry_name)
.extension()
.unwrap_or_default()
.to_str()
.unwrap_or_default()
);
if IGNORED_COUNT_EXTENTIONS.contains(&*extension) {
return git2::TreeWalkResult::Ok;
}
if let Some(blob) = entry.to_object(repo).unwrap().as_blob() {
// not going to code count a binary anyways
if blob.is_binary() {
return git2::TreeWalkResult::Ok;
}
let extension_for_writing = if &extension == "." { "" } else { &extension };
let write_path = format!("{}/{}{}", tmpfs_mount, i, extension_for_writing);
if let Err(err) = std::fs::write(write_path, blob.content()) {
warn!("err writing write_path {:?} {:?}", err, file_path);
};
return git2::TreeWalkResult::Ok;
}
// blob.content is byte slice, can be written to file.
// if let Ok(blob_str) = str::from_utf8(blob.content()) {
git2::TreeWalkResult::Ok
},
)?;
return Ok(tmpfs_mount_clone);
}
pub fn get_repo_languages(
repo: &Repository,
username: &str,
repo_path: &str,
) -> Result<RepoLanguageStats> {
// i.e. languages we are not interesting in counting for code statistics
if repo_path.contains("/") {
return Err(anyhow!("repo has illegal character"));
}
if repo_path.contains("\\") {
return Err(anyhow!("repo has illegal character"));
}
let mut statistics = Languages::new();
let config = TokeiConfig::default();
let worktree_path = create_worktree(repo, username, repo_path)?;
statistics.get_statistics(
&[&worktree_path],
&IGNORED_COUNT_EXTENTIONS
.iter()
.map(|x| x.clone())
.collect::<Vec<_>>(),
&config,
);
let mut totals: Vec<_> = statistics
.into_iter()
.filter(|(name, _)| !DATA_LANGUAGES.contains(name))
.map(|(name, stats)| {
let total = stats.lines();
LanguageStats {
name,
total,
percentage: 0.,
}
})
.collect();
totals.sort_by(|a, b| b.total.cmp(&a.total));
let repo_loc = totals.iter().fold(0, |acc, curr| acc + curr.total);
Ok(RepoLanguageStats {
total_loc: repo_loc,
languages: totals
.into_iter()
.map(|x| LanguageStats {
percentage: (x.total as f64) / (repo_loc as f64),
..x
})
.collect(),
})
}