// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Net; using System.Runtime.CompilerServices; using System.Runtime.Serialization; using System.Runtime.Serialization.Json; using System.Text; using System.Threading; using System.ComponentModel; using System.Reflection; using EpicGames.Core; using System.Linq; using Microsoft.Extensions.Logging; namespace AutomationTool { /// /// Help Attribute. /// [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] public class HelpAttribute : DescriptionAttribute { private bool IsParamHelp; /// /// Basic constructor for class descriptions. /// /// Class description public HelpAttribute(string Description) : base(Description) { IsParamHelp = false; } /// /// Constructor for parameter descriptions. /// /// Paramter name /// Paramter description public HelpAttribute(string ParamName, string Description) : base(FormatDescription(ParamName, Description)) { IsParamHelp = true; } /// /// Additional type to display help for. /// /// public HelpAttribute(Type AdditionalType) { IsParamHelp = false; AdditionalHelp = AdditionalType; } private static string FormatDescription(string Name, string Description) { return String.Format("-{0} {1}", Name, Description); } public bool IsParam { get { return IsParamHelp; } } public Type AdditionalHelp { get; private set; } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true)] public class ParamHelpAttribute : HelpAttribute { public enum ParamAction { Store, Store_True, Store_False, Append, Override } private ParamHelpAttribute(string Description) : base(Description) { } public ParamHelpAttribute() : this("", "") { // Needed for de-serialization } public ParamHelpAttribute(string Name, string Description) : base(Name, Description) { ParamName = Name.TrimStart('-'); int EqualsInParamNameIndex = ParamName.IndexOf("="); if (EqualsInParamNameIndex > 0) { ParamName = ParamName.Substring(0, EqualsInParamNameIndex); } _Action = ParamAction.Store; Required = false; ParamType = typeof(string); DefaultValue = null; _Choices = null; MultiSelectSeparator = null; Deprecated = false; } public string ParamName { get; set; } private string _ParamDescription = string.Empty; public string ParamDescription { get { if (!string.IsNullOrEmpty(_ParamDescription)) { return _ParamDescription; } string RequiredStr = Required ? ". Required" : ". Optional"; string ChoicesStr = ""; var ValidChoices = Choices as IEnumerable; if (ValidChoices != null) { ChoicesStr += ". Choices=["; foreach (var Value in ValidChoices) { if (Value != null) { ChoicesStr += Value.ToString() + (MultiSelectSeparator != null ? MultiSelectSeparator : "|"); } } ChoicesStr = ChoicesStr.Remove(ChoicesStr.Length - 1); ChoicesStr += "]"; } string DefaultStr = DefaultValue != null ? ". DefaultValue=" + DefaultValue.ToString() : ""; return base.Description + string.Format("{0}{1}{2}", RequiredStr, DefaultStr, ChoicesStr ); } set { _ParamDescription = value; } } private ParamAction _Action = ParamAction.Store; public ParamAction Action { get { return _Action; } set { if (value == ParamAction.Store_True || value == ParamAction.Store_False) { ParamType = typeof(bool); } _Action = value; } } public object DefaultValue { get; set; } public Type ParamType { get; set; } object _Choices; public object Choices { get { return _Choices; } set { var Enumerable = value as System.Collections.IEnumerable; if (Enumerable != null) { _Choices = Enumerable; } } } public bool Required { get; set; } public bool IsArgument { get; set; } public string MultiSelectSeparator { get; set; } public bool Deprecated { get; set; } private string _Flag = null; public string Flag { get { if (_Flag == null) { return "-" + ParamName; } return _Flag; } set { if (!string.IsNullOrEmpty(value)) { _Flag = value; } } } /// /// This string/character separates the flag from the value; ex. -foo=bar /// public string ParamKeyValueDelimiter { get; set; } = "="; } /// /// Base utility function for script commands. /// public partial class CommandUtils { /// /// Displays help for the specified command. /// Command type. /// public static void Help(Type Command) { string Description; List Params; GetTypeHelp(Command, out Description, out Params); LogHelp(Command, Description, Params); } /// /// Displays help for the specified Type. /// /// Type to display help for. public static void LogHelp(Type Command) { string Description; List Params; GetTypeHelp(Command, out Description, out Params); LogHelp(Command, Description, Params); } /// /// Displays a formatted help for the specified Command type. /// /// Command Type /// Command Desscription /// Command Parameters public static void LogHelp(Type Command, string Description, List Params) { Dictionary ParamDict = new Dictionary(StringComparer.InvariantCultureIgnoreCase); // Extract Params/Descriptions into Key/Value pairs foreach (var Param in Params) { // Find the first space (should be following the param name) if (!String.IsNullOrWhiteSpace(Param)) { var ParamName = String.Empty; var ParamDesc = String.Empty; var SplitPoint = Param.IndexOf(' '); if (SplitPoint > 0) { // Extract the name and description seperately ParamName = Param.Substring(0, SplitPoint); ParamDesc = Param.Substring(SplitPoint + 1, Param.Length - (SplitPoint + 1)); } else { ParamName = Param; } // build dictionary using Name and Desc as Key and Value if (!ParamDict.ContainsKey(ParamName)) { ParamDict.Add(ParamName, ParamDesc); } else { Logger.LogWarning("Duplicated help parameter \"{ParamName}\"", ParamName); } } } Logger.LogInformation(""); HelpUtils.PrintHelp(String.Format("{0} Help:", Command.Name), Description, ParamDict.ToList()); } /// /// Gets the description and a list of parameters for the specified type. /// /// Type to get help for. /// Description /// List of paramters public static void GetTypeHelp(Type ObjType, out string ObjectDescription, out List Params) { ObjectDescription = String.Empty; Params = new List(); var AllAttributes = ObjType.GetCustomAttributes(false); foreach (var CustomAttribute in AllAttributes) { var HelpAttribute = CustomAttribute as HelpAttribute; if (HelpAttribute != null) { if (HelpAttribute.IsParam) { Params.Add(HelpAttribute.Description); } else if (HelpAttribute.AdditionalHelp != null) { string DummyDescription; List AdditionalParams; GetTypeHelp(HelpAttribute.AdditionalHelp, out DummyDescription, out AdditionalParams); Params.AddRange(AdditionalParams); } else { if (!String.IsNullOrEmpty(ObjectDescription)) { ObjectDescription += Environment.NewLine; } ObjectDescription += HelpAttribute.Description; } } } var AllMembers = GetAllMemebers(ObjType); foreach (var Member in AllMembers) { var AllMemeberAtributes = Member.GetCustomAttributes(false); foreach (var CustomAttribute in AllMemeberAtributes) { var HelpAttribute = CustomAttribute as HelpAttribute; if (HelpAttribute != null && HelpAttribute.IsParam) { Params.Add(HelpAttribute.Description); } } } } private static List GetAllMemebers(Type ObjType) { var Members = new List(); for (; ObjType != null; ObjType = ObjType.BaseType) { var ObjMembers = ObjType.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); Members.AddRange(ObjMembers); } return Members; } } }