private static IEnumerable <ResultFile> GeneratePackage(string ns, IEnumerable <FileDescriptor> packageFileDescriptors, ProtoCatalog catalog, IClock clock, ServiceConfig grpcServiceConfig, Service serviceConfig, List <ServiceDetails> allServiceDetails, ApiTransports transports) { var clientPathPrefix = $"{ns}{Path.DirectorySeparatorChar}"; var serviceSnippetsPathPrefix = $"{ns}.Snippets{Path.DirectorySeparatorChar}"; var snippetsPathPrefix = $"{ns}.GeneratedSnippets{Path.DirectorySeparatorChar}"; var unitTestsPathPrefix = $"{ns}.Tests{Path.DirectorySeparatorChar}"; bool hasLro = false; HashSet <string> mixins = new HashSet <string>(); bool hasContent = false; var packageServiceDetails = new List <ServiceDetails>(); HashSet <string> allResourceNameClasses = new HashSet <string>(); HashSet <string> duplicateResourceNameClasses = new HashSet <string>(); IList <Snippet> snippets = new List <Snippet>(); IEnumerable <Typ> packageTyps = packageFileDescriptors.SelectMany( fileDescriptor => fileDescriptor.Services.Select(serv => Typ.Manual(ns, serv.Name)) .Union(fileDescriptor.MessageTypes.Select(msg => Typ.Manual(ns, msg.Name))) .Union(fileDescriptor.EnumTypes.Select(e => Typ.Manual(ns, e.Name, isEnum: true)))); var seenPaginatedResponseTyps = new HashSet <Typ>(); foreach (var fileDesc in packageFileDescriptors) { foreach (var service in fileDesc.Services) { // Generate settings and client code for requested package. var serviceDetails = new ServiceDetails(catalog, ns, service, grpcServiceConfig, serviceConfig, transports); packageServiceDetails.Add(serviceDetails); var ctx = SourceFileContext.CreateFullyAliased(clock, s_wellknownNamespaceAliases); var code = ServiceCodeGenerator.Generate(ctx, serviceDetails, seenPaginatedResponseTyps); var filename = $"{clientPathPrefix}{serviceDetails.ClientAbstractTyp.Name}.g.cs"; yield return(new ResultFile(filename, code)); // Generate service-per-file snippets for the service // TODO: Consider removing this once we have integrated the snippet-per-file snippets // with docs generation. var serviceSnippetsCtx = SourceFileContext.CreateUnaliased( clock, s_wellknownNamespaceAliases, s_avoidAliasingNamespaceRegex, packageTyps, maySkipOwnNamespaceImport: true); var serviceSnippetsCode = SnippetCodeGenerator.Generate(serviceSnippetsCtx, serviceDetails); var serviceSnippetsFilename = $"{serviceSnippetsPathPrefix}{serviceDetails.ClientAbstractTyp.Name}Snippets.g.cs"; yield return(new ResultFile(serviceSnippetsFilename, serviceSnippetsCode)); // Generate snippet-per-file snippets for the service // TODO: Integrate snippet-per-file snippets with docs generation. foreach (var snippetGenerator in SnippetCodeGenerator.SnippetsGenerators(serviceDetails)) { var snippetCtx = SourceFileContext.CreateUnaliased( clock, s_wellknownNamespaceAliases, s_avoidAliasingNamespaceRegex, packageTyps, maySkipOwnNamespaceImport: false); var(snippetCode, snippetMetadata) = snippetGenerator.Generate(snippetCtx); snippetMetadata.File = $"{serviceDetails.ClientAbstractTyp.Name}.{snippetGenerator.SnippetMethodName}Snippet.g.cs"; var snippetFile = $"{snippetsPathPrefix}{snippetMetadata.File}"; snippets.Add(snippetMetadata); yield return(new ResultFile(snippetFile, snippetCode)); } // Generate unit tests for the the service. var unitTestCtx = SourceFileContext.CreateFullyAliased(clock, s_wellknownNamespaceAliases); var unitTestCode = UnitTestCodeGeneration.Generate(unitTestCtx, serviceDetails); var unitTestFilename = $"{unitTestsPathPrefix}{serviceDetails.ClientAbstractTyp.Name}Test.g.cs"; yield return(new ResultFile(unitTestFilename, unitTestCode)); // Record whether LRO/mixins are used. hasLro |= serviceDetails.Methods.Any(x => x is MethodDetails.Lro); foreach (var mixin in serviceDetails.Mixins) { mixins.Add(mixin.GrpcServiceName); } hasContent = true; } var resCtx = SourceFileContext.CreateFullyAliased(clock, s_wellknownNamespaceAliases); var(resCode, resCodeClassCount) = ResourceNamesGenerator.Generate(catalog, resCtx, fileDesc); // Only produce an output file if it contains >0 [partial] classes. if (resCodeClassCount > 0) { // Keep track of the resource names, to spot duplicates var resourceNameClasses = catalog.GetResourceDefsByFile(fileDesc) .Where(def => def.HasNotWildcard && !def.IsCommon) .Select(def => def.ResourceNameTyp.Name); foreach (var resourceNameClass in resourceNameClasses) { if (!allResourceNameClasses.Add(resourceNameClass)) { duplicateResourceNameClasses.Add(resourceNameClass); } } // Yield the file to be generated. var filenamePrefix = Path.GetFileNameWithoutExtension(fileDesc.Name).ToUpperCamelCase(); var resFilename = $"{clientPathPrefix}{filenamePrefix}ResourceNames.g.cs"; yield return(new ResultFile(resFilename, resCode)); hasContent = true; } var lroAdaptationCtx = SourceFileContext.CreateFullyAliased(clock, s_wellknownNamespaceAliases); var(lroCode, lroClassCount) = LroAdaptationGenerator.Generate(catalog, lroAdaptationCtx, fileDesc); if (lroClassCount > 0) { var filenamePrefix = Path.GetFileNameWithoutExtension(fileDesc.Name).ToUpperCamelCase(); var lroAdaptationFilename = $"{clientPathPrefix}{filenamePrefix}LroAdaptation.g.cs"; yield return(new ResultFile(lroAdaptationFilename, lroCode)); hasContent = true; } } // Now we've processed all the files, check for duplicate resource names. if (duplicateResourceNameClasses.Count > 0) { throw new InvalidOperationException($"The following resource name classes were created multiple times: {string.Join(", ", duplicateResourceNameClasses)}"); } // Only output csproj's and snippet metadata if there is any other generated content. // When processing a (proto) package without any services there will be no generated content. if (hasContent) { // Generate client csproj. var csprojContent = CsProjGenerator.GenerateClient(hasLro, mixins); var csprojFilename = $"{clientPathPrefix}{ns}.csproj"; yield return(new ResultFile(csprojFilename, csprojContent)); // If we only generated resources, we don't need to generate all of these. if (packageServiceDetails.Count > 0) { allServiceDetails.AddRange(packageServiceDetails); // Generate the package-wide API metadata var ctx = SourceFileContext.CreateFullyAliased(clock, s_wellknownNamespaceAliases); var packageApiMetadataContent = PackageApiMetadataGenerator.GeneratePackageApiMetadata(ns, ctx, packageFileDescriptors); var packageApiMetadataFilename = $"{clientPathPrefix}{PackageApiMetadataGenerator.FileName}"; yield return(new ResultFile(packageApiMetadataFilename, packageApiMetadataContent)); // Generate snippets csproj. var serviceSnippetsCsprojContent = CsProjGenerator.GenerateSnippets(ns); var serviceSnippetsCsProjFilename = $"{serviceSnippetsPathPrefix}{ns}.Snippets.csproj"; yield return(new ResultFile(serviceSnippetsCsProjFilename, serviceSnippetsCsprojContent)); // Generate dependency injection extension methods var dependencyInjectionExtensionsContent = ServiceCollectionExtensionsGenerator.GenerateExtensions(ctx, packageServiceDetails); var dependencyInjectionExtensionsFilename = $"{clientPathPrefix}{ServiceCollectionExtensionsGenerator.FileName}"; yield return(new ResultFile(dependencyInjectionExtensionsFilename, dependencyInjectionExtensionsContent)); // Generate snippet metadata (only for snippet-per-file snippets). // All services in this package have the same package information, namespace etc. so, we // just pick the first one var serviceDetails = packageServiceDetails.First(); var snippetIndexJsonContent = SnippetCodeGenerator.GenerateSnippetIndexJson(snippets, serviceDetails); yield return(new ResultFile($"{snippetsPathPrefix}snippet_metadata_{serviceDetails.ProtoPackage}.json", snippetIndexJsonContent)); // Generate snippet-per-file snippets csproj. var snippetsCsprojContent = CsProjGenerator.GenerateSnippets(ns); var snippetsCsProjFilename = $"{snippetsPathPrefix}{ns}.GeneratedSnippets.csproj"; yield return(new ResultFile(snippetsCsProjFilename, snippetsCsprojContent)); // Generate unit-tests csproj. var unitTestsCsprojContent = CsProjGenerator.GenerateUnitTests(ns); var unitTestsCsprojFilename = $"{unitTestsPathPrefix}{ns}.Tests.csproj"; yield return(new ResultFile(unitTestsCsprojFilename, unitTestsCsprojContent)); } } }
private static IEnumerable <ResultFile> GeneratePackage(string ns, IEnumerable <FileDescriptor> packageFileDescriptors, ProtoCatalog catalog, IClock clock, ServiceConfig grpcServiceConfig, bool generateMetadata) { var clientPathPrefix = $"{ns}{Path.DirectorySeparatorChar}"; var snippetsPathPrefix = $"{ns}.Snippets{Path.DirectorySeparatorChar}"; var standaloneSnippetsPathPrefix = $"{ns}.StandaloneSnippets{Path.DirectorySeparatorChar}"; var unitTestsPathPrefix = $"{ns}.Tests{Path.DirectorySeparatorChar}"; bool hasLro = false; bool hasContent = false; HashSet <string> allResourceNameClasses = new HashSet <string>(); HashSet <string> duplicateResourceNameClasses = new HashSet <string>(); var allServiceDetails = new List <ServiceDetails>(); foreach (var fileDesc in packageFileDescriptors) { foreach (var service in fileDesc.Services) { // Generate settings and client code for requested package. var serviceDetails = new ServiceDetails(catalog, ns, service, grpcServiceConfig); allServiceDetails.Add(serviceDetails); var ctx = SourceFileContext.CreateFullyAliased(clock, s_wellknownNamespaceAliases); var code = ServiceCodeGenerator.Generate(ctx, serviceDetails); var filename = $"{clientPathPrefix}{serviceDetails.ClientAbstractTyp.Name}.g.cs"; yield return(new ResultFile(filename, code)); // Generate snippets for the service // TODO: Consider removing this once we have integrated the standalone snippets // with docs generation. var snippetCtx = SourceFileContext.CreateUnaliased( clock, s_wellknownNamespaceAliases, s_avoidAliasingNamespaceRegex, maySkipOwnNamespaceImport: true); var snippetCode = SnippetCodeGenerator.Generate(snippetCtx, serviceDetails); var snippetFilename = $"{snippetsPathPrefix}{serviceDetails.ClientAbstractTyp.Name}Snippets.g.cs"; yield return(new ResultFile(snippetFilename, snippetCode)); // Generate standalone snippets for the service // TODO: Integrate standalone snippets with docs generation. // TODO: Once (and if we) generate just one set of snippets, stop using "standalone" as a differentiatior. foreach (var snippetGenerator in SnippetCodeGenerator.StandaloneGenerators(serviceDetails)) { var standaloneSnippetCtx = SourceFileContext.CreateUnaliased( clock, s_wellknownNamespaceAliases, s_avoidAliasingNamespaceRegex, maySkipOwnNamespaceImport: false); var standaloneSnippetCode = snippetGenerator.Generate(standaloneSnippetCtx); var standaloneSnippetFilename = $"{standaloneSnippetsPathPrefix}{serviceDetails.ClientAbstractTyp.Name}.{snippetGenerator.SnippetMethodName}Snippet.g.cs"; yield return(new ResultFile(standaloneSnippetFilename, standaloneSnippetCode)); } // Generate unit tests for the the service. var unitTestCtx = SourceFileContext.CreateFullyAliased(clock, s_wellknownNamespaceAliases); var unitTestCode = UnitTestCodeGeneration.Generate(unitTestCtx, serviceDetails); var unitTestFilename = $"{unitTestsPathPrefix}{serviceDetails.ClientAbstractTyp.Name}Test.g.cs"; yield return(new ResultFile(unitTestFilename, unitTestCode)); // Record whether LRO is used. hasLro |= serviceDetails.Methods.Any(x => x is MethodDetails.Lro); hasContent = true; } var resCtx = SourceFileContext.CreateFullyAliased(clock, s_wellknownNamespaceAliases); var(resCode, resCodeClassCount) = ResourceNamesGenerator.Generate(catalog, resCtx, fileDesc); // Only produce an output file if it contains >0 [partial] classes. if (resCodeClassCount > 0) { // Keep track of the resource names, to spot duplicates var resourceNameClasses = catalog.GetResourceDefsByFile(fileDesc) .Where(def => def.HasNotWildcard && !def.IsCommon) .Select(def => def.ResourceNameTyp.Name); foreach (var resourceNameClass in resourceNameClasses) { if (!allResourceNameClasses.Add(resourceNameClass)) { duplicateResourceNameClasses.Add(resourceNameClass); } } // Yield the file to be generated. var filenamePrefix = Path.GetFileNameWithoutExtension(fileDesc.Name).ToUpperCamelCase(); var resFilename = $"{clientPathPrefix}{filenamePrefix}ResourceNames.g.cs"; yield return(new ResultFile(resFilename, resCode)); hasContent = true; } } // Now we've processed all the files, check for duplicate resource names. if (duplicateResourceNameClasses.Count > 0) { throw new InvalidOperationException($"The following resource name classes were created multiple times: {string.Join(", ", duplicateResourceNameClasses)}"); } // Only output csproj's if there is any other generated content. // When processing a (proto) package without any services there will be no generated content. if (hasContent) { // Generate client csproj. var csprojContent = CsProjGenerator.GenerateClient(hasLro); var csprojFilename = $"{clientPathPrefix}{ns}.csproj"; yield return(new ResultFile(csprojFilename, csprojContent)); // Generate snippets csproj. var snippetsCsprojContent = CsProjGenerator.GenerateSnippets(ns); var snippetsCsProjFilename = $"{snippetsPathPrefix}{ns}.Snippets.csproj"; yield return(new ResultFile(snippetsCsProjFilename, snippetsCsprojContent)); // Generate standalone snippets csproj. var standaloneSnippetsCsprojContent = CsProjGenerator.GenerateSnippets(ns); var standaloneSnippetsCsProjFilename = $"{standaloneSnippetsPathPrefix}{ns}.StandaloneSnippets.csproj"; yield return(new ResultFile(standaloneSnippetsCsProjFilename, standaloneSnippetsCsprojContent)); // Generate unit-tests csproj. var unitTestsCsprojContent = CsProjGenerator.GenerateUnitTests(ns); var unitTestsCsprojFilename = $"{unitTestsPathPrefix}{ns}.Tests.csproj"; yield return(new ResultFile(unitTestsCsprojFilename, unitTestsCsprojContent)); if (generateMetadata && allServiceDetails.Any()) { // Generate gapic_metadata.json, if there are any services. var gapicMetadataJsonContent = MetadataGenerator.GenerateGapicMetadataJson(allServiceDetails); yield return(new ResultFile("gapic_metadata.json", gapicMetadataJsonContent)); } } }
private static IEnumerable <ResultFile> GeneratePackage(string ns, IEnumerable <FileDescriptor> packageFileDescriptors, ProtoCatalog catalog, IClock clock, ServiceConfig grpcServiceConfig) { var clientPathPrefix = $"{ns}{Path.DirectorySeparatorChar}"; var snippetsPathPrefix = $"{ns}.Snippets{Path.DirectorySeparatorChar}"; var unitTestsPathPrefix = $"{ns}.Tests{Path.DirectorySeparatorChar}"; bool hasLro = false; bool hasContent = false; foreach (var fileDesc in packageFileDescriptors) { foreach (var service in fileDesc.Services) { // Generate settings and client code for requested package. var serviceDetails = new ServiceDetails(catalog, ns, service, grpcServiceConfig); var ctx = SourceFileContext.CreateFullyAliased(clock, s_wellknownNamespaceAliases); var code = ServiceCodeGenerator.Generate(ctx, serviceDetails); var filename = $"{clientPathPrefix}{serviceDetails.ClientAbstractTyp.Name}.g.cs"; yield return(new ResultFile(filename, code)); // Generate snippets for the service var snippetCtx = SourceFileContext.CreateUnaliased(clock); var snippetCode = SnippetCodeGenerator.Generate(snippetCtx, serviceDetails); var snippetFilename = $"{snippetsPathPrefix}{serviceDetails.ClientAbstractTyp.Name}Snippets.g.cs"; yield return(new ResultFile(snippetFilename, snippetCode)); // Generate unit tests for the the service. var unitTestCtx = SourceFileContext.CreateFullyAliased(clock, s_wellknownNamespaceAliases); var unitTestCode = UnitTestCodeGeneration.Generate(unitTestCtx, serviceDetails); var unitTestFilename = $"{unitTestsPathPrefix}{serviceDetails.ClientAbstractTyp.Name}Test.g.cs"; yield return(new ResultFile(unitTestFilename, unitTestCode)); // Record whether LRO is used. hasLro |= serviceDetails.Methods.Any(x => x is MethodDetails.Lro); hasContent = true; } var resCtx = SourceFileContext.CreateFullyAliased(clock, s_wellknownNamespaceAliases); var(resCode, resCodeClassCount) = ResourceNamesGenerator.Generate(catalog, resCtx, fileDesc); // Only produce an output file if it contains >0 [partial] classes. if (resCodeClassCount > 0) { var filenamePrefix = Path.GetFileNameWithoutExtension(fileDesc.Name).ToUpperCamelCase(); var resFilename = $"{clientPathPrefix}{filenamePrefix}ResourceNames.g.cs"; yield return(new ResultFile(resFilename, resCode)); hasContent = true; } } // Only output csproj's if there is any other generated content. // When processing a (proto) package without any services there will be no generated content. if (hasContent) { // Generate client csproj. var csprojContent = CsProjGenerator.GenerateClient(hasLro); var csprojFilename = $"{clientPathPrefix}{ns}.csproj"; yield return(new ResultFile(csprojFilename, csprojContent)); // Generate snippets csproj. var snippetsCsprojContent = CsProjGenerator.GenerateSnippets(ns); var snippetsCsProjFilename = $"{snippetsPathPrefix}{ns}.Snippets.csproj"; yield return(new ResultFile(snippetsCsProjFilename, snippetsCsprojContent)); // Generate unit-tests csproj. var unitTestsCsprojContent = CsProjGenerator.GenerateUnitTests(ns); var unitTestsCsprojFilename = $"{unitTestsPathPrefix}{ns}.Tests.csproj"; yield return(new ResultFile(unitTestsCsprojFilename, unitTestsCsprojContent)); } }