csdeps/
lib.rs

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(), //in_name.to_os_string().into_string().unwrap(),
41          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}