Skip to content

Commit

Permalink
feat: add Rust project scanning
Browse files Browse the repository at this point in the history
chore: move read_deps_file() into the Project trait

feat: scan Rust projects
  • Loading branch information
mathieubes committed Nov 21, 2024
1 parent acbcccf commit f1040d4
Show file tree
Hide file tree
Showing 6 changed files with 231 additions and 15 deletions.
75 changes: 75 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ edition = "2021"
colored = "2.1.0"
serde = { version = "1.0.215", features = ["derive"] }
serde_json = "1.0.133"
toml = "0.8.19"
walkdir = "2.5.0"
4 changes: 2 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
mod file_handler;
mod project;

use project::{node_js::NodeProject, scan_project_deps, Project};
use project::{rust::RustProject, scan_project_deps, Project};

fn main() {
println!(
"Running in {} folder.",
std::env::current_dir().unwrap().display()
);

scan_project_deps(NodeProject::default());
scan_project_deps(RustProject::default());
}
6 changes: 4 additions & 2 deletions src/project/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use std::collections::HashSet;

use colored::Colorize;
use node_js::read_deps_file;
use walkdir::WalkDir;

use crate::file_handler::{read_file_at_path, string_exists_in_multiline_text};

pub mod node_js;
pub mod rust;

pub trait Project {
/// Default contrustor for a Project
Expand All @@ -23,10 +23,12 @@ pub trait Project {
fn should_scan_file(&self, file_name: &str) -> bool;

fn deps(&self) -> &Vec<String>;

fn read_deps_file() -> String;
}

pub fn scan_project_deps<T: Project>(mut project: T) {
let deps_count = project.parse_deps(&read_deps_file());
let deps_count = project.parse_deps(&T::read_deps_file());
println!("{deps_count} packages found in current project.");
let mut used_deps = HashSet::new();

Expand Down
23 changes: 12 additions & 11 deletions src/project/node_js.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ pub struct NodeProject {
}

impl NodeProject {
fn new(allowed_extensions: Vec<String>, excluded_files: Vec<String>) -> Self {
fn new(allowed_extensions: Vec<String>, excluded_paths: Vec<String>) -> Self {
Self {
allowed_extensions,
excluded_paths: excluded_files,
excluded_paths,
deps: vec![],
}
}
Expand Down Expand Up @@ -55,6 +55,7 @@ impl Project for NodeProject {
self.deps.len()
}

// TODO should_scan_file functions are exactly the same for both projects
fn should_scan_file(&self, file_path: &str) -> bool {
if file_path == "." {
return true;
Expand All @@ -76,16 +77,16 @@ impl Project for NodeProject {
fn deps(&self) -> &Vec<String> {
&self.deps
}
}

pub fn read_deps_file() -> String {
let f = File::open(DEPS_FILE).unwrap_or_else(|_| {
panic!(
"No file \"{DEPS_FILE}\" in {}",
env::current_dir().unwrap().display()
)
});
read_file(f).unwrap_or_else(|_| panic!("Cannot read {DEPS_FILE} file."))
fn read_deps_file() -> String {
let f = File::open(DEPS_FILE).unwrap_or_else(|_| {
panic!(
"No file \"{DEPS_FILE}\" in {}",
env::current_dir().unwrap().display()
)
});
read_file(f).unwrap_or_else(|_| panic!("Cannot read {DEPS_FILE} file."))
}
}

fn is_used_in_package_scripts(parsed_file: &NodePackagesHandler, name: &str) -> bool {
Expand Down
137 changes: 137 additions & 0 deletions src/project/rust.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
use std::{env, fs::File};

use serde::Deserialize;
use toml::Table;

use crate::file_handler::read_file;

use super::Project;

const DEPS_FILE: &str = "Cargo.toml";

#[derive(Deserialize)]
pub struct RustPackagesHandler {
dependencies: Table
}

pub struct RustProject {
deps: Vec<String>,

allowed_extensions: Vec<String>,
excluded_paths: Vec<String>,
}

impl RustProject {
fn new(allowed_extensions: Vec<String>, excluded_paths: Vec<String>) -> Self {
Self {
allowed_extensions,
excluded_paths,
deps: Vec::new(),
}
}
}

impl Project for RustProject {
fn default() -> Self {
Self::new(
Vec::from(["rs".into()]),
// For now, excluding Cargo.toml file is not necessary but if in the future we need to
// include .toml files then we can't miss it.
Vec::from(["Cargo.toml".into()]),
)
}

fn parse_deps(&mut self, deps_file_content: &str) -> usize {
let packages_handler: RustPackagesHandler = toml::from_str(deps_file_content)
.unwrap_or_else(|e| panic!("Cannot parse {DEPS_FILE} file. {}", e.to_string()));
self.deps = get_deps_names(packages_handler);
self.deps.len()
}

// TODO should_scan_file functions are exactly the same for both projects
fn should_scan_file(&self, file_path: &str) -> bool {
if file_path == "." {
return true;
}

for excluded in self.excluded_paths.iter() {
if file_path.contains(excluded) {
return false;
}
}
for ext in self.allowed_extensions.iter() {
if file_path.ends_with(&format!(".{ext}")) {
return true;
}
}
false
}

fn deps(&self) -> &Vec<String> {
&self.deps
}

// TODO Always same function, need to be refactored
fn read_deps_file() -> String {
let f = File::open(DEPS_FILE).unwrap_or_else(|_| {
panic!(
"No file \"{DEPS_FILE}\" in {}",
env::current_dir().unwrap().display()
)
});
read_file(f).unwrap_or_else(|_| panic!("Cannot read {DEPS_FILE} file."))
}
}

fn get_deps_names(parsed_file: RustPackagesHandler) -> Vec<String> {
let mut names = Vec::from_iter(parsed_file
.dependencies
.iter()
.map(|(name, _version)| {
name.clone()
}));
names.sort();
names
}

#[cfg(test)]
mod project_rust_tests {
use super::*;

#[test]
fn should_scan_file_works() {
let project = RustProject::default();
assert_eq!(project.should_scan_file("foo.rs"), true);
assert_eq!(project.should_scan_file("foo.js"), false);
assert_eq!(project.should_scan_file("Cargo.toml"), false);
assert_eq!(project.should_scan_file("Cargo.lock"), false);
}

#[test]
fn get_deps_names_works() {
let mut packages_handler = RustPackagesHandler{
dependencies: Table::new(),
};
packages_handler
.dependencies
.insert("foo".into(), "0.1.0".into());
packages_handler
.dependencies
.insert("bar".into(), "0.1.0".into());

assert_eq!(get_deps_names(packages_handler), Vec::from(["bar", "foo"]));
}

#[test]
fn parse_deps_works() {
let mut project = RustProject::default();

let file_content = "[dependencies]
foo = \"2.1.0\"
bar = { version = \"1.0.215\", features = [\"derive\"] }";

assert_eq!(project.parse_deps(file_content), 2);
assert_eq!(project.deps.len(), 2);
assert_eq!(project.deps, Vec::from(["bar", "foo"]));
}
}

0 comments on commit f1040d4

Please sign in to comment.