예제 #1
0
        private static IEnumerable <MemberDeclarationSyntax> PaginatedPartialClasses(SourceFileContext ctx, ServiceDetails svc, HashSet <Typ> seenPaginatedResponseTyps)
        {
            var paginatedMethods = svc.Methods.OfType <MethodDetails.Paginated>();

            foreach (var representingMethod in paginatedMethods
                     .GroupBy(m => m.RequestTyp)
                     .Select(typMethodGrp => typMethodGrp.First()))
            {
                yield return(PaginatedPartialInterfaceClass(ctx, representingMethod.RequestTyp, representingMethod.RequestMessageDesc));
            }

            foreach (var method in paginatedMethods)
            {
                if (seenPaginatedResponseTyps.Add(method.ResponseTyp))
                {
                    var cls = Class(Public | Partial, method.ResponseTyp, baseTypes: ctx.Type(Typ.Generic(typeof(IPageResponse <>), method.ResourceTyp)));
                    using (ctx.InClass(cls))
                    {
                        var propertyName         = method.ResourcesFieldName;
                        var genericGetEnumerator = Method(Public, ctx.Type(Typ.Generic(typeof(IEnumerator <>), method.ResourceTyp)), "GetEnumerator")()
                                                   .WithBody(Property(Public, ctx.TypeDontCare, propertyName).Call(nameof(IEnumerable <int> .GetEnumerator))())
                                                   .WithXmlDoc(XmlDoc.Summary("Returns an enumerator that iterates through the resources in this response."));
                        var getEnumerator = Method(None, ctx.Type <IEnumerator>(), "GetEnumerator")()
                                            .WithExplicitInterfaceSpecifier(ctx.Type <IEnumerable>())
                                            .WithBody(This.Call(genericGetEnumerator)());
                        cls = cls.AddMembers(genericGetEnumerator, getEnumerator);
                    }
                    yield return(cls);
                }
            }
        }
                private MethodDeclarationSyntax AbstractRequestMethod(bool sync, bool callSettings, IEnumerable <ParameterInfo> parameters, DocumentationCommentTriviaSyntax returnsXmlDoc = null)
                {
                    var returnTyp        = sync ? MethodDetails.SyncReturnTyp : MethodDetails.AsyncReturnTyp;
                    var methodName       = sync ? MethodDetails.SyncMethodName : MethodDetails.AsyncMethodName;
                    var finalParam       = callSettings ? _def.CallSettingsParam : _def.CancellationTokenParam;
                    var finalParamXmlDoc = callSettings ? _def.CallSettingsXmlDoc : _def.CancellationTokenXmlDoc;

                    returnsXmlDoc = returnsXmlDoc ?? (sync ?
                                                      MethodDetails is MethodDetails.Paginated ? _def.ReturnsSyncPaginatedXmlDoc : _def.ReturnsSyncXmlDoc :
                                                      MethodDetails is MethodDetails.Paginated ? _def.ReturnsAsyncPaginatedXmlDoc : _def.ReturnsAsyncXmlDoc);
                    if (callSettings)
                    {
                        return(Method(Public | Virtual, Ctx.Type(returnTyp), methodName)(parameters.Select(x => x.Parameter).Append(finalParam).ToArray())
                               .MaybeWithAttribute(MethodDetails.IsDeprecated || _sig.HasDeprecatedField, () => Ctx.Type <ObsoleteAttribute>())()
                               .WithBody(This.Call(methodName)(New(Ctx.Type(MethodDetails.RequestTyp))()
                                                               .WithInitializer(NestInit(parameters, 0).ToArray()), finalParam))
                               .WithXmlDoc(parameters.Select(x => x.XmlDoc).Prepend(_def.SummaryXmlDoc).Append(finalParamXmlDoc).Append(returnsXmlDoc).ToArray()));

                        IEnumerable <ObjectInitExpr> NestInit(IEnumerable <ParameterInfo> ps, int ofs)
                        {
                            var byField = ps.GroupBy(x => (x.FieldDescs ?? Enumerable.Empty <FieldDescriptor>()).Skip(ofs).SkipLast(1).FirstOrDefault())
                                          .OrderBy(x => x.Key?.Index);

                            foreach (var f in byField)
                            {
                                if (f.Key == null)
                                {
                                    // No more nesting, these are the actual fields that need filling.
                                    foreach (var param in f.OrderBy(x => x.FieldDescs?.Last().Index ?? int.MaxValue))
                                    {
                                        // Note: even if the signature includes a deprecated field, we don't indicate that in the
                                        // ObjectInitExpr, as we don't want or need the pragma when the method we're generating is obsolete.
                                        yield return((param.InitExpr as ObjectInitExpr) ??
                                                     new ObjectInitExpr(param.ResourcePropertyName ?? param.FieldDescs.Last().CSharpPropertyName(), param.InitExpr));
                                    }
                                }
                                else
                                {
                                    // Nested field.
                                    var code = New(Ctx.Type(ProtoTyp.Of(f.Key)))().WithInitializer(NestInit(f, ofs + 1).ToArray());
                                    yield return(new ObjectInitExpr(f.Key.CSharpPropertyName(), code));
                                }
                            }
                        }
                    }
                    else
                    {
                        return(Method(Public | Virtual, Ctx.Type(returnTyp), methodName)(parameters.Select(x => x.Parameter).Append(finalParam).ToArray())
                               .MaybeWithAttribute(MethodDetails.IsDeprecated || _sig.HasDeprecatedField, () => Ctx.Type <ObsoleteAttribute>())()
                               .WithBody(This.Call(methodName)(parameters.Select(x => (object)x.Parameter).Append(
                                                                   Ctx.Type <CallSettings>().Call(nameof(CallSettings.FromCancellationToken))(finalParam))))
                               .WithXmlDoc(parameters.Select(x => x.XmlDoc).Prepend(_def.SummaryXmlDoc).Append(finalParamXmlDoc).Append(returnsXmlDoc).ToArray()));
                    }
                }
            private IEnumerable <MethodDeclarationSyntax> FormatMethods()
            {
                bool first = true;

                foreach (var pattern in PatternDetails)
                {
                    var xmlDoc = pattern.PathElements.Select(x => x.ParameterXmlDoc)
                                 .Prepend(XmlDoc.Summary("Formats the IDs into the string representation of this ", _ctx.Type(_def.ResourceNameTyp),
                                                         " with pattern ", XmlDoc.C(pattern.PatternString), "."))
                                 .Append(XmlDoc.Returns("The string representation of this ", _ctx.Type(_def.ResourceNameTyp), " with pattern ", XmlDoc.C(pattern.PatternString), "."))
                                 .ToArray();
                    var expandArgs = pattern.PathSegments.Where(x => x.Segment.ParameterCount > 0).Select(x =>
                    {
                        if (x.Segment.IsComplex)
                        {
                            var dollarItems = x.Elements.Zip(x.Segment.Separators.Select(x => x.ToString()).Append(""), (element, sep) => (FormattableString)
                                                             $"{Parens(_ctx.Type(typeof(GaxPreconditions)).Call(nameof(GaxPreconditions.CheckNotNullOrEmpty))(element.Parameter, Nameof(element.Parameter)))}{sep:raw}");
                            return((object)Dollar(dollarItems.ToArray()));
                        }
                        else
                        {
                            return(_ctx.Type(typeof(GaxPreconditions)).Call(nameof(GaxPreconditions.CheckNotNullOrEmpty))(x.Elements[0].Parameter, Nameof(x.Elements[0].Parameter)));
                        }
                    });
                    var method = Method(Public | Static, _ctx.Type <string>(), $"Format{pattern.UpperName}")(pattern.PathElements.Select(x => x.Parameter).ToArray())
                                 .WithBody(Return(pattern.PathTemplateField.Call(nameof(PathTemplate.Expand))(expandArgs)))
                                 .WithXmlDoc(xmlDoc);
                    if (first)
                    {
                        yield return(Method(Public | Static, _ctx.Type <string>(), "Format")(pattern.PathElements.Select(x => x.Parameter).ToArray())
                                     .WithBody(Return(This.Call(method)(pattern.PathElements.Select(x => x.Parameter))))
                                     .WithXmlDoc(xmlDoc));

                        first = false;
                    }
                    yield return(method);
                }
            }
            private IEnumerable <MethodDeclarationSyntax> ParseMethods()
            {
                var paramName            = _def.ResourceNameTyp.Name.ToLowerCamelCase();
                var name                 = Parameter(_ctx.Type <string>(), paramName);
                var allowUnparsed        = Parameter(_ctx.Type <bool>(), "allowUnparsed");
                var result               = Parameter(_ctx.Type(_def.ResourceNameTyp), "result").Out();
                var resultLocal          = Local(_ctx.Type(_def.ResourceNameTyp), "result");
                var resourceName         = Local(_ctx.Type <TemplatedResourceName>(), paramName == "resourceName" ? "resourceName2" : "resourceName");
                var unparsedResourceName = Local(_ctx.Type <UnparsedResourceName>(), "unparsedResourceName");
                MethodDeclarationSyntax parseSplitHelper;

                if (PatternDetails.SelectMany(x => x.PathSegments).Any(x => x.Segment.IsComplex))
                {
                    var s          = Parameter(_ctx.Type <string>(), "s");
                    var separators = Parameter(_ctx.ArrayType <char[]>(), "separators");
                    var i0         = Local(_ctx.Type <int>(), "i0");
                    var i1         = Local(_ctx.Type <int>(), "i1");
                    var i          = Local(_ctx.Type <int>(), "i");
                    var pshResult  = Local(_ctx.ArrayType <string[]>(), "result");
                    parseSplitHelper = Method(Private | Static, _ctx.ArrayType <string[]>(), "ParseSplitHelper")(s, separators)
                                       .WithBody(
                        pshResult.WithInitializer(NewArray(_ctx.ArrayType <string[]>(), separators.Access(nameof(Array.Length)).Plus(1))),
                        i0.WithInitializer(0),
                        For(i.WithInitializer(0), i.LessThanOrEqual(separators.Access(nameof(Array.Length))), i.PlusPlus())(
                            i1.WithInitializer(i.LessThan(separators.Access(nameof(Array.Length))).ConditionalOperator(
                                                   s.Call(nameof(string.IndexOf))(separators.ElementAccess(i), i0), s.Access(nameof(string.Length)))),
                            If(i1.LessThan(0).Or(i1.Equality(i0))).Then(Return(Null)),
                            pshResult.ElementAccess(i).Assign(s.Call(nameof(string.Substring))(i0, i1.Minus(i0))),
                            i0.Assign(i1.Plus(1))),
                        Return(pshResult));
                }
                else
                {
                    parseSplitHelper = null;
                }
                var tryParse2 = Method(Public | Static, _ctx.Type <bool>(), "TryParse")(name, allowUnparsed, result)
                                .WithBody(
                    _ctx.Type(typeof(GaxPreconditions)).Call(nameof(GaxPreconditions.CheckNotNull))(name, Nameof(name)),
                    resourceName,
                    PatternDetails.Zip(FromMethods(), (pattern, create) =>
                {
                    IEnumerable <(object code, IEnumerable <ExpressionSyntax> args)> codeAndArgs = pattern.PathSegments
                                                                                                   .Where(x => x.Segment.ParameterCount > 0)
                                                                                                   .Select((seg, segmentIndex) =>
                    {
                        if (seg.Segment.IsComplex)
                        {
                            var splitResult = Local(_ctx.ArrayType <string[]>(), $"split{segmentIndex}")
                                              .WithInitializer(This.Call(parseSplitHelper)(
                                                                   resourceName.ElementAccess(segmentIndex), NewArray(_ctx.ArrayType <char[]>())(seg.Segment.Separators.ToArray())));
                            var splitIf = If(splitResult.Equality(Null)).Then(
                                result.Assign(Null),
                                Return(false));
                            var args = Enumerable.Range(0, seg.Segment.ParameterCount).Select(i => splitResult.ElementAccess(i));
                            return((object)new object[] { splitResult, splitIf }, args);
                        }
                        else
                        {
                            return((object)null, new[] { resourceName.ElementAccess(segmentIndex) });
                        }
                    });
                    var elements = codeAndArgs.SelectMany(x => x.args);
                    return(If(pattern.PathTemplateField.Call(nameof(PathTemplate.TryParseName))(name, Out(resourceName))).Then(
                               codeAndArgs.Select(x => x.code),
                               result.Assign(This.Call(create)(elements.ToArray())),
                               Return(true)));
                }),