Started framework detection
This commit is contained in:
parent
9d19cdd5f8
commit
16e9c422a8
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -2390,6 +2390,8 @@ dependencies = [
|
|||||||
"directories",
|
"directories",
|
||||||
"gio",
|
"gio",
|
||||||
"glib",
|
"glib",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"tokei",
|
"tokei",
|
||||||
"tokio",
|
"tokio",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
|
8
Makefile
8
Makefile
@ -156,10 +156,4 @@ docs: ## Build and open documentation
|
|||||||
# Development tools installation
|
# Development tools installation
|
||||||
tools: ## Install useful development tools
|
tools: ## Install useful development tools
|
||||||
cargo install cargo-watch cargo-audit cargo-machete cargo-flamegraph cargo-deb cargo-outdated
|
cargo install cargo-watch cargo-audit cargo-machete cargo-flamegraph cargo-deb cargo-outdated
|
||||||
@echo "Development tools installed!"
|
@echo "Development tools installed!"
|
||||||
|
|
||||||
nix-install:
|
|
||||||
nix profile install .
|
|
||||||
|
|
||||||
nix-reinstall:
|
|
||||||
nix profile install --reinstall .
|
|
@ -19,6 +19,8 @@ tokio = { version = "1.0", features = [
|
|||||||
] }
|
] }
|
||||||
walkdir = "2.5.0"
|
walkdir = "2.5.0"
|
||||||
tokei = "12.1.2"
|
tokei = "12.1.2"
|
||||||
|
serde = "1.0.219"
|
||||||
|
serde_json = "1.0.143"
|
||||||
|
|
||||||
[lints.rust]
|
[lints.rust]
|
||||||
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(rust_analyzer)'] }
|
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(rust_analyzer)'] }
|
||||||
|
@ -1,46 +1,33 @@
|
|||||||
use std::{collections::BTreeMap, path::PathBuf};
|
use std::{collections::BTreeMap, path::PathBuf};
|
||||||
use tokei::{Config, LanguageType, Languages};
|
use tokei::{Config, LanguageType, Languages};
|
||||||
|
use waycast_plugins::projects::{
|
||||||
fn lang_breakdown(paths: &[&str], excluded: &[&str]) -> Vec<(LanguageType, usize, f64)> {
|
framework_detector::{self, FrameworkDetector},
|
||||||
let mut langs = Languages::new();
|
type_scanner::TypeScanner,
|
||||||
let cfg = Config::default();
|
};
|
||||||
langs.get_statistics(paths, excluded, &cfg);
|
|
||||||
|
|
||||||
let total_code: usize = langs.iter().map(|(_, l)| l.code).sum();
|
|
||||||
let mut rows: Vec<_> = langs
|
|
||||||
.iter()
|
|
||||||
.map(|(lt, l)| {
|
|
||||||
(
|
|
||||||
*lt,
|
|
||||||
l.code,
|
|
||||||
if total_code > 0 {
|
|
||||||
(l.code as f64) * 100.0 / (total_code as f64)
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
rows.sort_by_key(|(_, lines, _)| std::cmp::Reverse(*lines));
|
|
||||||
rows
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
|
let scanner = TypeScanner::new();
|
||||||
|
let framework_detector = FrameworkDetector::new();
|
||||||
if let Ok(entries) = std::fs::read_dir(PathBuf::from("/home/javi/projects")) {
|
if let Ok(entries) = std::fs::read_dir(PathBuf::from("/home/javi/projects")) {
|
||||||
for e in entries
|
for e in entries
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|e| e.is_ok())
|
.filter(|e| e.is_ok())
|
||||||
.map(|e| e.unwrap())
|
.map(|e| e.unwrap())
|
||||||
|
.filter(|e| e.path().is_dir())
|
||||||
{
|
{
|
||||||
let langs = lang_breakdown(&[e.path().to_str().unwrap()], &[]);
|
let fw = framework_detector.detect(e.path().to_string_lossy().to_string().as_str());
|
||||||
|
|
||||||
let top = langs
|
if let Some(name) = fw {
|
||||||
.iter()
|
println!("{}: {}", e.path().display(), name);
|
||||||
.map(|(l, _, _)| l.to_owned())
|
} else {
|
||||||
.take(3)
|
println!("{}: {}", e.path().display(), "NONE");
|
||||||
.collect::<Vec<LanguageType>>();
|
}
|
||||||
|
// let langs = scanner.scan(e.path(), Some(3));
|
||||||
|
// // let langs = lang_breakdown(&[e.path().to_str().unwrap()], &[]);
|
||||||
|
|
||||||
println!("{}: {:?}", e.path().display(), top);
|
// let top: Vec<String> = langs.iter().map(|l| l.name.to_owned()).collect();
|
||||||
|
|
||||||
|
// println!("{}: {:?}", e.path().display(), top);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
84
waycast-plugins/src/projects/framework_detector.rs
Normal file
84
waycast-plugins/src/projects/framework_detector.rs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
pub enum Framework {
|
||||||
|
Laravel,
|
||||||
|
Rails,
|
||||||
|
Vue,
|
||||||
|
NextJS,
|
||||||
|
Ansible,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_file<P: AsRef<Path>>(project_path: P, file: P) -> bool {
|
||||||
|
PathBuf::from(project_path.as_ref()).join(file).exists()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_json_config<P: AsRef<Path>>(project_path: P, file: P) -> Option<serde_json::Value> {
|
||||||
|
let pb = PathBuf::from(project_path.as_ref());
|
||||||
|
if let Ok(text) = std::fs::read_to_string(pb.join(file)) {
|
||||||
|
if let Ok(v) = serde_json::from_str::<serde_json::Value>(&text) {
|
||||||
|
return Some(v);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
trait FrameworkHeuristics: Sync {
|
||||||
|
fn name(&self) -> &'static str;
|
||||||
|
fn matches(&self, project_path: &str) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Laravel;
|
||||||
|
impl FrameworkHeuristics for Laravel {
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"Laravel"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn matches(&self, project_path: &str) -> bool {
|
||||||
|
// Check for composer.json
|
||||||
|
if !has_file(project_path, "composer.json") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If composer.json has "laravel/framework"
|
||||||
|
// we can say yes immediately
|
||||||
|
if let Some(cfg) = read_json_config(project_path, "composer.json") {
|
||||||
|
let requires_laravel = cfg
|
||||||
|
.get("require")
|
||||||
|
.and_then(|r| r.get("laravel/framework"))
|
||||||
|
.is_some();
|
||||||
|
if requires_laravel {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FrameworkDetector {
|
||||||
|
heuristics: &'static [&'static dyn FrameworkHeuristics],
|
||||||
|
}
|
||||||
|
|
||||||
|
static LARAVEL: Laravel = Laravel;
|
||||||
|
static HEURISTICS: &[&dyn FrameworkHeuristics] = &[&LARAVEL];
|
||||||
|
|
||||||
|
impl FrameworkDetector {
|
||||||
|
pub fn new() -> FrameworkDetector {
|
||||||
|
FrameworkDetector {
|
||||||
|
heuristics: HEURISTICS,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn detect(&self, project_path: &str) -> Option<String> {
|
||||||
|
for h in self.heuristics {
|
||||||
|
if h.matches(project_path) {
|
||||||
|
return Some(String::from(h.name()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,5 @@
|
|||||||
// TODO: Use the user's preferred editor.
|
pub mod framework_detector;
|
||||||
// This should just be in the config when I implement
|
pub mod type_scanner;
|
||||||
// that eventually since figuring out every editor's
|
|
||||||
// launch option would be a pain. The user can just
|
|
||||||
// configure launch_command and pass a parameter.
|
|
||||||
// Example: code -n {path}
|
|
||||||
// and I'll just regex in the path.
|
|
||||||
// TODO: Project type detection and icon
|
// TODO: Project type detection and icon
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashSet,
|
collections::HashSet,
|
63
waycast-plugins/src/projects/type_scanner.rs
Normal file
63
waycast-plugins/src/projects/type_scanner.rs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use tokei::{Config, Language, LanguageType, Languages};
|
||||||
|
|
||||||
|
pub struct ProjectLanguage {
|
||||||
|
pub name: String,
|
||||||
|
pub percentage: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TypeScanner {
|
||||||
|
tokei_config: Config,
|
||||||
|
ignore_langs: [LanguageType; 5],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TypeScanner {
|
||||||
|
pub fn new() -> TypeScanner {
|
||||||
|
TypeScanner {
|
||||||
|
tokei_config: Config::default(),
|
||||||
|
ignore_langs: [
|
||||||
|
LanguageType::Css,
|
||||||
|
LanguageType::Json,
|
||||||
|
LanguageType::Markdown,
|
||||||
|
LanguageType::CppHeader,
|
||||||
|
LanguageType::CHeader,
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan a project for languages used. Limit gives the
|
||||||
|
// top [limit] entries
|
||||||
|
pub fn scan<P: AsRef<Path>>(&self, path: P, limit: Option<usize>) -> Vec<ProjectLanguage> {
|
||||||
|
let mut langs = Languages::new();
|
||||||
|
langs.get_statistics(&[path], &[], &self.tokei_config);
|
||||||
|
|
||||||
|
let total_code: usize = langs.iter().map(|(_, l)| l.code).sum();
|
||||||
|
let mut rows: Vec<ProjectLanguage> = langs
|
||||||
|
.iter()
|
||||||
|
.map(|(lt, l)| {
|
||||||
|
(
|
||||||
|
*lt,
|
||||||
|
l.code,
|
||||||
|
if total_code > 0 {
|
||||||
|
(l.code as f64) * 100.0 / (total_code as f64)
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.filter(|(l, _, _)| !self.ignore_langs.contains(l))
|
||||||
|
.map(|(lang, _, percent)| ProjectLanguage {
|
||||||
|
name: String::from(lang.name()),
|
||||||
|
percentage: percent,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
rows.sort_by(|a, b| b.percentage.partial_cmp(&a.percentage).unwrap());
|
||||||
|
|
||||||
|
if let Some(l) = limit {
|
||||||
|
rows.truncate(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
rows
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user