using AWSPowerShellGenerator.ServiceConfig; using AWSPowerShellGenerator.Generators; using AWSPowerShellGenerator.Utils; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis; namespace AWSPowerShellGenerator.Writers.SourceCode { /// /// /// Text-based parser that scans the source file for an advanced (hand-maintained) /// cmdlet looking for 'interesting' items. Currently this just means looking for /// marker attributes on parameters that signal the use of ConstantClass-derived /// types for which argument completers should be generated. /// internal class AdvancedCmdletScanner { /// /// The parent service assembly for the cmdlet to be processed. Used to /// load remaining type definitions we may not have already seen when /// generating cmdlets. /// public Assembly ServiceAssembly { get; set; } public AdvancedCmdletScanner(Assembly serviceAssembly) { this.ServiceAssembly = serviceAssembly; } /// /// Perform the scan on the source file. If we locate attributes marking use /// of ConstantClass-derived types, update the service model so that argument /// completers are generated and/or the cmdlet is added to the completer /// registration. /// public void Scan(string sourceFile, Dictionary advancedCmdlets, ArgumentCompleterDetails argumentCompleters = null) { var originalSource = File.ReadAllText(sourceFile); try { SyntaxTree tree = CSharpSyntaxTree.ParseText(originalSource); CompilationUnitSyntax root = tree.GetCompilationUnitRoot(); foreach (var ns in root.Members.OfType()) { foreach (var cls in ns.Members.OfType()) { var attributes = cls.AttributeLists.SelectMany(al => al.Attributes).ToArray(); var cmdletAttribute = attributes.FirstOrDefault(attr => GetAttributeName(attr) == "Cmdlet"); if (cmdletAttribute != null) { var cmdletVerb = (cmdletAttribute.ArgumentList.Arguments[0].Expression as LiteralExpressionSyntax).Token.ValueText; var cmdletNoun = (cmdletAttribute.ArgumentList.Arguments[1].Expression as LiteralExpressionSyntax).Token.ValueText; var cmdletName = $"{cmdletVerb}-{cmdletNoun}"; var cmdletInfo = new AdvancedCmdletInfo(); advancedCmdlets.Add(cmdletName, cmdletInfo); var awsCmdletAttribute = attributes.FirstOrDefault(attr => GetAttributeName(attr) == "AWSCmdlet"); var operationNameAttribute = awsCmdletAttribute?.ArgumentList.Arguments.Where(arg => arg.NameEquals?.Name.Identifier.ValueText == "Operation").FirstOrDefault() as AttributeArgumentSyntax; var operationNames = (operationNameAttribute?.Expression as ImplicitArrayCreationExpressionSyntax)?.Initializer.Expressions.OfType().Select(expr => expr.Token.ValueText); if (operationNames != null) { cmdletInfo.OperationNames.AddRange(operationNames); } if (argumentCompleters != null) { foreach (var prop in cls.Members.OfType()) { var propertyAttributes = prop.AttributeLists.SelectMany(al => al.Attributes).ToArray(); if (propertyAttributes.Any(attr => GetAttributeName(attr) == "Parameter")) { var awsConstantClassSourceAttribute = propertyAttributes.FirstOrDefault(attr => GetAttributeName(attr) == "AWSConstantClassSource"); if (awsConstantClassSourceAttribute != null) { var constantType = (awsConstantClassSourceAttribute.ArgumentList.Arguments[0].Expression as LiteralExpressionSyntax).Token.ValueText; var propertyName = prop.Identifier.ValueText; if (!argumentCompleters.IsConstantClassRegistered(constantType)) { var propertyType = ServiceAssembly.GetType(constantType); var members = SimplePropertyInfo.GetConstantClassMembers(propertyType); argumentCompleters.AddConstantClass(constantType, members); } argumentCompleters.AddConstantClassReference(constantType, propertyName, cmdletName); } } } } } } } } catch (Exception e) { throw new InvalidDataException($"Error parsing advanced cmdlet file {sourceFile}", e); } } private static string GetAttributeName(AttributeSyntax attributeSyntax) { switch (attributeSyntax.Name) { case IdentifierNameSyntax idName: return idName.Identifier.Text; case QualifiedNameSyntax qName: return qName.ChildNodes().OfType().FirstOrDefault()?.Identifier.Text; default: return null; } } } }