ast.rs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  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(roots: Vec<String>) -> Vec<CrateProtoInfo> {
  11. let crate_infos = parse_crate_info_from_path(roots);
  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())
  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('\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('\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('\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| {
  97. if let Item::Struct(item_struct) = item {
  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. ctxt.check().unwrap();
  113. proto_structs
  114. }
  115. pub fn get_ast_enums(ast: &syn::File) -> Vec<FlowyEnum> {
  116. let mut flowy_enums: Vec<FlowyEnum> = vec![];
  117. let ctxt = Ctxt::new();
  118. ast.items.iter().for_each(|item| {
  119. // https://docs.rs/syn/1.0.54/syn/enum.Item.html
  120. if let Item::Enum(item_enum) = item {
  121. let attrs =
  122. flowy_ast::enum_from_ast(&ctxt, &item_enum.ident, &item_enum.variants, &ast.attrs);
  123. flowy_enums.push(FlowyEnum {
  124. name: item_enum.ident.to_string(),
  125. attrs,
  126. });
  127. }
  128. });
  129. ctxt.check().unwrap();
  130. flowy_enums
  131. }
  132. pub struct FlowyEnum<'a> {
  133. pub name: String,
  134. pub attrs: Vec<ASTEnumVariant<'a>>,
  135. }
  136. pub struct Struct<'a> {
  137. pub name: String,
  138. pub fields: Vec<ASTField<'a>>,
  139. }
  140. lazy_static! {
  141. static ref SYNTAX_REGEX: Regex = Regex::new("syntax.*;").unwrap();
  142. static ref IMPORT_REGEX: Regex = Regex::new("(import\\s).*;").unwrap();
  143. }
  144. fn find_proto_file_import(path: &str) -> String {
  145. let mut result = String::new();
  146. if !Path::new(path).exists() {
  147. // log::error!("{} not exist", path);
  148. result = String::from("syntax = \"proto3\";");
  149. return result;
  150. }
  151. let mut file = File::open(path).unwrap();
  152. let mut content = String::new();
  153. file.read_to_string(&mut content).unwrap();
  154. content.lines().for_each(|line| {
  155. ////Result<Option<Match<'t>>>
  156. if let Ok(Some(m)) = SYNTAX_REGEX.find(line) {
  157. result.push_str(m.as_str());
  158. result.push('\n');
  159. }
  160. if let Ok(Some(m)) = IMPORT_REGEX.find(line) {
  161. result.push_str(m.as_str());
  162. result.push('\n');
  163. }
  164. });
  165. result
  166. }