/// <summary> /// Returns help content derived from the provided assembly and parsable object. /// </summary> /// <param name="parsable"></param> /// <param name="asm"></param> /// <param name="template"></param> /// <param name="argumentTemplate"></param> /// <param name="maxLineLength">The maximum number of characters in a line before it is wrapped</param> /// <returns></returns> public static string GetHelpInfoFromAssembly(Parsable parsable, Assembly asm, string template, string argumentTemplate, int maxLineLength = 80) { if (parsable == null) throw new ArgumentNullException("parsable"); if(asm == null) throw new ArgumentNullException("asm"); if (string.IsNullOrEmpty(template)) return ""; var parsableClass = Helper.GetObjectAttribute(parsable, typeof(ParsableClassAttribute)) as ParsableClassAttribute; if (parsableClass == null) throw new CliParseException("Unable to find 'ParsableClass' attribute on provided object."); var title = GetAssemblyAttribute(asm, typeof (AssemblyTitleAttribute)); template = template.Replace("{title}", title); var version = asm.GetName().Version.ToString(); template = template.Replace("{version}", version); var company = GetAssemblyAttribute(asm, typeof(AssemblyCompanyAttribute)); template = template.Replace("{company}", company); var description = GetAssemblyAttribute(asm, typeof (AssemblyDescriptionAttribute)); template = template.Replace("{description}", description); var syntax = GetSyntaxInfo(parsable, argumentTemplate, parsableClass.AllowedPrefixes); template = template.Replace("{syntax}", syntax); var copyright = GetAssemblyAttribute(asm, typeof (AssemblyCopyrightAttribute)); template = template.Replace("{copyright}", copyright); var footer = GetAssemblyMetadataAttribute(asm, "footer"); template = template.Replace("{footer}", footer); return FormatTextForScreen(template.Trim(), maxLineLength); }
/// <summary> /// Parses the provided args collection and uses its values to set the appropriate properties on the parsable object. /// </summary> /// <param name="parsable"></param> /// <param name="args"></param> /// <returns></returns> public static CliParseResult Parse(Parsable parsable, IEnumerable<string> args) { if (args == null) throw new CliParseException("Parameter 'args' cannot be null."); if (parsable == null) throw new CliParseException("Parameter 'parsable' cannot be null."); var result = new CliParseResult(); try { // single enumeration. var argumentList = args as IList<string> ?? args.ToList(); parsable.PreParse(argumentList, result); if (result.Successful == false || result.ShowHelp) return result; result = MapArguments(parsable, argumentList); parsable.PostParse(argumentList, result); } catch (CliParseException ex) { result.AddMessageFromException(ex); } return result; }
/// <summary> /// Returns help content derived from the provided parsable object. /// </summary> /// <param name="parsable"></param> /// <param name="template"></param> /// <param name="argumentTemplate"></param> /// <param name="maxLineLength">The maximum number of characters in a line before it is wrapped</param> /// <returns></returns> public static string GetHelpInfo(Parsable parsable, string template, string argumentTemplate, int maxLineLength = 80) { if (parsable == null) throw new ArgumentNullException("parsable"); if (string.IsNullOrEmpty(template)) return ""; var parsableClass = Helper.GetObjectAttribute(parsable, typeof(ParsableClassAttribute)) as ParsableClassAttribute; if(parsableClass == null) throw new CliParseException("Unable to find 'ParsableClass' attribute on provided object."); template = template.Replace("{title}", parsableClass.Title); template = template.Replace("{description}", parsableClass.Description); template = template.Replace("{copyright}", string.IsNullOrEmpty(parsableClass.Copyright) ? "" : parsableClass.Copyright); template = template.Replace("{version}", parsableClass.Version); var syntax = GetSyntaxInfo(parsable, argumentTemplate, parsableClass.AllowedPrefixes); template = template.Replace("{syntax}", syntax); template = template.Replace("{example}", parsableClass.ExampleText); template = template.Replace("{footer}", parsableClass.FooterText); return FormatTextForScreen(template.Trim(), maxLineLength); }
public static object GetObjectAttribute(Parsable parsable, Type type) { var parsableType = parsable.GetType(); return parsableType.GetCustomAttributes(true).FirstOrDefault(x => x.GetType() == type); }
private static CliParseResult MapArguments(Parsable parsable, IEnumerable<string> args) { var result = new CliParseResult(); var parsableClass = Helper.GetObjectAttribute(parsable, typeof(ParsableClassAttribute)) as ParsableClassAttribute; var allowedPrefixes = GetParsableClassAllowedPrefixs(parsableClass); var ignoreUnknowns = parsableClass != null && parsableClass.IgnoreUnknowns; var tokens = Tokenizer.Tokenize(args, allowedPrefixes).ToList(); result.ShowHelp = tokens.Any(token=>IsHelpToken(token, parsable)); if (tokens.Count == 0) { if (parsableClass == null || parsableClass.ShowHelpWhenNoArgumentsProvided) result.ShowHelp = true; } var parsableType = parsable.GetType(); List<PropertyInfo> unsetProperties = parsableType.GetProperties().ToList(); List<PropertyInfo> tmpSetProperties = new List<PropertyInfo>(); // find by names foreach (var prop in unsetProperties) { foreach (var argument in prop.GetCustomAttributes(true).OfType<ParsableArgumentAttribute>()) { // find by name var token = GetTokenForArgumentByName(tokens, argument); var propertySet = false; if(token != null) propertySet = SetPropertyValue(parsable, token, tokens, argument, prop); if (!propertySet) { // find by position token = GetTokenForArgumentByPosition(tokens, argument); propertySet = SetPropertyValue(parsable, token, tokens, argument, prop); } // flag property as set and remove later. if (propertySet) { tmpSetProperties.Add(prop); } } } tmpSetProperties.ForEach(x => unsetProperties.Remove(x)); foreach (var unsetProperty in unsetProperties) { foreach (var argument in unsetProperty.GetCustomAttributes(true).OfType<ParsableArgumentAttribute>()) { if(argument.Required) result.AddErrorMessage(string.Format(CultureInfo.CurrentCulture,"Required argument '{0}' was not supplied.", argument.Name)); } } // unknown/unused aruments if (!result.ShowHelp && !ignoreUnknowns) { tokens.Where(x => !x.Taken) .ToList() .ForEach( x => result.AddErrorMessage(string.Format(CultureInfo.CurrentCulture, "Unknown argument '{0}' was supplied.", x.Value.ToString()))); } return result; }
private static bool SetPropertyValue(Parsable parsable, Token token, IEnumerable<Token> tokens, ParsableArgumentAttribute parsableArgument, PropertyInfo prop) { if (token == null) { if (parsableArgument.DefaultValue != null) { prop.SetValue(parsable, parsableArgument.DefaultValue); return true; } return false; // couldnt find matching token, return false. } Token tokenValue = null; if(token.Type == TokenType.Field) { if (prop.PropertyType == typeof (bool)) { // check if we have been provided a "true" or "false" value. tokenValue = tokens.FirstOrDefault(x => !x.Taken && x.Index == token.Index + 1 && x.Type == TokenType.Value); var optionValue = true; if (tokenValue != null && !Boolean.TryParse(tokenValue.Value.ToString(), out optionValue)) { // tokenValue did not contain a valid bool so do not use its value, set it back to null so it will not be flagged as Taken. tokenValue = null; optionValue = true; } prop.SetValue(parsable, optionValue); // set property to true if flag was provided. } else { tokenValue = tokens.FirstOrDefault(x => !x.Taken && x.Index == token.Index + 1 && x.Type == TokenType.Value); if (tokenValue == null) throw new CliParseException(string.Format(CultureInfo.CurrentCulture, "Missing value for ParsableArgument {0}", token.Value)); PropertyDescriptor propertyDescriptor = TypeDescriptor.GetProperties(parsable)[prop.Name]; prop.SetValue(parsable, propertyDescriptor.Converter != null ? propertyDescriptor.Converter.ConvertFrom(tokenValue.Value) : tokenValue); } } if (token.Type == TokenType.Value) { PropertyDescriptor propertyDescriptor = TypeDescriptor.GetProperties(parsable)[prop.Name]; prop.SetValue(parsable, propertyDescriptor.Converter != null ? propertyDescriptor.Converter.ConvertFrom(token.Value) : token.Value); } token.Taken = true; if (tokenValue != null) tokenValue.Taken = true; return true; }
private static bool IsHelpToken(Token token, Parsable parsable) { var parsableClass = Helper.GetObjectAttribute(parsable, typeof(ParsableClassAttribute)) as ParsableClassAttribute ?? new ParsableClassAttribute(""); var defaultHelpArgs = parsableClass.ShowHelpParameters; return token.Type == TokenType.Field && defaultHelpArgs.Contains(token.Value.ToString()); }
private static string GetSyntaxInfo(Parsable parsable, string argumentTemplate, ICollection<char> prefixes) { var arguments = GetListArgumentAttributes(parsable); var sb = new StringBuilder(); var prefix = "-"; // default if (prefixes.Count > 1) { prefix = prefixes.FirstOrDefault().ToString(); var allowedPrefixes = ""; prefixes.ToList().ForEach(x => allowedPrefixes += "'" + x + "',"); allowedPrefixes = allowedPrefixes.Substring(0, allowedPrefixes.Length - 1); sb.AppendLine("The following argument prefix characters can be used: "+allowedPrefixes); } foreach (var argument in arguments) { sb.AppendLine(argument.GetSyntax(argumentTemplate, prefix)); } return sb.ToString(); }
private static IEnumerable<ParsableArgumentAttribute> GetListArgumentAttributes(Parsable parsable) { var parsableType = parsable.GetType(); var properties = parsableType.GetProperties(); var arguments = new List<ParsableArgumentAttribute>(); foreach (var prop in properties) { arguments.AddRange(prop.GetCustomAttributes(true).OfType<ParsableArgumentAttribute>()); } return arguments; }