// Copyright 2005-2015 Giacomo Stelluti Scala & Contributors. All rights reserved. See License.md in the project root for license information. using CommandLine.Infrastructure; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace CommandLine.Text { /// /// Exposes standard delegates to provide a mean to customize part of help screen generation. /// This type is consumed by . /// public abstract class SentenceBuilder { /// /// Create instance of , /// /// The instance. public static SentenceBuilder Create() { return Factory(); } /// /// Factory to allow custom SentenceBuilder injection /// public static Func Factory { get; set; } = () => new DefaultSentenceBuilder(); /// /// Gets a delegate that returns the word 'required'. /// public abstract Func RequiredWord { get; } /// /// Gets a delegate that returns the word 'group'. /// public abstract Func OptionGroupWord { get; } /// /// Gets a delegate that returns that errors block heading text. /// public abstract Func ErrorsHeadingText { get; } /// /// Gets a delegate that returns usage text block heading text. /// public abstract Func UsageHeadingText { get; } /// /// Get a delegate that returns the help text of help command. /// The delegates must accept a boolean that is equal true for options; otherwise false for verbs. /// public abstract Func HelpCommandText { get; } /// /// Get a delegate that returns the help text of vesion command. /// The delegates must accept a boolean that is equal true for options; otherwise false for verbs. /// public abstract Func VersionCommandText { get; } /// /// Gets a delegate that handles singular error formatting. /// The delegates must accept an and returns a string. /// public abstract Func FormatError { get; } /// /// Gets a delegate that handles mutually exclusive set errors formatting. /// The delegates must accept a sequence of and returns a string. /// public abstract Func, string> FormatMutuallyExclusiveSetErrors { get; } private class DefaultSentenceBuilder : SentenceBuilder { public override Func RequiredWord { get { return () => "Required."; } } public override Func ErrorsHeadingText { get { return () => "ERROR(S):"; } } public override Func UsageHeadingText { get { return () => "USAGE:"; } } public override Func OptionGroupWord { get { return () => "Group"; } } public override Func HelpCommandText { get { return isOption => isOption ? "Display this help screen." : "Display more information on a specific command."; } } public override Func VersionCommandText { get { return _ => "Display version information."; } } public override Func FormatError { get { return error => { switch (error.Tag) { case ErrorType.BadFormatTokenError: return "Token '".JoinTo(((BadFormatTokenError)error).Token, "' is not recognized."); case ErrorType.MissingValueOptionError: return "Option '".JoinTo(((MissingValueOptionError)error).NameInfo.NameText, "' has no value."); case ErrorType.UnknownOptionError: return "Option '".JoinTo(((UnknownOptionError)error).Token, "' is unknown."); case ErrorType.MissingRequiredOptionError: var errMisssing = ((MissingRequiredOptionError)error); return errMisssing.NameInfo.Equals(NameInfo.EmptyName) ? "A required value not bound to option name is missing." : "Required option '".JoinTo(errMisssing.NameInfo.NameText, "' is missing."); case ErrorType.BadFormatConversionError: var badFormat = ((BadFormatConversionError)error); return badFormat.NameInfo.Equals(NameInfo.EmptyName) ? "A value not bound to option name is defined with a bad format." : "Option '".JoinTo(badFormat.NameInfo.NameText, "' is defined with a bad format."); case ErrorType.SequenceOutOfRangeError: var seqOutRange = ((SequenceOutOfRangeError)error); return seqOutRange.NameInfo.Equals(NameInfo.EmptyName) ? "A sequence value not bound to option name is defined with few items than required." : "A sequence option '".JoinTo(seqOutRange.NameInfo.NameText, "' is defined with fewer or more items than required."); case ErrorType.BadVerbSelectedError: return "Verb '".JoinTo(((BadVerbSelectedError)error).Token, "' is not recognized."); case ErrorType.NoVerbSelectedError: return "No verb selected."; case ErrorType.RepeatedOptionError: return "Option '".JoinTo(((RepeatedOptionError)error).NameInfo.NameText, "' is defined multiple times."); case ErrorType.SetValueExceptionError: var setValueError = (SetValueExceptionError)error; return "Error setting value to option '".JoinTo(setValueError.NameInfo.NameText, "': ", setValueError.Exception.Message); case ErrorType.MissingGroupOptionError: var missingGroupOptionError = (MissingGroupOptionError)error; return "At least one option from group '".JoinTo( missingGroupOptionError.Group, "' (", string.Join(", ", missingGroupOptionError.Names.Select(n => n.NameText)), ") is required."); case ErrorType.GroupOptionAmbiguityError: var groupOptionAmbiguityError = (GroupOptionAmbiguityError)error; return "Both SetName and Group are not allowed in option: (".JoinTo(groupOptionAmbiguityError.Option.NameText, ")"); case ErrorType.MultipleDefaultVerbsError: return MultipleDefaultVerbsError.ErrorMessage; } throw new InvalidOperationException(); }; } } public override Func, string> FormatMutuallyExclusiveSetErrors { get { return errors => { var bySet = from e in errors group e by e.SetName into g select new { SetName = g.Key, Errors = g.ToList() }; var msgs = bySet.Select( set => { var names = string.Join( string.Empty, (from e in set.Errors select "'".JoinTo(e.NameInfo.NameText, "', ")).ToArray()); var namesCount = set.Errors.Count(); var incompat = string.Join( string.Empty, (from x in (from s in bySet where !s.SetName.Equals(set.SetName) from e in s.Errors select e) .Distinct() select "'".JoinTo(x.NameInfo.NameText, "', ")).ToArray()); return new StringBuilder("Option") .AppendWhen(namesCount > 1, "s") .Append(": ") .Append(names.Substring(0, names.Length - 2)) .Append(' ') .AppendIf(namesCount > 1, "are", "is") .Append(" not compatible with: ") .Append(incompat.Substring(0, incompat.Length - 2)) .Append('.') .ToString(); }).ToArray(); return string.Join(Environment.NewLine, msgs); }; } } } } }