1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
use serde::{Serialize, Deserialize};
use serde_xml_rs::{from_str};
use std::fs::{File, read_dir};
use std::io::prelude::*;
use std::path::PathBuf;
use indicatif::ProgressBar;

const INVALID_CHAR: &str = "\u{feff}";

#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct PackageReference {
  #[serde(alias = "Include", default)]
  pub include: String,

  #[serde(alias = "Version", default)]
  pub version: String,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct ItemGroup {
  #[serde(alias = "PackageReference", default)]
  pub dependencies: Vec<PackageReference>,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Project {
  #[serde(rename = "ItemGroup", default)]
  pub item_groups: Vec<ItemGroup>,
}

#[derive(Debug, Serialize, PartialEq)]
pub struct Deps {
  pub name: String,
  pub dependencies: Vec<PackageReference>,
}

impl Deps {
  fn new(in_name: &str) -> Deps {
      Deps {
          name : in_name.to_string(), //in_name.to_os_string().into_string().unwrap(),
          dependencies : vec!(),
      }
  }

  fn from_proj_str(name: &str, proj: &mut Project) -> Result<Deps, serde_xml_rs::Error> {
    let mut deps = Deps::new(name); 

    for item_group in &mut proj.item_groups {
      if item_group.dependencies.len() > 0 {
        deps.dependencies.append(&mut item_group.dependencies);
      }
    }

    Ok(deps)
  }

  pub fn vec_from_filepaths(paths: Vec<PathBuf>) -> Result<Vec<Deps>, std::io::Error> {
    let mut deps_vec: Vec<Deps> = vec!();
    for path in paths{
      let source = read_csproj(&path)?;
      match from_str(&source) {
        Err(why) => println!("{:?}", why),
        Ok(mut proj) => {
          let deps = 
            Deps::from_proj_str(
              &path.file_stem().unwrap().to_os_string().into_string().unwrap(), 
              &mut proj).unwrap();
              deps_vec.push(deps);
        }
      }
    }
    Ok(deps_vec)
  }
}

#[derive(Debug, Serialize)]
pub struct ProjectCollection<'a> {
  pub content: &'a Vec<Deps>,
  pub project_count: &'a usize,
}

pub fn find_projects<'a>(input_path: PathBuf, paths: &'a mut Vec<PathBuf>, bar: &mut ProgressBar, is_rec_search: bool) {
  bar.inc(1);
  if input_path.is_file() {
    if is_project(&input_path) {
      paths.push(input_path);
    }
  }
  else {
    let entries = read_dir(input_path).unwrap();
    for dir in entries {
      match dir {
        Err(why) => println!("{:?}", why),
        Ok(entry) => {
          if is_rec_search && entry.path().is_dir() {
            bar.set_message(&format!("Searching /{}", entry.path().file_stem().unwrap().to_os_string().into_string().unwrap()));
            find_projects(entry.path(), paths, bar, is_rec_search);
          }
          if is_project(&entry.path()){
            bar.set_message(&format!("Found {}", entry.path().file_stem().unwrap().to_os_string().into_string().unwrap()));
            paths.push(entry.path());
          }
        }
      }
    }
  }
}

fn read_csproj(path: &PathBuf) -> Result<String, std::io::Error> {
  let mut file = File::open(path)?;
  let mut proj = String::new();
  file.read_to_string(&mut proj)?;
  Ok(proj.trim_start_matches(INVALID_CHAR).to_string())
}

fn is_project(entry: &PathBuf) -> bool {
  if let Some(extension) = entry.extension(){
    return extension == "csproj";
  }
  false
}