Skip to content

Argument definition rules

Giuseppe Cramarossa edited this page Jan 10, 2024 · 1 revision

From version 0.4.0, you can define definition rules. In this page you will find information about argument definition rules. In particular, it contains:

  • what is a definition rule
  • generic definition rule
  • definition rule specific to an attribute
  • an example that shows how to use a definition rule

Prior to read this page, it is suggested to read the page that explains what is a rule in general

What is a definition rule

Argument definition rules are rules that are executed when the argument is created and defined. For this reason, they are the first rules evaluated by the library. At this point, the value inserted by the user is not known. For each of the argument created, the library creates a ParameterDescriptor object that contains the informations about the argument (name, aliases, type, ...).

You can create two types of argument rules:

  • generic rules
  • specializeed rules

Generic rules

Generic rules are not associated to any attribute, and they are very useful if you want to define a generic rule for an object. You may need to define an argument that does not have any specific attribute, but you have to associate some specific tasks or add some functionalities. Generic rules helps to cover this case.

In order to define a generic rule, you have to use a class that implements the IArgumentDefinitionRule interface, as showed in the code below

public class GenericRule : IArgumentDefinitionRule
{
    public ParameterDescriptor DefineArgumentIfTypeDoesNotExist(object parentObject, PropertyInfo property, ParameterDescriptor descriptor)
    {
        return descriptor;
    }

    public ParameterDescriptor DefineArgumentIfTypeDoesNotExist(object parentObject, FieldInfo property, ParameterDescriptor descriptor)
    {
        return descriptor;
    }

    public ParameterDescriptor DefineArgumentIfTypeExists(object parentObject, FieldInfo field, ParameterDescriptor descriptor)
    {
        return descriptor;
    }

    public ParameterDescriptor DefineArgumentIfTypeExists(object parentObject, PropertyInfo property, ParameterDescriptor descriptor)
    {
        return descriptor;
    }

    public bool IsRuleEnabledInArgumentDefinition(PropertyInfo property)
    {
        return true;
    }

    public bool IsRuleEnabledInArgumentDefinition(FieldInfo field)
    {
        return true;
    }
}

In particular, you can see that there are three main methods that have two overloads (for a total of six methods)

  • IsRuleEnabledInArgumentDefinition is executed always and define if the rule should be executed or not
  • DefineArgumentIfTypeDoesNotExist is not executed in any case in generic rules. Probably they wll be removed in future versions
  • DefineArgumentIfTypeExists is executed if the rule is enabled

The main difference between the overloads is that one method accept a property (the method with PropertyInfo) and the other accept a field (the method with FieldInfo)

Specialized rules

Specialized rules are associated to an attribute, and they are very useful if you want to define a behaviour for a rule that has a specific attribute.

Let's assume you have created an attribute called MyCustomAttribute and you want to define a rule. In order to define a specific rule, you have to use a class that implements the IArgumentDefinitionRule<ParameterType> interface, as showed in the code below

public class GenericRule : IArgumentDefinitionRule<MyCustomAttributeAttribute>
{
    public ParameterDescriptor DefineArgumentIfTypeDoesNotExist(object parentObject, PropertyInfo property, ParameterDescriptor descriptor)
    {
        return descriptor;
    }

    public ParameterDescriptor DefineArgumentIfTypeDoesNotExist(object parentObject, FieldInfo property, ParameterDescriptor descriptor)
    {
        return descriptor;
    }

    public ParameterDescriptor DefineArgumentIfTypeExists(object parentObject, FieldInfo field, ParameterDescriptor descriptor)
    {
        return descriptor;
    }

    public ParameterDescriptor DefineArgumentIfTypeExists(object parentObject, PropertyInfo property, ParameterDescriptor descriptor)
    {
        return descriptor;
    }

    public bool IsRuleEnabledInArgumentDefinition(PropertyInfo property)
    {
        return true;
    }

    public bool IsRuleEnabledInArgumentDefinition(FieldInfo field)
    {
        return true;
    }
}

In particular, you can see that there are three main methods that have two overloads (for a total of six methods)

  • IsRuleEnabledInArgumentDefinition is executed always and define if the rule should be executed or not
  • DefineArgumentIfTypeExists is executed if the type or the attrbute exists and the rule is enabled
  • DefineArgumentIfTypeDoesNotExist is executed only if the attribute or the type is not present and if the rule is enabled

The main difference between the overloads is that one method accept a property (the method with PropertyInfo) and the other accept a field (the method with FieldInfo)

Example

In this example, we will create a simple hello world definition rule that will print a message when an argument of a custom type is defined as argument of a command

We will create

  • the library that contains the rule and an example command
  • the console application

For this example it is assumed that you have learned what is a command and how to implement it

Follow these steps to complete this example:

  1. Clone the example repository available here
  2. Build and run the BasicArgumentDefinitionRule project available in folder rule
  3. If you try to execute the activityexample command, you can see something like the output below:
[04/01/2024 18:49:11] SUCCESS (0) : 2 elements in the list
 Executing generic definition rule contained in the library

 Command start
Clone this wiki locally