mod.rs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. #![allow(unused_imports)]
  2. #![allow(unused_attributes)]
  3. #![allow(dead_code)]
  4. mod ast;
  5. mod proto_gen;
  6. mod proto_info;
  7. mod template;
  8. use crate::util::path_string_with_component;
  9. use itertools::Itertools;
  10. use log::info;
  11. pub use proto_gen::*;
  12. pub use proto_info::*;
  13. use std::fs::File;
  14. use std::io::Write;
  15. use std::path::{Path, PathBuf};
  16. use std::process::Command;
  17. use walkdir::WalkDir;
  18. pub fn gen(crate_name: &str) {
  19. let crate_path = std::fs::canonicalize(".")
  20. .unwrap()
  21. .as_path()
  22. .display()
  23. .to_string();
  24. // 1. generate the proto files to proto_file_dir
  25. #[cfg(feature = "proto_gen")]
  26. let proto_crates = gen_proto_files(crate_name, &crate_path);
  27. for proto_crate in proto_crates {
  28. let mut proto_file_paths = vec![];
  29. let mut file_names = vec![];
  30. let proto_file_output_path = proto_crate
  31. .proto_output_path()
  32. .to_str()
  33. .unwrap()
  34. .to_string();
  35. let protobuf_output_path = proto_crate
  36. .protobuf_crate_path()
  37. .to_str()
  38. .unwrap()
  39. .to_string();
  40. for (path, file_name) in WalkDir::new(&proto_file_output_path)
  41. .into_iter()
  42. .filter_map(|e| e.ok())
  43. .map(|e| {
  44. let path = e.path().to_str().unwrap().to_string();
  45. let file_name = e.path().file_stem().unwrap().to_str().unwrap().to_string();
  46. (path, file_name)
  47. })
  48. {
  49. if path.ends_with(".proto") {
  50. // https://stackoverflow.com/questions/49077147/how-can-i-force-build-rs-to-run-again-without-cleaning-my-whole-project
  51. println!("cargo:rerun-if-changed={}", path);
  52. proto_file_paths.push(path);
  53. file_names.push(file_name);
  54. }
  55. }
  56. let protoc_bin_path = protoc_bin_vendored::protoc_bin_path().unwrap();
  57. // 2. generate the protobuf files(Dart)
  58. #[cfg(feature = "dart")]
  59. generate_dart_protobuf_files(
  60. crate_name,
  61. &proto_file_output_path,
  62. &proto_file_paths,
  63. &file_names,
  64. &protoc_bin_path,
  65. );
  66. #[cfg(feature = "ts")]
  67. generate_ts_protobuf_files(
  68. crate_name,
  69. &proto_file_output_path,
  70. &proto_file_paths,
  71. &file_names,
  72. &protoc_bin_path,
  73. );
  74. // 3. generate the protobuf files(Rust)
  75. generate_rust_protobuf_files(
  76. &protoc_bin_path,
  77. &proto_file_paths,
  78. &proto_file_output_path,
  79. &protobuf_output_path,
  80. );
  81. }
  82. }
  83. fn generate_rust_protobuf_files(
  84. protoc_bin_path: &Path,
  85. proto_file_paths: &[String],
  86. proto_file_output_path: &str,
  87. protobuf_output_path: &str,
  88. ) {
  89. protoc_rust::Codegen::new()
  90. .out_dir(protobuf_output_path)
  91. .protoc_path(protoc_bin_path)
  92. .inputs(proto_file_paths)
  93. .include(proto_file_output_path)
  94. .run()
  95. .expect("Running rust protoc failed.");
  96. }
  97. #[cfg(feature = "ts")]
  98. fn generate_ts_protobuf_files(
  99. name: &str,
  100. proto_file_output_path: &str,
  101. paths: &[String],
  102. file_names: &Vec<String>,
  103. protoc_bin_path: &Path,
  104. ) {
  105. let root = std::env::var("CARGO_MAKE_WORKING_DIRECTORY").unwrap_or("../../".to_string());
  106. let tauri_backend_service_path = std::env::var("TAURI_BACKEND_SERVICE_PATH")
  107. .unwrap_or("appflowy_tauri/src/services/backend".to_string());
  108. let mut output = PathBuf::new();
  109. output.push(root);
  110. output.push(tauri_backend_service_path);
  111. output.push("models");
  112. output.push(name);
  113. if !output.as_path().exists() {
  114. std::fs::create_dir_all(&output).unwrap();
  115. }
  116. let protoc_bin_path = protoc_bin_path.to_str().unwrap().to_owned();
  117. paths.iter().for_each(|path| {
  118. // if let Err(err) = Command::new(protoc_bin_path.clone())
  119. // .arg(format!("--ts_out={}", output.to_str().unwrap()))
  120. // .arg(format!("--proto_path={}", proto_file_output_path))
  121. // .arg(path)
  122. // .spawn()
  123. // {
  124. // panic!("Generate ts pb file failed: {}, {:?}", path, err);
  125. // }
  126. let result = cmd_lib::run_cmd! {
  127. ${protoc_bin_path} --ts_out=${output} --proto_path=${proto_file_output_path} ${path}
  128. };
  129. if result.is_err() {
  130. panic!("Generate ts pb file failed with: {}, {:?}", path, result)
  131. };
  132. });
  133. let ts_index = path_string_with_component(&output, vec!["index.ts"]);
  134. match std::fs::OpenOptions::new()
  135. .create(true)
  136. .write(true)
  137. .append(false)
  138. .truncate(true)
  139. .open(&ts_index)
  140. {
  141. Ok(ref mut file) => {
  142. let mut export = String::new();
  143. export.push_str("// Auto-generated, do not edit \n");
  144. for file_name in file_names {
  145. let c = format!("export * from \"./{}\";\n", file_name);
  146. export.push_str(c.as_ref());
  147. }
  148. file.write_all(export.as_bytes()).unwrap();
  149. File::flush(file).unwrap();
  150. },
  151. Err(err) => {
  152. panic!("Failed to open file: {}", err);
  153. },
  154. }
  155. }
  156. #[cfg(feature = "dart")]
  157. fn generate_dart_protobuf_files(
  158. name: &str,
  159. proto_file_output_path: &str,
  160. paths: &[String],
  161. file_names: &Vec<String>,
  162. protoc_bin_path: &Path,
  163. ) {
  164. if std::env::var("CARGO_MAKE_WORKING_DIRECTORY").is_err() {
  165. log::error!("CARGO_MAKE_WORKING_DIRECTORY was not set, skip generate dart pb");
  166. return;
  167. }
  168. if std::env::var("FLUTTER_FLOWY_SDK_PATH").is_err() {
  169. log::error!("FLUTTER_FLOWY_SDK_PATH was not set, skip generate dart pb");
  170. return;
  171. }
  172. let mut output = PathBuf::new();
  173. output.push(std::env::var("CARGO_MAKE_WORKING_DIRECTORY").unwrap());
  174. output.push(std::env::var("FLUTTER_FLOWY_SDK_PATH").unwrap());
  175. output.push("lib");
  176. output.push("protobuf");
  177. output.push(name);
  178. if !output.as_path().exists() {
  179. std::fs::create_dir_all(&output).unwrap();
  180. }
  181. check_pb_dart_plugin();
  182. let protoc_bin_path = protoc_bin_path.to_str().unwrap().to_owned();
  183. paths.iter().for_each(|path| {
  184. let result = cmd_lib::run_cmd! {
  185. ${protoc_bin_path} --dart_out=${output} --proto_path=${proto_file_output_path} ${path}
  186. };
  187. if result.is_err() {
  188. panic!("Generate dart pb file failed with: {}, {:?}", path, result)
  189. };
  190. });
  191. let protobuf_dart = path_string_with_component(&output, vec!["protobuf.dart"]);
  192. match std::fs::OpenOptions::new()
  193. .create(true)
  194. .write(true)
  195. .append(false)
  196. .truncate(true)
  197. .open(&protobuf_dart)
  198. {
  199. Ok(ref mut file) => {
  200. let mut export = String::new();
  201. export.push_str("// Auto-generated, do not edit \n");
  202. for file_name in file_names {
  203. let c = format!("export './{}.pb.dart';\n", file_name);
  204. export.push_str(c.as_ref());
  205. }
  206. file.write_all(export.as_bytes()).unwrap();
  207. File::flush(file).unwrap();
  208. },
  209. Err(err) => {
  210. panic!("Failed to open file: {}", err);
  211. },
  212. }
  213. }
  214. pub fn check_pb_dart_plugin() {
  215. if cfg!(target_os = "windows") {
  216. //Command::new("cmd")
  217. // .arg("/C")
  218. // .arg(cmd)
  219. // .status()
  220. // .expect("failed to execute process");
  221. //panic!("{}", format!("\n❌ The protoc-gen-dart was not installed correctly."))
  222. } else {
  223. let exit_result = Command::new("sh")
  224. .arg("-c")
  225. .arg("command -v protoc-gen-dart")
  226. .status()
  227. .expect("failed to execute process");
  228. if !exit_result.success() {
  229. let mut msg = "\n❌ Can't find protoc-gen-dart in $PATH:\n".to_string();
  230. let output = Command::new("sh").arg("-c").arg("echo $PATH").output();
  231. let paths = String::from_utf8(output.unwrap().stdout)
  232. .unwrap()
  233. .split(':')
  234. .map(|s| s.to_string())
  235. .collect::<Vec<String>>();
  236. paths.iter().for_each(|s| msg.push_str(&format!("{}\n", s)));
  237. if let Ok(output) = Command::new("sh")
  238. .arg("-c")
  239. .arg("which protoc-gen-dart")
  240. .output()
  241. {
  242. msg.push_str(&format!(
  243. "Installed protoc-gen-dart path: {:?}\n",
  244. String::from_utf8(output.stdout).unwrap()
  245. ));
  246. }
  247. msg.push_str("✅ You can fix that by adding:");
  248. msg.push_str("\n\texport PATH=\"$PATH\":\"$HOME/.pub-cache/bin\"\n");
  249. msg.push_str("to your shell's config file.(.bashrc, .bash, .profile, .zshrc etc.)");
  250. panic!("{}", msg)
  251. }
  252. }
  253. }
  254. #[cfg(feature = "proto_gen")]
  255. fn gen_proto_files(crate_name: &str, crate_path: &str) -> Vec<ProtobufCrate> {
  256. let crate_context = ProtoGenerator::gen(crate_name, crate_path);
  257. let proto_crates = crate_context
  258. .iter()
  259. .map(|info| info.protobuf_crate.clone())
  260. .collect::<Vec<_>>();
  261. crate_context
  262. .into_iter()
  263. .flat_map(|info| info.files)
  264. .for_each(|file| {
  265. println!("cargo:rerun-if-changed={}", file.file_path);
  266. });
  267. proto_crates
  268. }