diff --git a/CliFx/Services/CommandInitializer.cs b/CliFx/Services/CommandInitializer.cs index e35d749..9cff52a 100644 --- a/CliFx/Services/CommandInitializer.cs +++ b/CliFx/Services/CommandInitializer.cs @@ -84,7 +84,7 @@ namespace CliFx.Services if (unsetRequiredOptions.Any()) { var unsetRequiredOptionNames = unsetRequiredOptions.Select(o => o.GetAliases().FirstOrDefault()).JoinToString(", "); - throw new CliFxException($"One or more required options were not set: {unsetRequiredOptionNames}."); + throw new CliFxException($"Some of the required options were not provided: {unsetRequiredOptionNames}."); } } } diff --git a/CliFx/Services/CommandOptionInputConverter.cs b/CliFx/Services/CommandOptionInputConverter.cs index c4006fc..c72d714 100644 --- a/CliFx/Services/CommandOptionInputConverter.cs +++ b/CliFx/Services/CommandOptionInputConverter.cs @@ -133,11 +133,16 @@ namespace CliFx.Services catch (Exception ex) { // Wrap and rethrow exceptions that occur when trying to convert the value - throw new CliFxException($"Can't convert value [{value}] to type [{targetType}].", ex); + throw new CliFxException( + $"Can't convert value [{value}] to type [{targetType}]. " + + "Provided value probably doesn't match the expected format. " + + $"Underlying exception message: {ex.Message}", ex); } // Throw if we can't find a way to convert the value - throw new CliFxException($"Can't convert value [{value}] to type [{targetType}]."); + throw new CliFxException( + $"Can't find a way to convert user input to type [{targetType}]. " + + "This type is not among the list of types supported by this library."); } /// @@ -192,12 +197,12 @@ namespace CliFx.Services public partial class CommandOptionInputConverter { - private static ConstructorInfo GetStringConstructor(Type type) => type.GetConstructor(new[] {typeof(string)}); + private static ConstructorInfo? GetStringConstructor(Type type) => type.GetConstructor(new[] {typeof(string)}); - private static MethodInfo GetStaticParseMethod(Type type) => + private static MethodInfo? GetStaticParseMethod(Type type) => type.GetMethod("Parse", BindingFlags.Public | BindingFlags.Static, null, new[] {typeof(string)}, null); - private static MethodInfo GetStaticParseMethodWithFormatProvider(Type type) => + private static MethodInfo? GetStaticParseMethodWithFormatProvider(Type type) => type.GetMethod("Parse", BindingFlags.Public | BindingFlags.Static, null, new[] {typeof(string), typeof(IFormatProvider)}, null); } } \ No newline at end of file diff --git a/CliFx/Services/CommandSchemaResolver.cs b/CliFx/Services/CommandSchemaResolver.cs index 23ff8be..8798319 100644 --- a/CliFx/Services/CommandSchemaResolver.cs +++ b/CliFx/Services/CommandSchemaResolver.cs @@ -42,8 +42,9 @@ namespace CliFx.Services if (existingOptionWithSameName != null) { throw new CliFxException( - $"Command type [{commandType}] has options defined with the same name: " + - $"[{existingOptionWithSameName.Property}] and [{optionSchema.Property}]."); + $"Command type [{commandType}] has two options that have the same name ({optionSchema.Name}): " + + $"[{existingOptionWithSameName.Property}] and [{optionSchema.Property}]. " + + "All options in a command need to have unique names (case-insensitive)."); } // Make sure there are no other options with the same short name @@ -54,8 +55,9 @@ namespace CliFx.Services if (existingOptionWithSameShortName != null) { throw new CliFxException( - $"Command type [{commandType}] has options defined with the same short name: " + - $"[{existingOptionWithSameShortName.Property}] and [{optionSchema.Property}]."); + $"Command type [{commandType}] has two options that have the same short name ({optionSchema.ShortName}): " + + $"[{existingOptionWithSameShortName.Property}] and [{optionSchema.Property}]. " + + "All options in a command need to have unique short names (case-sensitive)."); } // Add schema to list @@ -71,7 +73,9 @@ namespace CliFx.Services // Make sure there's at least one command defined if (!commandTypes.Any()) { - throw new CliFxException("There are no commands defined."); + throw new CliFxException( + "There are no commands defined. " + + "An application needs to have at least one command to work."); } var result = new List(); @@ -81,7 +85,11 @@ namespace CliFx.Services // Make sure command type implements ICommand. if (!commandType.Implements(typeof(ICommand))) { - throw new CliFxException($"Command type [{commandType}] must implement {typeof(ICommand)}."); + throw new CliFxException( + $"Command type [{commandType}] needs to implement [{typeof(ICommand)}]." + + Environment.NewLine + Environment.NewLine + + $"public class {commandType.Name} : ICommand" + Environment.NewLine + + "// ^-- implement interface"); } // Get attribute @@ -90,7 +98,11 @@ namespace CliFx.Services // Make sure attribute is set if (attribute == null) { - throw new CliFxException($"Command type [{commandType}] must be annotated with [{typeof(CommandAttribute)}]."); + throw new CliFxException( + $"Command type [{commandType}] needs to be annotated with [{typeof(CommandAttribute)}]." + + Environment.NewLine + Environment.NewLine + + "[Command] // <-- add attribute" + Environment.NewLine + + $"public class {commandType.Name} : ICommand"); } // Get option schemas @@ -109,7 +121,8 @@ namespace CliFx.Services if (existingCommandWithSameName != null) { throw new CliFxException( - $"Command type [{existingCommandWithSameName.Type}] has the same name as another command type [{commandType}]."); + $"Command type [{existingCommandWithSameName.Type}] has the same name as another command type [{commandType}]. " + + "All commands need to have unique names (case-insensitive)."); } // Add schema to list