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
use fs_err as fs;
use globwalk::GlobWalkerBuilder;
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
pub struct Skeleton {
pub manifests: Vec<Manifest>,
pub lock_file: Option<String>,
}
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
pub struct Manifest {
pub relative_path: PathBuf,
pub contents: String,
}
impl Skeleton {
pub fn derive<P: AsRef<Path>>(base_path: P) -> Result<Self, anyhow::Error> {
let walker = GlobWalkerBuilder::new(&base_path, "/**/Cargo.toml").build()?;
let mut manifests = vec![];
for manifest in walker {
let manifest = manifest?;
let absolute_path = manifest.path().to_path_buf();
let contents = fs::read_to_string(&absolute_path)?;
let mut parsed = cargo_manifest::Manifest::from_str(&contents)?;
parsed.complete_from_path(&absolute_path)?;
let intermediate = toml::Value::try_from(parsed)?;
let contents = toml::to_string(&intermediate)?;
let relative_path = pathdiff::diff_paths(absolute_path, &base_path)
.ok_or_else(|| anyhow::anyhow!("Failed to compute relative path of manifest."))?;
manifests.push(Manifest {
relative_path,
contents,
});
}
let lock_file = match fs::read_to_string(base_path.as_ref().join("Cargo.lock")) {
Ok(lock) => Some(lock),
Err(e) => {
if std::io::ErrorKind::NotFound != e.kind() {
return Err(anyhow::Error::from(e).context("Failed to read Cargo.lock file."));
}
None
}
};
Ok(Skeleton {
manifests,
lock_file,
})
}
pub fn build_minimum_project(&self) -> Result<(), anyhow::Error> {
if let Some(lock_file) = &self.lock_file {
fs::write("Cargo.lock", lock_file.as_str())?;
}
for manifest in &self.manifests {
let parent_directory = if let Some(parent_directory) = manifest.relative_path.parent() {
fs::create_dir_all(&parent_directory)?;
parent_directory.to_path_buf()
} else {
PathBuf::new()
};
fs::write(&manifest.relative_path, &manifest.contents)?;
let parsed_manifest =
cargo_manifest::Manifest::from_slice(manifest.contents.as_bytes())?;
if let Some(bins) = parsed_manifest.bin {
for bin in &bins {
let binary_relative_path = bin.path.clone().expect("Missing path for binary.");
let binary_path = parent_directory.join(binary_relative_path);
if let Some(parent_directory) = binary_path.parent() {
fs::create_dir_all(parent_directory)?;
}
fs::write(binary_path, "fn main() {}")?;
}
}
for lib in &parsed_manifest.lib {
let lib_relative_path = lib.path.clone().expect("Missing path for library.");
let lib_path = parent_directory.join(lib_relative_path);
if let Some(parent_directory) = lib_path.parent() {
fs::create_dir_all(parent_directory)?;
}
fs::write(lib_path, "")?;
}
if let Some(package) = parsed_manifest.package {
if let Some(build) = package.build {
if let cargo_manifest::Value::String(build_raw_path) = build {
let build_relative_path = PathBuf::from(build_raw_path);
let build_path = parent_directory.join(build_relative_path);
if let Some(parent_directory) = build_path.parent() {
fs::create_dir_all(parent_directory)?;
}
fs::write(build_path, "fn main() {}")?;
}
}
}
}
Ok(())
}
}