Skip to content

Interfaces

BIGDummyHead edited this page May 2, 2022 · 8 revisions

This covers some of the most important basics of this library!

Let's go over the more simple first.

ICommandModule

All command modules will implement this interface, it is impossible to Register a command without this ValueTask OnCommandExecute(MethodInfo, object, object[], object?) and the property Handler UserHandler.

* Method Parameters Explained

  • MethodInfo method - The method being invoked
  • object instance - The instance of the type to which the method belongs to
  • object[] invokes - The object instances that were used to invoke the method
  • object? returnInstance - The returned type instance of the method if any

ILog

This interface is only used by the Handler to log messages like debug, info, warnings, and errors.

We must implement these 5 methods from this interface.

  • void Log(string, LogLevel)
  • void LogDebug(string)
  • void LogInfo(string)
  • void LogWarning(string)
  • void LogError(string)

These are pretty self explanatory with their arguments, so no need to include the params.

IConverter

This interface is used to convert objects that cannot be parsed by the IStringHandler via the primal conversion methods.

Instead it provides the flexibility for you to build upon your own IStringHandler and create types that can be converted by string.

* Example

class Person() { public string name; }

class PersonConverter : IConverter<Person>
{
     public ValueTask<Person> Convert(object[] previousArgs, string parse, object[] afterArgs)
     {

         //Understanding:
         //parse = The exact argument that needs to be converted. This is raw string passed in by a user! This can be one word longer
         //previousArgs/afterArgs = objects passed in by YOU, these can help you create command modules that require a certain object set before or after

         var person = new Person();
         person.name = parse; //we don't really care about the information that is passed and just want to set the name so it is not null
         
         return parse; 
     }
}

IStringConverter

The most complicated interface to implement as it pushes you to create your own StringConverter just like this one here

* Converters and Registration

Converters is one of the most important part for the IStringConverter because the user is doing work for you!

void RegisterConverter<T>(Func<object[], string, object[], ValueTask<T>);

This method provides the possibility to create a primitive converter without making a IConverter<T>. This can sometimes be the better solution when making a simple converter, like the example above.

We should take note that the Type we register with SHOULD be SAVED, so it can be used for UnRegisterConverter or UnRegisterConverter(Type)

Since the library uses ValueTask we can use async delegates!

stringConverter.RegisterConverter<Person>(async delegate(object[] prev, string parse, object[] after) 
{
     return new Person()
     {
        name = parse
     };
});

Simply takes in a IConverter instance. You can do whatever you like with this

void RegisterConverter<T>(IConverter<T>);

Sort through the types we've registered and remove it!

UnRegisterConverter(Type)
UnRegisterConverter<T>()

Now that we've registered our converter, we should let the user know if their converter has been registered 😕

bool CanConvert(Type)
bool CanConvert<T>()

Simply use those registered types that you should have saved and check for them in your cache, dictionary, list, or whatever you are using!

Now that the user can register, unregister, and see if the can convert we can now use the converter!

bool UseConverter(Type, object[], string, object[], out ValueTask<object>)
bool UseConverter<T>(object[], string, object[], out ValueTask<T>)

You should return if the converter was successful.

I also recommend using CanConvert(Type) in these methods

So, what should go inside the UseConverter method?

Simply put, check if the Converter type is registered, find the converter, and use the ValueTask<T> Convert(object[], string, object[] method or the delegate from other RegisterConverter method.

* Casting

Ok, everything so far should have been pretty simple to figure out. Adding converters to your cache, dictionary, or whatever it is definitely not the most extreme task 🧇

So, now let's work on creating the most important part of this entire interface which is CastString the method that should be able to handle a few primitive types.

The basic StringConverter can convert Empty ctor classes, Classes with a single string ctor, Digits, and Enums.

Important: GlobalCastString

The method above should be the user's last resort since instead we want to use the Converters we registered earlier.

bool CastString(object[], string, object[], Type, out ValueTask<object>, out string)
bool CastString<T>(object[], string, object[], out ValueTask<T>, out string)

So, your CastString method should look something like this:

* Example

public bool CastString(object[] before, string from, object[] after, Type castType, out ValueTask<object> converted, out string error)
{
   if(UseConverter(castType, before, from, after, out converted)) //use a converter if we can
   {
      error = string.Empty;
      return true;
   }

   //we should have some kind of static method to use here. This allows the user to cast a string to primitive types
   
   return GlobalCastString(from, castType, out converted, out error);
}

public bool CastString<T>(object[] before, string from, object[] after, out ValueTask<T> converted, out string error)
{
    bool ret = CastString(before, parse, after, typeof(T), out ValueTask<object> conversion, out error);

    //change the result from object to type T
    converted = ValueTask.FromResult((T)conversion.Result);
    return ret;    
}
Clone this wiki locally