ast.rs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. use crate::proto::proto_info::*;
  2. use crate::proto::template::{EnumTemplate, StructTemplate};
  3. use crate::util::*;
  4. use fancy_regex::Regex;
  5. use flowy_ast::*;
  6. use lazy_static::lazy_static;
  7. use std::{fs::File, io::Read, path::Path};
  8. use syn::Item;
  9. use walkdir::WalkDir;
  10. pub fn parse_crate_protobuf(root: &str) -> Vec<CrateProtoInfo> {
  11. let crate_infos = parse_crate_info_from_path(root);
  12. crate_infos
  13. .into_iter()
  14. .map(|crate_info| {
  15. let proto_output_dir = crate_info.proto_file_output_dir();
  16. let files = crate_info
  17. .proto_paths
  18. .iter()
  19. .map(|proto_crate_path| parse_files_protobuf(proto_crate_path, &proto_output_dir))
  20. .flatten()
  21. .collect::<Vec<ProtoFile>>();
  22. CrateProtoInfo::from_crate_info(crate_info, files)
  23. })
  24. .collect::<Vec<CrateProtoInfo>>()
  25. }
  26. fn parse_files_protobuf(proto_crate_path: &str, proto_output_dir: &str) -> Vec<ProtoFile> {
  27. let mut gen_proto_vec: Vec<ProtoFile> = vec![];
  28. // file_stem https://doc.rust-lang.org/std/path/struct.Path.html#method.file_stem
  29. for (path, file_name) in WalkDir::new(proto_crate_path)
  30. .into_iter()
  31. .filter_entry(|e| !is_hidden(e))
  32. .filter_map(|e| e.ok())
  33. .filter(|e| e.file_type().is_dir() == false)
  34. .map(|e| {
  35. let path = e.path().to_str().unwrap().to_string();
  36. let file_name = e.path().file_stem().unwrap().to_str().unwrap().to_string();
  37. (path, file_name)
  38. })
  39. {
  40. if file_name == "mod" {
  41. continue;
  42. }
  43. // https://docs.rs/syn/1.0.54/syn/struct.File.html
  44. let ast =
  45. syn::parse_file(read_file(&path).unwrap().as_ref()).expect("Unable to parse file");
  46. let structs = get_ast_structs(&ast);
  47. let proto_file_path = format!("{}/{}.proto", &proto_output_dir, &file_name);
  48. let mut proto_file_content = parse_or_init_proto_file(proto_file_path.as_ref());
  49. structs.iter().for_each(|s| {
  50. let mut struct_template = StructTemplate::new();
  51. struct_template.set_message_struct_name(&s.name);
  52. s.fields
  53. .iter()
  54. .filter(|f| f.attrs.pb_index().is_some())
  55. .for_each(|f| {
  56. struct_template.set_field(&f);
  57. });
  58. let s = struct_template.render().unwrap();
  59. proto_file_content.push_str(s.as_ref());
  60. proto_file_content.push_str("\n");
  61. });
  62. let enums = get_ast_enums(&ast);
  63. enums.iter().for_each(|e| {
  64. let mut enum_template = EnumTemplate::new();
  65. enum_template.set_message_enum(&e);
  66. let s = enum_template.render().unwrap();
  67. proto_file_content.push_str(s.as_ref());
  68. proto_file_content.push_str("\n");
  69. });
  70. if !enums.is_empty() || !structs.is_empty() {
  71. let info = ProtoFile {
  72. file_path: path.clone(),
  73. file_name: file_name.clone(),
  74. structs: structs.iter().map(|s| s.name.clone()).collect(),
  75. enums: enums.iter().map(|e| e.name.clone()).collect(),
  76. generated_content: proto_file_content.clone(),
  77. };
  78. gen_proto_vec.push(info);
  79. }
  80. }
  81. gen_proto_vec
  82. }
  83. pub fn parse_or_init_proto_file(path: &str) -> String {
  84. let mut proto_file_content = String::new();
  85. let imported_content = find_proto_file_import(path);
  86. proto_file_content.push_str(imported_content.as_ref());
  87. proto_file_content.push_str("\n");
  88. proto_file_content
  89. }
  90. pub fn get_ast_structs(ast: &syn::File) -> Vec<Struct> {
  91. // let mut content = format!("{:#?}", &ast);
  92. // let mut file = File::create("./foo.txt").unwrap();
  93. // file.write_all(content.as_bytes()).unwrap();
  94. let ctxt = Ctxt::new();
  95. let mut proto_structs: Vec<Struct> = vec![];
  96. ast.items.iter().for_each(|item| match item {
  97. Item::Struct(item_struct) => {
  98. let (_, fields) = struct_from_ast(&ctxt, &item_struct.fields);
  99. if fields
  100. .iter()
  101. .filter(|f| f.attrs.pb_index().is_some())
  102. .count()
  103. > 0
  104. {
  105. proto_structs.push(Struct {
  106. name: item_struct.ident.to_string(),
  107. fields,
  108. });
  109. }
  110. }
  111. _ => {}
  112. });
  113. ctxt.check().unwrap();
  114. proto_structs
  115. }
  116. pub fn get_ast_enums(ast: &syn::File) -> Vec<FlowyEnum> {
  117. let mut flowy_enums: Vec<FlowyEnum> = vec![];
  118. let ctxt = Ctxt::new();
  119. ast.items.iter().for_each(|item| {
  120. // https://docs.rs/syn/1.0.54/syn/enum.Item.html
  121. match item {
  122. Item::Enum(item_enum) => {
  123. let attrs = flowy_ast::enum_from_ast(
  124. &ctxt,
  125. &item_enum.ident,
  126. &item_enum.variants,
  127. &ast.attrs,
  128. );
  129. flowy_enums.push(FlowyEnum {
  130. name: item_enum.ident.to_string(),
  131. attrs,
  132. });
  133. }
  134. _ => {}
  135. }
  136. });
  137. ctxt.check().unwrap();
  138. flowy_enums
  139. }
  140. pub struct FlowyEnum<'a> {
  141. pub name: String,
  142. pub attrs: Vec<ASTEnumVariant<'a>>,
  143. }
  144. pub struct Struct<'a> {
  145. pub name: String,
  146. pub fields: Vec<ASTField<'a>>,
  147. }
  148. lazy_static! {
  149. static ref SYNTAX_REGEX: Regex = Regex::new("syntax.*;").unwrap();
  150. static ref IMPORT_REGEX: Regex = Regex::new("(import\\s).*;").unwrap();
  151. }
  152. fn find_proto_file_import(path: &str) -> String {
  153. let mut result = String::new();
  154. if !Path::new(path).exists() {
  155. // log::error!("{} not exist", path);
  156. result = String::from("syntax = \"proto3\";");
  157. return result;
  158. }
  159. let mut file = File::open(path).unwrap();
  160. let mut content = String::new();
  161. file.read_to_string(&mut content).unwrap();
  162. content.lines().for_each(|line| {
  163. ////Result<Option<Match<'t>>>
  164. if let Ok(some_line) = SYNTAX_REGEX.find(line) {
  165. if let Some(m) = some_line {
  166. result.push_str(m.as_str());
  167. result.push_str("\n");
  168. }
  169. }
  170. if let Ok(some_line) = IMPORT_REGEX.find(line) {
  171. if let Some(m) = some_line {
  172. result.push_str(m.as_str());
  173. result.push_str("\n");
  174. }
  175. }
  176. });
  177. result
  178. }