Skip to content

ParseErrors

DoctorKrolic edited this page Oct 23, 2024 · 3 revisions

If any errors occured during argument parsing an erroneous ParseResult<T> is returned. It doesn't contain Options object, but contains non-null Errors collection. The library is designed to be resilient to any kind of input (with the exception of null or in any other way invalid input collection itself), so any combination of input strings must produce a ParseResult<T> and not fail with an exception. All error-related functionality lives in ArgumentParsing.Results.Errors namespace.

ParseErrorCollection

The Errors property of a ParseResult<T> is of type ParseErrorCollection. This type represents a read-only collection of ParseErrors and is meant to be constructed only by the generated argument parser method. Given the nature of source generators, the way to construct this type has to be accessible to user's code, but that entry point is hidden from the IntelliSense and is not considered a part of public API. The library reserves the rights to change how the errors collection is constructed for any release without notice in the changelog.

ParseError

The base abstract class for all possible errors is ParseError. It has GetMessage() method, which constructs a final error message with all the details about an error. This message is meant to be shown to the end user. ExecuteDefaults implementation for the error scenario shows a help screen with errors section, in which it lists all error messages obtained through this GetMessage() method. Other than that, ParseError doesn't provide any additional functionality besides value-like equality and good implementation of GetHashCode().

Each parse error has its own dedicated type. These error types are all well-known and used by the generator. Arbitrary extending this error set is not supported.

All error types are public (thus can be used with is check if desired). Each error exposes all the details about itself, e.g. UnknownOptionError exposes OptionName and ContainingArgument, in which it was encountered. These details are obviously different for different errors and are used to construct a final error message with GetMessage() method.

Customizing parse error messages

Final error messages are constructed via string.Format method, supplyed with a message format and arguments. Each error has a default message format string. All these default messages can be found in this file in the source. It is possible to change message formats for all errors though. In order to do this, an error message format provider type is specified as ErrorMessageProvider named argument in [GeneratedArgumentParser] attribute:

using ArgumentParsing;
using ArgumentParsing.Results;

partial class Program
{
    [GeneratedArgumentParser(ErrorMessageFormatProvider = typeof(CustomErrorMessageFormatProvider))]
    private static partial ParseResult<Options> ParseArguments(string[] args);
}

public class CustomErrorMessageFormatProvider
{
    // ...
}

[OptionsType]
class Options
{
}

In the example above CustomErrorMessageFormatProvider is responsible for messages of all errors, which can occur during parsing of options type.

Each error must have a corresponding entry in the message provider. 'Entry' here is a statically accessible member of string type. It can be a property or a constant, it only matters, that it can be accessed outside of the error message provider type in the same assembly, e.g. like CustomErrorMessageFormatProvider.UnrecognizedArgumentError. The intent here is that a string resource will be used, which will return different values for different cultures, since error message localization is the primary target of this feature.

Error message formats correspond to the name of the error type, e.g. for DuplicateOptionError the name of the message format member is also DuplicateOptionError and so on. The only exception from this rule is MissingRequiredOptionError. There are 3 possible cases for it:

  • Missing option has only a short name. The name of the message format member is MissingRequiredOptionError_OnlyShortOptionName then
  • Missing option has only a long name. The name of the message format member is MissingRequiredOptionError_OnlyLongOptionName then
  • Missing option has both short and names. The name of the message format member is MissingRequiredOptionError_BothOptionNames then

Note

The default message format for MissingRequiredOptionError is identical for case 1 and case 2 from above and correspond to a single MissingRequiredOptionError_OneOptionName member in the default error message formats list. That's an implementation detail and I'm leaving this note here just to avoid potential confusion. MissingRequiredOptionError_OneOptionName has no effect in a custom message format provider type. Of course, you are absolutly free to do the same and share the same resource string for MissingRequiredOptionError_OnlyShortOptionName and MissingRequiredOptionError_OnlyLongOptionName, but you will still need 2 different members pointing to a shared resource to achieve it

Given that a slightly different set of possible parse errors for various options types, the library doesn't provide any custom diagnostics for missing error message format strings. If you lack some message format members, you'll get a 'Missing member X' C# compiler error, pointing to the generated source when building your app, this is expected.

Clone this wiki locally