Husqvik / GraphQlClientGenerator

GraphQL C# client generator
MIT License
213 stars 49 forks source link

Custom scalar types in source generator mode #73

Closed imdbere closed 3 years ago

imdbere commented 3 years ago

Hi,

I have seen that the library supports custom scalar types by explicitly setting configuration.CustomScalarFieldTypeMapping. Is there any way to also do this in the source generator mode ?

Thanks again! 😄

Husqvik commented 3 years ago

I see few options how to achieve this (though all require extension of the GraphQlClientGenerator):

  1. having a compiled assembly where the custom type mapping configuration method signature would be set as configuration value of the generator
  2. having a separate source file with custom type mapping method that the Source generator could compile ad hoc into an in memory assembly and assign the method reference to the configuration object.
  3. I could define an attribute that would decorate an interface used to provide to mapping method instead of referring source file itself for two. Still it would require ad hoc compilation during the source generator execution.

Sounds as funny weekend project :)

Husqvik commented 3 years ago

Implemented the option 2. There must be an assembly available to load at compilation time (when the source generator runs). You implement an interface providing the custom mapping like:

public class MyCustomBooleanTypeMappingProvider : IScalarFieldTypeMappingProvider
{
    public ScalarFieldTypeDescription GetCustomScalarFieldType(GraphQlGeneratorConfiguration configuration, GraphQlType baseType, GraphQlTypeBase valueType, string valueName)
    {
        ...
    }
}

And set the configuration parameter

<GraphQlClientGenerator_ScalarFieldTypeMappingProvider>Fully.Qualified.ClassName, AssemblyName</GraphQlClientGenerator_ScalarFieldTypeMappingProvider>

Option 3 is problematic as it's difficult to isolate needed references of the mapping class unless there would be hardcoded set or some dynamic way of retrieving them which is really an inception :). Running nested compilation within compilation to generate dynamic assembly. The references for the main compilation cannot be used because MSBuild compilation runs still under .NET Framework 4.8 runtime but the references for compilation can and nowadays mostly are .NET core.

I haven't created a new nuget yet as it requires some testing, I found a bit problematic dynamic assembly load during the compilation time.

Husqvik commented 3 years ago

Got another idea: to write RegexScalarFieldTypeMappingProvider where the input will be list of regular expression rules for matching the types in for of additional JSON file. Probably a better solution than requiring assembly with compiled provider. Not as powerful but pretty sure enough for 99% of cases or more.

Husqvik commented 3 years ago

Pushed, now it's possible to include addional file

<AdditionalFiles Include="RegexScalarFieldTypeMappingProviderConfiguration.json" CacheObjects="true" />

containing rules like

[
  {
    "patternBaseType": ".+",
    "patternValueType": ".+",
    "patternValueName": "^((timestamp)|(.*(f|F)rom)|(.*(t|T)o))$",
    "netTypeName": "DateTimeOffset?",
    "formatMask": "O"
  }
]

where the patterns are standard C# regular expression patterns, netTypeName the .NET type to generate and optional formatMask for string conversion.

imdbere commented 3 years ago

This regex method seems to work perfectly, thanks for your awesome work 👍