Skip to content

Commit

Permalink
feat: jvm project framework detect.
Browse files Browse the repository at this point in the history
  • Loading branch information
ynfeng committed Feb 19, 2021
1 parent 2f1fdce commit e851bb5
Show file tree
Hide file tree
Showing 12 changed files with 268 additions and 24 deletions.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Empty file.
Empty file.
147 changes: 136 additions & 11 deletions framework/src/framework_detector.rs
Original file line number Diff line number Diff line change
@@ -1,58 +1,146 @@
use crate::facet::{Facet, FacetsBuilder};
use crate::lang::LangDetectors;
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::path::Path;

#[derive(Serialize, PartialEq, Debug, Clone)]
pub struct Framework {
pub name: String,
pub path: String,
// for find the projects
pub relative_path: String,
// in some languages has different framework file
// | languages | files |
// |-------------|------------|
// | Java | build.gradle, settings.gradle |
pub files: Vec<String>,
pub files: RefCell<Vec<String>>,
// in JVM projects, has different languages, such as Java, Groovy, Kotlin...
pub languages: Vec<String>,
pub languages: RefCell<Vec<String>>,
}

#[derive(Serialize)]
struct SourceFile {
file_path: String,
language: String,
}

#[derive(Serialize)]
pub struct Frameworks {
frameworks: RefCell<Vec<Framework>>,

#[serde(skip_serializing)]
temp_source_files: RefCell<Vec<SourceFile>>,
}

impl Frameworks {
pub fn add_framework(&self, framework: Framework) {
if !self.frameworks.borrow().contains(&framework) {
self.associate_with_source_files(&framework);
self.frameworks.borrow_mut().push(framework);
}
}

fn associate_with_source_files(&self, framework: &Framework) {
for temp_source_file in self.temp_source_files.borrow().iter() {
if temp_source_file.file_path.starts_with(&framework.path) {
framework
.languages
.borrow_mut()
.push(temp_source_file.language.clone());
}
}
}

pub fn add_language(&self, file_path: &str, language: &str) {
self.add_language_to_frameworks(file_path, &language);
self.cache_source_file(file_path, language);
}

fn add_language_to_frameworks(&self, file_path: &str, language: &&str) {
for framework in self.frameworks.borrow_mut().iter() {
if file_path.starts_with(&framework.path)
&& !framework.languages.borrow().contains(&language.to_string())
{
framework.languages.borrow_mut().push(language.to_string());
}
}
}

fn cache_source_file(&self, file_path: &str, language: &str) {
self.temp_source_files.borrow_mut().push(SourceFile {
file_path: file_path.to_string(),
language: language.to_string(),
});
}

pub fn append(&self, frameworks: &Frameworks) {
self.frameworks
.borrow_mut()
.append(&mut frameworks.frameworks.borrow_mut())
}

pub(crate) fn add_settings_file(&self, framework_name: &str, file_path: &str, file_name: &str) {
for framework in self.frameworks.borrow_mut().iter() {
if file_path.starts_with(&framework.path) && framework.name.eq(framework_name) {
framework.files.borrow_mut().push(file_name.to_string());
}
}
}
}

impl Default for Frameworks {
fn default() -> Self {
Frameworks {
frameworks: RefCell::new(vec![]),
temp_source_files: RefCell::new(vec![]),
}
}
}

#[derive(Serialize)]
pub struct FrameworkDetector<'a> {
pub tags: BTreeMap<&'a str, bool>,
pub frameworks: Vec<Framework>,
pub frameworks: Frameworks,
pub facets: Vec<Box<Facet>>,
}

impl<'a> Default for FrameworkDetector<'a> {
fn default() -> Self {
FrameworkDetector {
tags: BTreeMap::default(),
frameworks: vec![],
frameworks: Frameworks::default(),
facets: vec![],
}
}
}

impl<'a> FrameworkDetector<'a> {
pub fn run<P: AsRef<Path>>(&mut self, path: P) {
self.lang_detect(path);
self.build_project_info();
let mut lang_detectors = FrameworkDetector::detect(&path);

self.add_tags(&mut lang_detectors);
self.add_frameworks(&mut lang_detectors);
self.add_facets();
}

fn lang_detect<P: AsRef<Path>>(&mut self, path: P) {
fn detect<P: AsRef<Path>>(path: P) -> LangDetectors<'a> {
let mut lang_detectors = LangDetectors::default();
lang_detectors.detect(&path);
lang_detectors
}

self.tags.append(&mut lang_detectors.tags);
fn add_tags(&mut self, detectors: &mut LangDetectors<'a>) {
self.tags.append(&mut detectors.tags);
}

fn build_project_info(&mut self) {
fn add_facets(&mut self) {
let builder = FacetsBuilder::default();
let mut facets = builder.build(&self.tags);
self.facets.append(&mut facets);
}

fn add_frameworks(&mut self, detectors: &mut LangDetectors<'a>) {
self.frameworks.append(&detectors.frameworks);
}
}

#[cfg(test)]
Expand Down Expand Up @@ -145,4 +233,41 @@ mod tests {
]"#;
assert_eq!(expect_json, facets_json)
}

#[test]
fn should_detect_jvm_frameworks() {
let detector = build_test_detector(vec!["_fixtures", "projects", "jvm"]);

let framework_json = serde_json::to_string_pretty(&detector.frameworks).unwrap();
let expect_json = r#"{
"frameworks": [
{
"name": "Gradle",
"path": "/Users/mac/git/coco/_fixtures/projects/jvm",
"files": [
"build.gradle",
"settings.gradle"
],
"languages": [
"Scala",
"Groovy",
"Kotlin",
"Java"
]
},
{
"name": "Maven",
"path": "/Users/mac/git/coco/_fixtures/projects/jvm/mavenproject",
"files": [
"pom.xml"
],
"languages": [
"Java",
"Kotlin"
]
}
]
}"#;
assert_eq!(expect_json, framework_json);
}
}
3 changes: 3 additions & 0 deletions framework/src/lang/go.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::framework_detector::Frameworks;
use walkdir::DirEntry;

pub fn get_tag<'a>(entry: &DirEntry) -> Option<&'a str> {
Expand All @@ -7,3 +8,5 @@ pub fn get_tag<'a>(entry: &DirEntry) -> Option<&'a str> {
_ => None,
}
}

pub fn framework_analysis(_entry: &DirEntry, _frameworks: &Frameworks) {}
3 changes: 3 additions & 0 deletions framework/src/lang/js.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::framework_detector::Frameworks;
use walkdir::DirEntry;

pub fn get_tag<'a>(entry: &DirEntry) -> Option<&'a str> {
Expand All @@ -8,3 +9,5 @@ pub fn get_tag<'a>(entry: &DirEntry) -> Option<&'a str> {
_ => None,
}
}

pub fn framework_analysis(_entry: &DirEntry, _frameworks: &Frameworks) {}
90 changes: 90 additions & 0 deletions framework/src/lang/jvm.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::framework_detector::{Framework, Frameworks};
use regex::Regex;
use std::cell::RefCell;
use walkdir::DirEntry;

pub const WORKSPACE_FRAMEWORK_GRADLE: &'static str = "workspace.framework.gradle";
Expand Down Expand Up @@ -56,6 +58,94 @@ pub fn get_tag<'a>(entry: &DirEntry) -> Option<&'a str> {
get_source_tag(file_name)
}

pub fn framework_analysis(entry: &DirEntry, frameworks: &Frameworks) {
let file_name = entry.file_name().to_str().unwrap();
let parent_path = entry.path().parent().unwrap().to_str().unwrap();

if is_build_file(file_name) {
frameworks.add_framework(Framework {
name: ident_framework_name(file_name).to_string(),
path: entry.path().parent().unwrap().to_str().unwrap().to_string(),
files: RefCell::new(vec![file_name.to_string()]),
languages: RefCell::new(vec![]),
});
}

if is_build_settings_file(file_name) {
let framework_name = get_settings_file_framework_name(file_name);

frameworks.add_settings_file(framework_name, parent_path, file_name);
}

if is_source_file(file_name) {
let language = ident_language(file_name);

match language {
Some(lang) => frameworks.add_language(parent_path, lang),
_ => {}
}
}
}

fn get_settings_file_framework_name(file_name: &str) -> &str {
match file_name {
"settings.gradle" => "Gradle",
_ => "",
}
}

fn is_build_settings_file(file_name: &str) -> bool {
match file_name {
"settings.gradle" => true,
_ => false,
}
}

fn ident_language(file_name: &str) -> Option<&str> {
if is_java_source_file(file_name) {
return Some("Java");
}

if is_kotlin_source_file(file_name) {
return Some("Kotlin");
}

if is_groovy_source_file(file_name) {
return Some("Groovy");
}

if is_scala_source_file(file_name) {
return Some("Scala");
}

None
}

fn is_source_file(file_name: &str) -> bool {
for (_, detect_action) in SOURCE_DETECT_LIST.iter() {
if (detect_action)(file_name) {
return true;
}
}
false
}

fn ident_framework_name(build_file: &str) -> &str {
match build_file {
"pom.xml" => "Maven",
"build.gradle" => "Gradle",
_ => "UnKnow",
}
}

fn is_build_file(file_name: &str) -> bool {
match file_name {
"build.gradle" => true,
"pom.xml" => true,
_ => false,
}
}

fn get_source_tag<'a>(file_name: &str) -> Option<&'a str> {
for (key, detect_action) in SOURCE_DETECT_LIST.iter() {
if (detect_action)(file_name) {
Expand Down
Loading

0 comments on commit e851bb5

Please sign in to comment.