generateForAnnotatedElement method

  1. @override
Future<String> generateForAnnotatedElement(
  1. covariant ClassElement element,
  2. ConstantReader cr,
  3. BuildStep buildStep
)

Implement to return source code to generate for element.

This method is invoked based on finding elements annotated with an instance of T. The annotation is provided as a ConstantReader.

Supported return values include a single String or multiple String instances within an Iterable or Stream. It is also valid to return a Future of String, Iterable, or Stream.

Implementations should return null when no content is generated. Empty or whitespace-only String instances are also ignored.

Implementation

@override
Future<String> generateForAnnotatedElement(
    covariant ClassElement element, ConstantReader cr, BuildStep buildStep) async {

  var superTypes = element.allSupertypes.where((st) =>
      !{'Object', 'SerializableMap', 'Map'}.contains(st.element.name));
  var stAccessors = superTypes.expand((st) => st.accessors);
  var stMethods = superTypes.expand((st) => st.methods);
  var stFields = superTypes.expand((st) => st.element.fields.where((f) => !f.isStatic));

  var methodCheck = (MethodElement e) => !e.isStatic && !e.isOperator;
  var accessorCheck = (ElementKind type) => (a) => a.kind == type && !a.isStatic && !a.isOperator && a.name != 'keys';

  var className = element.name;
  var fields = []
    ..addAll(_distinctByName<FieldElement>(element.fields.where((f) => !f.isStatic))
    ..addAll(stFields));

  var accessors = _distinctByName<PropertyAccessorElement>([...element.accessors, ...stAccessors]);
  var getters = _distinctByName<PropertyAccessorElement>(accessors.where(accessorCheck(ElementKind.GETTER)));
  var setters = _distinctByName<PropertyAccessorElement>(accessors.where(accessorCheck(ElementKind.SETTER)));
  var methods = _distinctByName<MethodElement>([...element.methods.where(methodCheck), ...stMethods.where(methodCheck)]);
  var typeGenerics = _distinctByName<TypeParameterElement>(element.typeParameters);

  return '''mixin _\$${className}Serializable${typeGenerics.isNotEmpty ? '<' + typeGenerics.map((x) => x.declaration.name).join(',') + '>' : ''} on SerializableMap {
${getters.map((g) => '${g.returnType.getDisplayString(withNullability: true)} get ${g.name};').join('\n')}
${setters.map((s) => 'set ${s.displayName}(${s.type.normalParameterTypes[0].getDisplayString(withNullability: true)} v);').join('\n')}
${methods.map((m) => '${m.returnType.getDisplayString(withNullability: true)} ${m.name}(${_renderParameters(m.parameters)});').join('\n')}

operator [](Object? __key) {
  switch(__key) {
    ${getters.map((a) => "case '${_getFieldName(a)}': return ${a.name};").join('\n')}
    ${methods.map((a) => "case '${a.name}': return ${a.name};").join('\n')}
  }
  throwFieldNotFoundException(__key, '$className');
}

operator []=(Object? __key, __value) {
  switch(__key) {
    ${setters.map((a) => "case '${_getFieldName(a)}': ${a.displayName} = ${_renderSetterValue(a)}; return;").join('\n')}
  }
  throwFieldNotFoundException(__key, '$className');
}

Iterable<String> get keys => ${useClassMirrors ? '${className}ClassMirror.fields?.keys ?? [];' : 'const [${fields.map((f) => "'${f.name}'").join(',')}];'}
}''';
}