public ServiceDetails(ProtoCatalog catalog, string ns, ServiceDescriptor desc, ServiceConfig grpcServiceConfig)
        {
            Catalog            = catalog;
            Namespace          = ns;
            ProtoPackage       = desc.File.Package;
            DocLines           = desc.Declaration.DocLines().ToList();
            SnippetsNamespace  = $"{ns}.Snippets";
            UnitTestsNamespace = $"{ns}.Tests";
            // Must come early; used by `MethodDetails.Create()`
            MethodGrpcConfigsByName = grpcServiceConfig?.MethodConfig
                                      .SelectMany(conf => conf.Name.Select(name => (name, conf)))
                                      .Where(x => x.name.Service == desc.FullName)
                                      .ToImmutableDictionary(x => $"{x.name.Service}/{x.name.Method}", x => x.conf) ??
                                      ImmutableDictionary <string, MethodConfig> .Empty;
            ServiceFullName   = desc.FullName;
            ServiceName       = desc.Name;
            DocumentationName = desc.Name; // TODO: There may be a more suitable name than this.
            ProtoTyp          = Typ.Manual(ns, desc.Name);
            GrpcClientTyp     = Typ.Nested(ProtoTyp, $"{desc.Name}Client");
            SettingsTyp       = Typ.Manual(ns, $"{desc.Name}Settings");
            BuilderTyp        = Typ.Manual(ns, $"{desc.Name}ClientBuilder");
            ClientAbstractTyp = Typ.Manual(ns, $"{desc.Name}Client");
            ClientImplTyp     = Typ.Manual(ns, $"{desc.Name}ClientImpl");
            DefaultHost       = desc.GetExtension(ClientExtensions.DefaultHost) ?? "";
            DefaultPort       = 443; // Hardcoded; this is not specifiable by proto annotation.
            var oauthScopes = desc.GetExtension(ClientExtensions.OauthScopes);

            DefaultScopes         = string.IsNullOrEmpty(oauthScopes) ? Enumerable.Empty <string>() : oauthScopes.Split(',', ' ');
            Methods               = desc.Methods.Select(x => MethodDetails.Create(this, x)).ToList();
            SnippetsTyp           = Typ.Manual(SnippetsNamespace, $"Generated{desc.Name}ClientSnippets");
            StandaloneSnippetsTyp = Typ.Manual(SnippetsNamespace, $"Generated{desc.Name}ClientStandaloneSnippets");
            SnippetsClientName    = $"{desc.Name.ToLowerCamelCase()}Client";
            UnitTestsTyp          = Typ.Manual(UnitTestsNamespace, $"Generated{desc.Name}ClientTest");
            NonStandardLro        = NonStandardLroDetails.ForService(desc);
        }
        public ServiceDetails(ProtoCatalog catalog, string ns, ServiceDescriptor desc, ServiceConfig grpcServiceConfig, Service serviceConfig, ApiTransports transports)
        {
            Catalog            = catalog;
            Namespace          = ns;
            ProtoPackage       = desc.File.Package;
            PackageVersion     = ProtoPackage.Split('.').FirstOrDefault(part => ApiVersionPattern.IsMatch(part));
            DocLines           = desc.Declaration.DocLines().ToList();
            SnippetsNamespace  = $"{ns}.Snippets";
            UnitTestsNamespace = $"{ns}.Tests";
            // Must come early; used by `MethodDetails.Create()`
            MethodGrpcConfigsByName = grpcServiceConfig?.MethodConfig
                                      .SelectMany(conf => conf.Name.Select(name => (name, conf)))
                                      .Where(x => x.name.Service == desc.FullName)
                                      .ToImmutableDictionary(x => $"{x.name.Service}/{x.name.Method}", x => x.conf) ??
                                      ImmutableDictionary <string, MethodConfig> .Empty;
            ServiceFullName   = desc.FullName;
            ServiceName       = desc.Name;
            DocumentationName = desc.Name; // TODO: There may be a more suitable name than this.
            ProtoTyp          = Typ.Manual(ns, desc.Name);
            GrpcClientTyp     = Typ.Nested(ProtoTyp, $"{desc.Name}Client");
            SettingsTyp       = Typ.Manual(ns, $"{desc.Name}Settings");
            BuilderTyp        = Typ.Manual(ns, $"{desc.Name}ClientBuilder");
            ClientAbstractTyp = Typ.Manual(ns, $"{desc.Name}Client");
            ClientImplTyp     = Typ.Manual(ns, $"{desc.Name}ClientImpl");
            DefaultHost       = desc.GetExtension(ClientExtensions.DefaultHost) ?? "";
            // We need to account for regional default endpoints like "us-east1-pubsub.googleapis.com"
            DefaultHostServiceName = DefaultHost
                                     .Split('.', StringSplitOptions.RemoveEmptyEntries).FirstOrDefault()
                                     ?.Split('-', StringSplitOptions.RemoveEmptyEntries).LastOrDefault();
            DefaultPort = 443; // Hardcoded; this is not specifiable by proto annotation.
            var oauthScopes = desc.GetExtension(ClientExtensions.OauthScopes);

            DefaultScopes      = string.IsNullOrEmpty(oauthScopes) ? Enumerable.Empty <string>() : oauthScopes.Split(',', ' ');
            Methods            = desc.Methods.Select(x => MethodDetails.Create(this, x)).ToList();
            ServiceSnippetsTyp = Typ.Manual(SnippetsNamespace, $"AllGenerated{desc.Name}ClientSnippets");
            SnippetsTyp        = Typ.Manual(SnippetsNamespace, $"Generated{desc.Name}ClientSnippets");
            SnippetsClientName = $"{desc.Name.ToLowerCamelCase()}Client";
            UnitTestsTyp       = Typ.Manual(UnitTestsNamespace, $"Generated{desc.Name}ClientTest");
            NonStandardLro     = NonStandardLroDetails.ForService(desc);
            Mixins             = serviceConfig?.Apis
                                 .Select(api => AvailableMixins.GetValueOrDefault(api.Name))
                                 .Where(mixin => mixin is object)
                                 // Don't use mixins within the package that contains that mixin.
                                 .Where(mixin => mixin.GapicClientType.Namespace != ns)
                                 .ToList() ?? Enumerable.Empty <MixinDetails>();
            Transports = transports;
        }