ProtoGenerator.cs 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. using Grpc.Extension.BaseService.Model;
  2. using ProtoBuf;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Text;
  8. namespace Grpc.Extension.Common.Internal
  9. {
  10. public static class ProtoGenerator
  11. {
  12. /// <summary>
  13. /// proto的message可能的开头的关键字
  14. /// </summary>
  15. internal static List<string> protoMsgStartWithKeywords { get; set; } = new List<string> { "message", "enum" };
  16. /// <summary>
  17. /// 添加proto
  18. /// </summary>
  19. public static void AddProto<TEntity>(string entityName)
  20. {
  21. if (!ProtoMethodInfo.Protos.ContainsKey(entityName))
  22. {
  23. var msg = Serializer.GetProto<TEntity>(ProtoBuf.Meta.ProtoSyntax.Proto3);
  24. ProtoMethodInfo.Protos.TryAdd(entityName, msg.FilterHead().AddMessageComment<TEntity>());
  25. }
  26. }
  27. /// <summary>
  28. /// 获取实体对应的proto
  29. /// </summary>
  30. internal static string GetProto(string entityName)
  31. {
  32. var rst = ProtoMethodInfo.Protos.TryGetValue(entityName, out string proto);
  33. return rst ? proto : null;
  34. }
  35. /// <summary>
  36. /// 过滤头部 只保留message部分
  37. /// </summary>
  38. internal static string FilterHead(this string proto)
  39. {
  40. var lines = new List<string>();
  41. using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(proto)))
  42. using (var sr = new StreamReader(ms))
  43. {
  44. var readEnable = false;
  45. while (sr.Peek() > 0)
  46. {
  47. var line = sr.ReadLine();
  48. if (protoMsgStartWithKeywords.Any(q => line.StartsWith(q)))
  49. {
  50. readEnable = true;
  51. }
  52. if (readEnable)
  53. {
  54. lines.Add(line);
  55. }
  56. }
  57. }
  58. return string.Join(Environment.NewLine, lines);
  59. }
  60. /// <summary>
  61. /// 生成grpc的message的proto内容
  62. /// </summary>
  63. private static string GenGrpcMessageProto(string pkgName,string srv, List<string> msgProtos, bool spiltProto)
  64. {
  65. var sb = new StringBuilder();
  66. if (spiltProto)
  67. {
  68. sb.AppendLine("syntax = \"proto3\";");
  69. if (!string.IsNullOrWhiteSpace(GrpcExtensionsOptions.Instance.ProtoNameSpace))
  70. {
  71. sb.AppendLine("option csharp_namespace = \"" + GrpcExtensionsOptions.Instance.ProtoNameSpace.Trim() +"."+ srv + "\";");
  72. sb.AppendLine("option java_package = \"" + GrpcExtensionsOptions.Instance.ProtoNameSpace.Trim() + "." + srv + "\";");
  73. }
  74. if (!string.IsNullOrWhiteSpace(pkgName))
  75. {
  76. sb.AppendLine($"package {pkgName.Trim()};");
  77. }
  78. }
  79. sb.AppendLine();
  80. sb.AppendLine(Environment.NewLine);
  81. //过滤重复的message
  82. var sbMsg = new StringBuilder();
  83. foreach (var proto in msgProtos)
  84. {
  85. sbMsg.AppendLine(proto);
  86. }
  87. var msg = sbMsg.ToString();
  88. var msgMapProtos = new Dictionary<string, List<string>>();
  89. using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(msg)))
  90. using (var sr = new StreamReader(ms))
  91. {
  92. var msgName = "";
  93. var lines = new List<string>();
  94. while (sr.Peek() > 0)
  95. {
  96. var line = sr.ReadLine();
  97. if (protoMsgStartWithKeywords.Any(q => line.StartsWith(q)))
  98. {
  99. msgName = line.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries)[1];
  100. }
  101. lines.Add(line);
  102. if (line.StartsWith("}"))
  103. {
  104. if (!msgMapProtos.ContainsKey(msgName))
  105. {
  106. msgMapProtos.Add(msgName, lines.Select(q => q).ToList());
  107. }
  108. lines.Clear();
  109. }
  110. }
  111. }
  112. msg = string.Join(Environment.NewLine + Environment.NewLine, msgMapProtos.Select(q => string.Join(Environment.NewLine, q.Value)));
  113. sb.Append(msg);
  114. return sb.ToString();
  115. }
  116. /// <summary>
  117. /// 生成grpc的service的proto内容
  118. /// </summary>
  119. private static string GenGrpcServiceProto(string msgProtoName, string pkgName, string srvName, List<ProtoMethodInfo> methodInfo, bool spiltProto)
  120. {
  121. var sb = new StringBuilder();
  122. sb.AppendLine("syntax = \"proto3\";");
  123. if (!string.IsNullOrWhiteSpace(GrpcExtensionsOptions.Instance.ProtoNameSpace))
  124. {
  125. sb.AppendLine("option csharp_namespace = \"" + GrpcExtensionsOptions.Instance.ProtoNameSpace.Trim() +"."+ srvName + "\";");
  126. sb.AppendLine("option java_package = \"" + GrpcExtensionsOptions.Instance.ProtoNameSpace.Trim() + "." + srvName + "\";");
  127. }
  128. if (!string.IsNullOrWhiteSpace(pkgName))
  129. {
  130. sb.AppendLine($"package {pkgName.Trim()};");
  131. }
  132. if (spiltProto)
  133. {
  134. sb.AppendLine(string.Format("import \"{0}\";", msgProtoName));
  135. }
  136. sb.AppendLine(Environment.NewLine);
  137. sb.AppendLine("service " + srvName + " {");
  138. var template = @" rpc {0}({1}) returns({2})";
  139. methodInfo.ForEach(q => {
  140. var requestName = q.RequestName;
  141. var responseName = q.ResponseName;
  142. switch (q.MethodType)
  143. {
  144. case Core.MethodType.Unary:
  145. break;
  146. case Core.MethodType.ClientStreaming:
  147. requestName = "stream " + requestName;
  148. break;
  149. case Core.MethodType.ServerStreaming:
  150. responseName = "stream " + responseName;
  151. break;
  152. case Core.MethodType.DuplexStreaming:
  153. requestName = "stream " + requestName;
  154. responseName = "stream " + responseName;
  155. break;
  156. }
  157. ProtoCommentGenerator.AddServiceComment(q, sb);
  158. sb.AppendLine(string.Format(template, q.MethodName, requestName, responseName) + ";" + Environment.NewLine);
  159. });
  160. sb.AppendLine("}");
  161. return sb.ToString();
  162. }
  163. /// <summary>
  164. /// 生成proto文件
  165. /// </summary>
  166. public static void Gen(string dir, bool spiltProto)
  167. {
  168. if (ProtoInfo.Methods == null || ProtoInfo.Methods.Count == 0) return;
  169. if (!Directory.Exists(dir))
  170. {
  171. Directory.CreateDirectory(dir);
  172. }
  173. foreach (var grp in ProtoInfo.Methods.GroupBy(q => q.ServiceName))
  174. {
  175. var arr = grp.Key.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
  176. if (arr.Length > 2) continue;
  177. var pkg = arr.Length == 2 ? arr[0] : null;
  178. var srv = arr.Length == 2 ? arr[1] : arr[0];
  179. #region message
  180. var protoName = srv;//grp.Key;
  181. var msgProtoName = $"{protoName}{(spiltProto ? ".message" : "")}.proto";
  182. var msgProtoPath = Path.Combine(dir, msgProtoName);
  183. var msgProtos = new List<string>();
  184. var rqNames = grp.ToList().Select(q => q.RequestName).ToList();
  185. var rsNames = grp.ToList().Select(q => q.ResponseName).ToList();
  186. var msgNames = rqNames.Union(rsNames).Distinct().ToList();
  187. foreach (var n in msgNames)
  188. {
  189. msgProtos.Add(GetProto(n));
  190. }
  191. var msgProtoContent = GenGrpcMessageProto(pkg, srv, msgProtos, spiltProto);
  192. #endregion
  193. #region service
  194. var srvProtoName = $"{protoName}{(spiltProto ? ".service" : "")}.proto";
  195. var srvProtoPath = Path.Combine(dir, srvProtoName);
  196. var methodInfos = grp.ToList();
  197. var srvProtoContent = GenGrpcServiceProto(msgProtoName, pkg, srv, methodInfos, spiltProto);
  198. #endregion
  199. //是否拆分message和service协议
  200. if (spiltProto)
  201. {
  202. //写message协议文件
  203. if (File.Exists(msgProtoPath)) File.Delete(msgProtoPath);
  204. File.AppendAllText(msgProtoPath, msgProtoContent);
  205. //写service协议文件
  206. if (File.Exists(srvProtoPath)) File.Delete(srvProtoPath);
  207. File.AppendAllText(srvProtoPath, srvProtoContent);
  208. }
  209. else
  210. {
  211. var protoPath = Path.Combine(dir, $"{protoName}.proto");
  212. //写协议文件
  213. if (File.Exists(protoPath)) File.Delete(protoPath);
  214. File.AppendAllText(protoPath, srvProtoContent);
  215. File.AppendAllText(protoPath, msgProtoContent);
  216. }
  217. }
  218. }
  219. }
  220. }