예제 #1
0
        async Task <TypeResolver> PrepareAsync()
        {
            var trees = _trees.ToList();
            //TODO: Temp workaround.. investigate why referencing annotations assembly is nor working properly
            var attributes = CSharpSyntaxTree.ParseText(@"
using System;

namespace WebTyped.Annotations {
	[AttributeUsage(AttributeTargets.Class)]
	public class ClientTypeAttribute : Attribute {
		public ClientTypeAttribute(string typeName = null, string module = null) {}
	}
	[AttributeUsage(AttributeTargets.Parameter)]
	public class NamedTupleAttribute : Attribute {
		public NamedTupleAttribute() {}
	}
}

namespace System.Web.Http {
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Parameter, AllowMultiple = false)]
	public class FromUriAttribute : Attribute {
		public string Name { get; set; }
		public Type BinderType { get; set; }
		public bool SuppressPrefixCheck { get; set; }
	}

	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Parameter, AllowMultiple = false)]
	public class FromBodyAttribute : Attribute {}
}

namespace Microsoft.AspNetCore.Mvc{
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Parameter, AllowMultiple = false)]
	public class FromQueryAttribute : Attribute {
		public string Name { get; set; }
	}

	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Parameter, AllowMultiple = false)]
	public class FromRouteAttribute : Attribute {
		public string Name { get; set; }
	}

	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Parameter, AllowMultiple = false)]
	public class FromBodyAttribute : Attribute {}
}
");
            //References
            var mscorlib = MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
            //var webTypedAnnotations = MetadataReference.CreateFromFile(typeof(ClientTypeAttribute).Assembly.Location);
            var systemRuntime   = MetadataReference.CreateFromFile(typeof(int).Assembly.Location);
            var linqExpressions = MetadataReference.CreateFromFile(typeof(IQueryable).Assembly.Location);
            //Nullable
            var thisAssembly = MetadataReference.CreateFromFile(this.GetType().Assembly.Location);

            //External assemblies
            var externals = new List <PortableExecutableReference>();

            foreach (var path in assemblies)
            {
                if (File.Exists(path))
                {
                    externals.Add(MetadataReference.CreateFromFile(path));
                }
            }

            //var nugetGlobalPackages = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
            //     ? "%userprofile%/.nuget/packages"
            //     : "~/.nuget/packages";
            if (packages.Any())
            {
                //Finding nuget global-packages
                //var process = Process.Start("dotnet", "nuget locals global-packages --list");
                //process.BeginOutputReadLine();
                Process process = new Process
                {
                    StartInfo =
                    {
                        FileName               = "dotnet",
                        Arguments              = "nuget locals global-packages --list",
                        UseShellExecute        = false,
                        RedirectStandardOutput = true,
                        RedirectStandardError  = true,
                        CreateNoWindow         = true
                    }
                };
                string globalPackagePath = "";
                process.EnableRaisingEvents = true;
                process.OutputDataReceived += (s, e) => {
                    if (string.IsNullOrWhiteSpace(globalPackagePath))
                    {
                        globalPackagePath = e.Data;
                    }
                };
                process.ErrorDataReceived += (s, e) => Debug.WriteLine($@"Error: {e.Data}");
                process.Start();
                process.BeginOutputReadLine();
                process.BeginErrorReadLine();
                process.WaitForExit();
                globalPackagePath = globalPackagePath.Replace("info : global-packages: ", "");

                if (string.IsNullOrWhiteSpace(globalPackagePath))
                {
                    Console.WriteLine($"Nuget global-packages not found");
                }
                else
                {
                    foreach (var pkg in packages)
                    {
                        if (string.IsNullOrWhiteSpace(pkg.Version) && string.IsNullOrWhiteSpace(pkg.Csproj))
                        {
                            throw new Exception($"{pkg.Name} version not informed");
                        }

                        var version = pkg.Version;
                        if (!string.IsNullOrWhiteSpace(pkg.Csproj))
                        {
                            var reference = XDocument
                                            .Load(pkg.Csproj)
                                            .Descendants()
                                            .FirstOrDefault(d => d.Name.LocalName == "PackageReference" && d.Attribute("Include").Value == pkg.Name);

                            if (reference != null)
                            {
                                version = reference.Attribute("Version").Value;
                            }
                        }



                        //var nugetGlobalPackages = $"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}/.nuget/packages";
                        var pkgDir = $"{globalPackagePath}{pkg.Name}/{version}".ToLower();

                        if (!Directory.Exists(pkgDir))
                        {
                            Console.WriteLine($"Package {pkg.Name} not found in {pkgDir}");
                            continue;
                        }

                        var dllPaths = Directory.GetFiles(pkgDir, $"*.dll", SearchOption.AllDirectories);
                        var grouped  = dllPaths.GroupBy(path => Path.GetFileName(path));
                        foreach (var g in grouped)
                        {
                            externals.Add(MetadataReference.CreateFromFile(g.First()));
                        }

                        //if(dllPath != null)
                        //{
                        //    externals.Add(MetadataReference.CreateFromFile(dllPath));
                        //}
                    }
                }
            }

            var compilation = CSharpCompilation.Create(
                "Comp",
                //Trees
                trees
                .Union(new SyntaxTree[] { attributes })
                ,
                //basic + external Assemblies
                new[] {
                mscorlib,
                systemRuntime,
                linqExpressions,
                //MetadataReference.CreateFromFile(typeof(Attribute).Assembly.Location),
                //thisAssembly
                /*, webTypedAnnotations*/
            }
                .Union(externals)
                );
            var typeResolver     = new TypeResolver(_options);
            var tasks            = new List <Task>();
            var namedTypeSymbols = new ConcurrentBag <INamedTypeSymbol>();
            var semanticModels   = trees.ToDictionary(t => t, t => compilation.GetSemanticModel(t));

            foreach (var t in trees)
            {
                tasks.Add(t.GetRootAsync().ContinueWith(tks =>
                {
                    var root = tks.Result;
                    foreach (var @type in root.DescendantNodes().OfType <BaseTypeDeclarationSyntax>())
                    {
                        var sm = semanticModels[t];
                        namedTypeSymbols.Add(sm.GetDeclaredSymbol(@type));
                    }
                }));
            }

            //Types in referenced assemblies
            var assembliesTypesMatcher = new Matcher();

            referenceTypes.ToList().ForEach(t => assembliesTypesMatcher.AddInclude(t));
            //var allSymbols = compilation.GetSymbolsWithName((str) => true);


            foreach (var e in externals)
            {
                //var ass = Assembly.ReflectionOnlyLoadFrom(Path.GetFullPath(e.FilePath));
                var assSymbol = compilation.GetAssemblyOrModuleSymbol(e) as IAssemblySymbol;

                //var tm = assSymbol.GlobalNamespace.GetTypeMembers();
                var named = GetNamedTypeSymbols(assSymbol.GlobalNamespace);// assSymbol.GlobalNamespace.GetMembers().OfType<INamedTypeSymbol>();
                //var fullNames = named.Select(n => n.ToString().Split(" ").Last());
                foreach (var n in named)
                {
                    try
                    {
                        var fullName = n.ToString().Split(" ").Last();
                        if (assembliesTypesMatcher.Match(fullName).HasMatches)
                        {
                            namedTypeSymbols.Add(n);
                        }
                    }
                    catch { }
                }

                //foreach(var fn in fullNames)
                //{
                //    try
                //    {
                //        if (assembliesTypesMatcher.Match(fn).HasMatches)
                //        {
                //            var name = fn;

                //            var nts = assSymbol.GetTypeByMetadataName(name);
                //            namedTypeSymbols.Add(nts);
                //        }
                //    }
                //    catch { }
                //}

                //var named = assSymbol.GlobalNamespace.GetMembers().OfType<INamedTypeSymbol>();

                //var typeNames = ass.GetTypes().Select(t => t.FullName);

                //foreach(var tn in typeNames)
                //{
                //    try
                //    {
                //        if (assembliesTypesMatcher.Match(tn).HasMatches)
                //        {
                //            namedTypeSymbols.Add(assSymbol.GetTypeByMetadataName(tn));
                //        }
                //    }
                //    catch { }
                //}
            }



            foreach (var tsk in tasks)
            {
                await tsk;
            }
            foreach (var s in namedTypeSymbols)
            {
                if (Service.IsService(s))
                {
                    typeResolver.Add(new Service(s, typeResolver, _options));
                    continue;
                }

                if (Model.IsModel(s))
                {
                    typeResolver.Add(new Model(s, typeResolver, _options));
                    continue;
                }

                if (TsEnum.IsEnum(s))
                {
                    typeResolver.Add(new TsEnum(s, typeResolver, _options));
                    continue;
                }
            }
            return(typeResolver);
        }
예제 #2
0
        public ParameterResolution(IParameterSymbol parameterSymbol, TypeResolver typeResolver, ResolutionContext context, Options options)
        {
            var p    = parameterSymbol;
            var type = p.Type;
            //it it is generic type, try get some constraint type
            var cTypes = (type as ITypeParameterSymbol)?.ConstraintTypes;

            if (cTypes.HasValue)
            {
                type = cTypes.Value.First();
            }

            var attrs = p.GetAttributes();

            //Removing tuple support...it' not worth it
            //var hasNamedTupleAttr = attrs.Any(a => a.AttributeClass.Name == nameof(NamedTupleAttribute));
            var res = typeResolver.Resolve(type, context /*, hasNamedTupleAttr*/);

            this.Name              = p.Name;
            this.FromName          = p.Name;
            this.SearchRelayFormat = this.Name;
            var fromAttr = attrs.FirstOrDefault(a => a.AttributeClass.Name.StartsWith("From"));

            //
            if (fromAttr != null)
            {
                switch (fromAttr.AttributeClass.Name)
                {
                case "FromUriAttribute":
                case "FromQueryAttribute":
                case "FromBodyAttribute":
                case "FromRouteAttribute":
                    this.From = (ParameterFromKind)typeof(ParameterFromKind).GetField(fromAttr.AttributeClass.Name.Replace("Attribute", "")).GetValue(null);
                    switch (fromAttr.AttributeClass.Name)
                    {
                    case "FromUriAttribute":
                    case "FromQueryAttribute":
                        KeyValuePair <string, TypedConstant>?nameArg = fromAttr.NamedArguments.ToList().FirstOrDefault(na => na.Key == "Name");
                        if (nameArg.HasValue)
                        {
                            var tConst = nameArg.Value.Value;
                            if (tConst.Value != null)
                            {
                                this.FromName          = tConst.Value.ToString();
                                this.SearchRelayFormat = $"{this.FromName}: {this.Name}";
                            }
                        }
                        break;
                    }

                    break;
                }
            }

            //Check if it is a Model being used to catch query/route parameters
            if (type.IsReferenceType)
            {
                var            props            = new List <string>();
                var            outProps         = new List <string>();
                var            hasModifications = false;
                List <ISymbol> members          = new List <ISymbol>();
                var            t = type;
                while (t != null)
                {
                    members.AddRange(t.GetMembers());
                    t = t.BaseType;
                }

                foreach (var m in members)
                {
                    if (m.Kind != SymbolKind.Field && m.Kind != SymbolKind.Property)
                    {
                        continue;
                    }
                    if (m.DeclaredAccessibility != Accessibility.Public)
                    {
                        continue;
                    }
                    if (((m as IFieldSymbol)?.IsConst).GetValueOrDefault())
                    {
                        continue;
                    }
                    var name = m.Name;
                    if (!options.KeepPropsCase && !((m as IFieldSymbol)?.IsConst).GetValueOrDefault())
                    {
                        name = name.ToCamelCase();
                    }
                    if (m is IPropertySymbol)
                    {
                        //allProps.Add(m.Name);
                        var propAttrs    = m.GetAttributes();
                        var propFromAttr = propAttrs.FirstOrDefault(a => a.AttributeClass.Name.StartsWith("From"));
                        if (propFromAttr != null)
                        {
                            switch (propFromAttr.AttributeClass.Name)
                            {
                            case "FromRouteAttribute":
                                hasModifications = true;
                                //ignoredQueryProps.Add(m.Name);
                                break;

                            case "FromUriAttribute":
                            case "FromQueryAttribute":
                                KeyValuePair <string, TypedConstant>?nameArg = propFromAttr.NamedArguments.ToList().FirstOrDefault(na => na.Key == "Name");
                                if (nameArg.HasValue)
                                {
                                    var tConst = nameArg.Value.Value;
                                    if (tConst.Value != null)
                                    {
                                        //renamedQueryProps.Add(p.Name, tConst.Value.ToString());
                                        outProps.Add($"{tConst.Value.ToString()}: {this.Name}.{name}");
                                        hasModifications = true;
                                    }
                                }
                                break;

                            default:
                                props.Add($"{name}: {this.Name}.{name}");
                                break;
                            }
                        }
                        else
                        {
                            props.Add($"{name}: {this.Name}.{name}");
                        }
                    }
                }

                if (hasModifications)
                {
                    this.SearchRelayFormat = "";
                    if (props.Any())
                    {
                        //Pensar melhor, no asp.net podemos colocar como parametros varias models com props de nomes iguais.
                        //Por esse motivo fazemos o obj: {}
                        //Ao mesmo tempo, isso é uma modelagem esquisita de api. Talvez devemos dar preferencia mesmo para a segunda opção
                        //onde fica tudo na "raiz". Além disso, não testei ainda o comportamento do asp.net quando multiplos parametros
                        //que clasheiam sujas props
                        //O maior motivo, é que no caso de uma model que possui alguns itens na raiz, ficando ora model.coisa e $coisa2
                        //por ex, o asp.net se perde em seu modelbinding, considerando apenas no model.<algo>.
                        //this.SearchRelayFormat = $"...({this.Name} ? {{ {this.FromName}: {{ {string.Join(", ", props)} }} }} : {{}})";
                        this.SearchRelayFormat = $"...({this.Name} ? {{ {string.Join(", ", props)} }} : {{}})";
                    }
                    if (outProps.Any())
                    {
                        if (props.Any())
                        {
                            //Add comma
                            this.SearchRelayFormat += ", ";
                        }
                        this.SearchRelayFormat += $"...({this.Name} ? {{{string.Join(", ", outProps)}}} : {{}})";
                    }
                }
            }


            string typeName = res.Declaration;

            if (TsEnum.IsEnum(type))
            {
                if (res.IsEnum)
                {
                    var enumNames = string
                                    .Join(
                        " | ",
                        type.GetMembers()
                        .Where(m => m.Kind == SymbolKind.Field)
                        .Select(m => $"'{m.Name}'"));
                    if (!string.IsNullOrEmpty(enumNames))
                    {
                        typeName = $"{typeName} | {enumNames}";
                    }
                }
            }

            this.Type       = res;
            this.IsOptional = p.IsOptional;
            this.Signature  = $"{p.Name}{(p.IsOptional ? "?" : "")}: {typeName}" + (res.IsNullable ? " | null" : "");
            this.Ignore     = p.GetAttributes().Any(a => a.AttributeClass.Name == "FromServices");
        }