|
| 1 | +using Google.Protobuf.Reflection; |
| 2 | +using ProtoBuf.Reflection; |
| 3 | + |
| 4 | +namespace ProtoGen.Wasm; |
| 5 | + |
| 6 | +/// <summary> |
| 7 | +/// Wraps protobuf-net.Reflection's parser and code generators. |
| 8 | +/// </summary> |
| 9 | +/// <remarks> |
| 10 | +/// Imports of <c>google/**</c> and <c>protobuf-net/**</c> resolve from resources embedded in |
| 11 | +/// protobuf-net.Reflection, so the common cases need no network access and no server. Any other |
| 12 | +/// import fails with protobuf-net's own "unable to find" diagnostic, which is the honest answer: |
| 13 | +/// there is no filesystem here to find it on. |
| 14 | +/// </remarks> |
| 15 | +internal static class Codegen |
| 16 | +{ |
| 17 | + public static GenerateResponse Generate(GenerateRequest request) |
| 18 | + { |
| 19 | + var response = new GenerateResponse(); |
| 20 | + try |
| 21 | + { |
| 22 | + var set = new FileDescriptorSet(); |
| 23 | + string fileName = string.IsNullOrWhiteSpace(request.FileName) ? "my.proto" : request.FileName.Trim(); |
| 24 | + set.Add(fileName, includeInOutput: true, new StringReader(request.Schema ?? "")); |
| 25 | + set.Process(); |
| 26 | + |
| 27 | + bool fatal = false; |
| 28 | + foreach (var error in set.GetErrors()) |
| 29 | + { |
| 30 | + response.Errors.Add(new SchemaError |
| 31 | + { |
| 32 | + IsError = error.IsError, |
| 33 | + LineNumber = error.LineNumber, |
| 34 | + ColumnNumber = error.ColumnNumber, |
| 35 | + Length = error.Text?.Length ?? 0, |
| 36 | + Message = error.Message, |
| 37 | + ErrorNumber = error.ErrorNumber, |
| 38 | + File = error.File ?? "", |
| 39 | + }); |
| 40 | + if (error.IsError) fatal = true; |
| 41 | + } |
| 42 | + |
| 43 | + // warnings are worth showing alongside output; errors mean there is no output to show |
| 44 | + if (fatal) return response; |
| 45 | + |
| 46 | + var generator = ResolveGenerator(request.Language); |
| 47 | + var normalizer = ResolveNormalizer(request.NamingConvention); |
| 48 | + foreach (var file in generator.Generate(set, normalizer, BuildOptions(request))) |
| 49 | + { |
| 50 | + response.Files.Add(new GeneratedFile { Name = file.Name, Text = file.Text }); |
| 51 | + } |
| 52 | + } |
| 53 | + catch (Exception ex) |
| 54 | + { |
| 55 | + response.Exception = $"{ex.GetType().Name}: {ex.Message}"; |
| 56 | + } |
| 57 | + return response; |
| 58 | + } |
| 59 | + |
| 60 | + private static CodeGenerator ResolveGenerator(string? language) => language?.ToLowerInvariant() switch |
| 61 | + { |
| 62 | + "csharp" or "c#" or null or "" => CSharpCodeGenerator.Default, |
| 63 | + "vb" or "vbnet" or "vb.net" => VBCodeGenerator.Default, |
| 64 | + _ => throw new ArgumentException( |
| 65 | + $"'{language}' is not supported. This tool generates C# and VB.NET via protobuf-net's own " + |
| 66 | + "generators; the other languages protoc supports need protoc itself, which cannot run in a browser."), |
| 67 | + }; |
| 68 | + |
| 69 | + private static NameNormalizer ResolveNormalizer(string? convention) => convention?.ToLowerInvariant() switch |
| 70 | + { |
| 71 | + "original" or "none" => NameNormalizer.Null, |
| 72 | + "noplural" => NameNormalizer.NoPlural, |
| 73 | + _ => NameNormalizer.Default, |
| 74 | + }; |
| 75 | + |
| 76 | + private static Dictionary<string, string> BuildOptions(GenerateRequest request) |
| 77 | + { |
| 78 | + var options = new Dictionary<string, string>(); |
| 79 | + if (!string.IsNullOrWhiteSpace(request.LanguageVersion)) options["langver"] = request.LanguageVersion!; |
| 80 | + if (request.Services) options["services"] = "yes"; |
| 81 | + if (request.OneOfEnum) options["oneof"] = "enum"; |
| 82 | + if (request.ListSet) options["listset"] = "yes"; |
| 83 | + if (request.DisableNullWrappers) options["nullwrappers"] = "false"; |
| 84 | + if (request.DisableCompatLevel) options["compatlevel"] = "false"; |
| 85 | + if (request.NullableValueType) options["nullablevaluetype"] = "yes"; |
| 86 | + if (request.RepeatedAsList) options["repeatedaslist"] = "yes"; |
| 87 | + return options; |
| 88 | + } |
| 89 | +} |
0 commit comments