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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#![warn(clippy::pedantic)]
use dialoguer::{theme::ColorfulTheme, MultiSelect};
use duct::cmd;
use semver::Version;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
pub mod web {
pub mod types {
pub mod github;
}
pub mod github;
pub mod provider;
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Crates {
installs: HashMap<String, Install>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Install {
#[serde(default)]
pub features: Vec<String>,
pub no_default_features: bool,
pub all_features: bool,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Package {
pub name: String,
pub features: Vec<String>,
pub all_features: bool,
pub no_default_features: bool,
pub version: Version,
}
fn slice_name(name: &str) -> (String, Version) {
let name = name.split(' ').collect::<Vec<&str>>();
let version = Version::parse(name[1]).unwrap();
(name[0].to_string(), version)
}
#[must_use]
pub fn get_packages() -> Vec<Package> {
let mut path = dirs::home_dir().unwrap();
path.push(".cargo/.crates2.json");
assert!(path.exists(), "{} does not exist", path.display());
let packages: Crates = serde_json::from_str(std::fs::read_to_string(path).expect("Failed to read the .crates2.json").as_str())
.expect("Failed to parse crates2.json");
let mut pkgs = Vec::new();
for (name, install) in packages.installs {
let (name, version) = slice_name(&name);
let pkg = Package {
name,
version,
features: install.features,
all_features: install.all_features,
no_default_features: install.no_default_features,
};
pkgs.push(pkg);
}
pkgs
}
fn install_package(package: &Package) {
let name = &package.name;
let mut args = vec!["install".to_string(), name.clone()];
if package.no_default_features {
args.push("--no-default-features".to_string());
}
if !package.features.is_empty() {
args.push("--features".to_string());
args.push(package.features.join(" "));
}
if let Err(err) = cmd("cargo", args).read() {
eprintln!("Error while installing ({}): {}", name, err);
}
}
pub fn restore(packages: &[Package]) {
let installed_packages = get_packages()
.iter()
.map(|p| p.name.clone())
.collect::<Vec<_>>();
let packages = packages
.iter()
.filter(|p| !installed_packages.contains(&p.name))
.collect::<Vec<_>>();
if packages.is_empty() {
println!("No packages to restore");
return;
}
let selected_packages = MultiSelect::with_theme(&ColorfulTheme::default())
.with_prompt("Packages to install")
.items(
&packages
.iter()
.map(|x| x.name.as_str())
.collect::<Vec<&str>>(),
)
.interact()
.unwrap();
for selected in selected_packages {
let package = packages.get(selected).unwrap();
install_package(package);
}
}