plottery_project/
cargo_project_template.rs1use anyhow::Result;
2use path_absolutize::Absolutize;
3use rust_embed::RustEmbed;
4use std::path::{Path, PathBuf};
5use std::vec;
6use std::{fs::File, io::Write};
7use vfs::{EmbeddedFS, FileSystem};
8
9#[derive(RustEmbed, Debug)]
10#[folder = "cargo_project_template/"]
11struct CargoProjectTemplate;
12
13fn get_fs() -> EmbeddedFS<CargoProjectTemplate> {
14 EmbeddedFS::<CargoProjectTemplate>::new()
15}
16
17#[derive(Debug, Clone)]
18pub enum LibSource {
19 PlotteryHome,
20 Path { path: PathBuf },
21 Cargo,
22}
23
24struct Replacement {
25 from: String,
26 to: String,
27}
28
29fn get_plottery_subcrate(
30 plottery_home_subdir: &str,
31 lib_source: &LibSource,
32 crate_name: &str,
33) -> Result<String> {
34 let lib_source_for_toml = match lib_source {
35 LibSource::PlotteryHome => {
36 let plottery_home = match std::env::var("PLOTTERY_HOME") {
37 Ok(home) => Path::new(&home).to_path_buf(),
38 Err(_) => {
39 return Err(anyhow::anyhow!(
40 "Environment variable PLOTTERY_HOME is not set"
41 ));
42 }
43 };
44 let plottery_home_lib = plottery_home
45 .join(plottery_home_subdir)
46 .absolutize()
47 .unwrap()
48 .to_path_buf();
49 if !plottery_home_lib.exists() {
50 return Err(anyhow::anyhow!(
51 "PLOTTERY_HOME/lib does not exist at: {}",
52 plottery_home_lib.to_string_lossy()
53 ));
54 }
55 let path = plottery_home_lib
56 .absolutize()
57 .unwrap()
58 .to_string_lossy()
59 .to_string();
60 format!("{} = {{ path = \"{}\" }}", crate_name, path)
61 }
62 LibSource::Path {
63 path: plottery_home_path,
64 } => {
65 let sub_crate_path = plottery_home_path
66 .join(plottery_home_subdir)
67 .to_string_lossy()
68 .to_string();
69 format!("{} = {{ path = \"{}\" }}", crate_name, sub_crate_path)
70 }
71 LibSource::Cargo => format!("{} = \">=0.1\"", crate_name),
72 };
73 Ok(lib_source_for_toml)
74}
75
76pub fn generate_cargo_project_to_disk(
77 out_dir: PathBuf,
78 project_name: &str,
79 lib_source: LibSource,
80) -> Result<()> {
81 let fs = get_fs();
82 std::fs::create_dir_all(&out_dir)?;
83
84 let plottery_lib_include = get_plottery_subcrate("lib", &lib_source, "plottery_lib")?;
85 let plottery_project_include =
86 get_plottery_subcrate("project", &lib_source, "plottery_project")?;
87
88 let string_replacements = vec![
89 Replacement {
90 from: "{{plottery-lib-include}}".to_string(),
91 to: plottery_lib_include,
92 },
93 Replacement {
94 from: "{{plottery-project-include}}".to_string(),
95 to: plottery_project_include,
96 },
97 Replacement {
98 from: "{{project-name}}".to_string(),
99 to: project_name.to_owned(),
100 },
101 ];
102 let file_name_replacements = vec![Replacement {
103 from: "Cargo_template.toml".to_string(),
104 to: "Cargo.toml".to_string(),
105 }];
106
107 write_dir_to_disk_recurse(
108 &fs,
109 "".to_string(),
110 &out_dir,
111 &string_replacements,
112 &file_name_replacements,
113 )?;
114
115 Ok(())
116}
117
118fn write_dir_to_disk_recurse(
119 fs: &EmbeddedFS<CargoProjectTemplate>,
120 sub_dir: String,
121 out_dir: &PathBuf,
122 string_replacements: &Vec<Replacement>,
123 file_name_replacements: &Vec<Replacement>,
124) -> Result<()> {
125 for element in fs.read_dir(&sub_dir).unwrap() {
126 let sub_element = format!("{}/{}", &sub_dir, &element);
127 let out_element = out_dir.join(sub_element.strip_prefix('/').unwrap()); if is_file(fs, &sub_element) {
129 write_file_to_disk(
130 fs,
131 &sub_element,
132 &out_element,
133 string_replacements,
134 file_name_replacements,
135 )?;
136 } else {
137 std::fs::create_dir_all(out_element)?;
138 write_dir_to_disk_recurse(
139 fs,
140 sub_element.clone(),
141 out_dir,
142 string_replacements,
143 file_name_replacements,
144 )?;
145 }
146 }
147 Ok(())
148}
149
150fn write_file_to_disk(
151 fs: &EmbeddedFS<CargoProjectTemplate>,
152 sub_element: &str,
153 out_element: &Path,
154 string_replacements: &Vec<Replacement>,
155 file_name_replacements: &Vec<Replacement>,
156) -> Result<()> {
157 let mut file = fs.open_file(sub_element)?;
158 let ext = match Path::new(&sub_element).extension() {
159 Some(ext) => ext.to_str().unwrap_or(""),
160 None => "",
161 };
162
163 let mut buf = Vec::new();
164 file.read_to_end(&mut buf)?;
165
166 if ext == "rs" || ext == "toml" {
167 let mut buf_str = String::from_utf8(buf)?;
168 for replacement in string_replacements {
169 buf_str = buf_str.replace(&replacement.from, &replacement.to);
170 }
171 buf = buf_str.into_bytes();
172 }
173
174 let mut file_name = out_element
175 .file_name()
176 .expect("Failed to get file name")
177 .to_str()
178 .unwrap();
179
180 for replacement in file_name_replacements {
181 if file_name == replacement.from {
182 file_name = &replacement.to;
183 break;
184 }
185 }
186
187 let out_file_path = out_element.with_file_name(file_name);
188
189 let mut out_file = File::create(out_file_path)?;
190 out_file.write_all(&buf)?;
191
192 Ok(())
193}
194
195fn is_file(fs: &EmbeddedFS<CargoProjectTemplate>, sub_dir: &str) -> bool {
196 fs.open_file(sub_dir).is_ok()
197}