Create reusable feature metadata for all generators
Description
As discussed in #503, this would be a starting point for creating a compatibility matrix. We need some way for generators to define all feature sets supported by the generator.
We need to take into consideration that a base (abstract) type used across multiple generators may implement more features than a subtype intends to support. This means we need to be able to mutate the feature metadata from derived types.
Related issues/PRs
Suggest a fix/enhancement
I have a prototype in which I created a bitmask type in Kotlin, and apply feature sets as properties.
The feature sets I created are:
Click to expand Bitmask/Feature snippet
class BitMask(val value: Long)
interface Flags {
val bit: Long
fun toBitMask(): BitMask = BitMask(bit)
}
// Flags extensions
infix fun Flags.and(other: Long): BitMask = BitMask(bit and other)
infix fun <T: Flags> Flags.or(other: T): BitMask = BitMask(bit or other.bit)
infix operator fun Flags.plus(other: Flags): BitMask = BitMask(bit or other.bit)
inline fun <reified T> enabledValues(mask: BitMask) : List<T> where T : Enum<T>, T : Flags {
return enumValues<T>().filter {
mask hasFlag it
}
}
infix fun BitMask.or(other: Flags): BitMask = BitMask(value or other.bit)
infix operator fun BitMask.plus(other: BitMask): BitMask = BitMask(value or other.value)
infix operator fun BitMask.plus(other: Flags): BitMask = BitMask(value or other.bit)
infix fun <T: Flags> BitMask.hasFlag(which: T): Boolean {
// an Undefined flag is a special case.
if(value == 0L || (value > 0L && which.bit == 0L)) return false
return value and which.bit == which.bit
}
infix fun <T: Flags> BitMask.unset(which: T): BitMask = BitMask(value xor which.bit)
// Features
enum class ClientModificationFeature(override val bit: Long) : Flags {
Undefined(0),
BasePath(1 shl 0),
Authorizations(1 shl 1),
UserAgent(1 shl 2);
}
enum class DataTypeFeature(override val bit: Long) : Flags {
Undefined(0),
Int32(1 shl 0),
Int64(1 shl 1),
Float(1 shl 2),
Double(1 shl 3),
Decimal(1 shl 4),
String(1 shl 5),
Byte(1 shl 6),
Binary(1 shl 7),
Boolean(1 shl 8),
Date(1 shl 9),
DateTime(1 shl 10),
Password(1 shl 11),
File(1 shl 12),
Array(1 shl 13),
Maps(1 shl 14),
CollectionFormat(1 shl 15),
CollectionFormatMulti(1 shl 16),
Enum(1 shl 17),
ArrayOfEnum(1 shl 18),
ArrayOfModel(1 shl 19),
ArrayOfCollectionOfPrimitives(1 shl 20),
ArrayOfCollectionOfModel(1 shl 21),
ArrayOfCollectionOfEnum(1 shl 22),
MapOfEnum(1 shl 23),
MapOfModel(1 shl 24),
MapOfCollectionOfPrimitives(1 shl 25),
MapOfCollectionOfModel(1 shl 26),
MapOfCollectionOfEnum(1 shl 27);
}
enum class DocumentationFeature(override val bit: Long) : Flags {
Undefined(0),
Readme(1 shl 0),
Model(1 shl 1),
Api(1 shl 2);
}
enum class ModelReuseFeature(override val bit: Long) : Flags {
Undefined(0),
Composition(1 shl 0),
Inheritance(1 shl 1);
}
enum class ParameterFeature(override val bit: Long) : Flags {
Undefined(0),
Path(1 shl 0),
Query(1 shl 1),
Header(1 shl 2),
Body(1 shl 3),
FormUnencoded(1 shl 4),
FormMultipart(1 shl 5);
}
enum class SecurityFeature(override val bit: Long) : Flags {
Undefined(0),
BasicAuth(1 shl 0),
ApiKey(1 shl 1),
OAuth2(1 shl 2);
}
Types representing generator options then include these as properties:
Click to expand interface example
/**
* A generator specific to a language and framework
*/
interface FrameworkGenerator {
// …
/**
* DataTypes support available in this generator
*/
@FeatureFlag(DataTypeFeature::class)
val supportedDataTypes: BitMask
/**
* Model reuse support available in this generator
*/
@FeatureFlag(ModelReuseFeature::class)
val supportedModelReuse: BitMask
/**
* Parameter support available in this generator
*/
@FeatureFlag(ParameterFeature::class)
val supportedParameters: BitMask
/**
* Client configuration/modification support available in this generator
*/
@FeatureFlag(ClientModificationFeature::class)
val supportedClientModification: BitMask
/**
* Security support available in this generator
*/
@FeatureFlag(SecurityFeature::class)
val supportedSecurity: BitMask
/**
* Documentation support available in this generator
*/
@FeatureFlag(DocumentationFeature::class)
val supportedDocumentation: BitMask
// …
}
Following a similar approach here would allow us to extract information about all generators and their supported feature set using reflection.
We should also eventually be able to automate templating tests related to features as well.