1use serde::{Serialize, Deserialize};
2use serde_xml_rs::{from_str};
3use std::fs::{File, read_dir};
4use std::io::prelude::*;
5use std::path::PathBuf;
6use indicatif::ProgressBar;
7
8const INVALID_CHAR: &str = "\u{feff}";
9
10#[derive(Debug, Serialize, Deserialize, PartialEq)]
11pub struct PackageReference {
12 #[serde(alias = "Include", default)]
13 pub include: String,
14
15 #[serde(alias = "Version", default)]
16 pub version: String,
17}
18
19#[derive(Debug, Serialize, Deserialize, PartialEq)]
20struct ItemGroup {
21 #[serde(alias = "PackageReference", default)]
22 pub dependencies: Vec<PackageReference>,
23}
24
25#[derive(Debug, Serialize, Deserialize, PartialEq)]
26struct Project {
27 #[serde(rename = "ItemGroup", default)]
28 pub item_groups: Vec<ItemGroup>,
29}
30
31#[derive(Debug, Serialize, PartialEq)]
32pub struct Deps {
33 pub name: String,
34 pub dependencies: Vec<PackageReference>,
35}
36
37impl Deps {
38 fn new(in_name: &str) -> Deps {
39 Deps {
40 name : in_name.to_string(), dependencies : vec!(),
42 }
43 }
44
45 fn from_proj_str(name: &str, proj: &mut Project) -> Result<Deps, serde_xml_rs::Error> {
46 let mut deps = Deps::new(name);
47
48 for item_group in &mut proj.item_groups {
49 if item_group.dependencies.len() > 0 {
50 deps.dependencies.append(&mut item_group.dependencies);
51 }
52 }
53
54 Ok(deps)
55 }
56
57 pub fn vec_from_filepaths(paths: Vec<PathBuf>) -> Result<Vec<Deps>, std::io::Error> {
58 let mut deps_vec: Vec<Deps> = vec!();
59 for path in paths{
60 let source = read_csproj(&path)?;
61 match from_str(&source) {
62 Err(why) => println!("{:?}", why),
63 Ok(mut proj) => {
64 let deps =
65 Deps::from_proj_str(
66 &path.file_stem().unwrap().to_os_string().into_string().unwrap(),
67 &mut proj).unwrap();
68 deps_vec.push(deps);
69 }
70 }
71 }
72 Ok(deps_vec)
73 }
74}
75
76#[derive(Debug, Serialize)]
77pub struct ProjectCollection<'a> {
78 pub content: &'a Vec<Deps>,
79 pub project_count: &'a usize,
80}
81
82pub fn find_projects<'a>(input_path: PathBuf, paths: &'a mut Vec<PathBuf>, bar: &mut ProgressBar, is_rec_search: bool) {
83 bar.inc(1);
84 if input_path.is_file() {
85 if is_project(&input_path) {
86 paths.push(input_path);
87 }
88 }
89 else {
90 let entries = read_dir(input_path).unwrap();
91 for dir in entries {
92 match dir {
93 Err(why) => println!("{:?}", why),
94 Ok(entry) => {
95 if is_rec_search && entry.path().is_dir() {
96 bar.set_message(&format!("Searching /{}", entry.path().file_stem().unwrap().to_os_string().into_string().unwrap()));
97 find_projects(entry.path(), paths, bar, is_rec_search);
98 }
99 if is_project(&entry.path()){
100 bar.set_message(&format!("Found {}", entry.path().file_stem().unwrap().to_os_string().into_string().unwrap()));
101 paths.push(entry.path());
102 }
103 }
104 }
105 }
106 }
107}
108
109fn read_csproj(path: &PathBuf) -> Result<String, std::io::Error> {
110 let mut file = File::open(path)?;
111 let mut proj = String::new();
112 file.read_to_string(&mut proj)?;
113 Ok(proj.trim_start_matches(INVALID_CHAR).to_string())
114}
115
116fn is_project(entry: &PathBuf) -> bool {
117 if let Some(extension) = entry.extension(){
118 return extension == "csproj";
119 }
120 false
121}