LCOV - code coverage report
Current view: top level - src/utilities - extensions.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 90 92 97.8 %
Date: 2021-09-23 09:58:53 Functions: 0 0 -

          Line data    Source code
       1             : part of rx_bloc_generator;
       2             : 
       3             : /// Supported types of streams
       4             : class _BlocEventStreamTypes {
       5             :   /// Constants feel more comfortable than strings
       6             :   static const String publish = 'PublishSubject';
       7             :   static const String behavior = 'BehaviorSubject';
       8             : }
       9             : 
      10             : /// String utilities
      11             : extension _StringExtensions on String {
      12             :   /// Capitalizes the first letter of the word
      13           5 :   String capitalize() => '${this[0].toUpperCase()}${substring(1)}';
      14             : 
      15             :   /// Converts string to red string when printed in terminal
      16           2 :   String toRedString() => '\x1B[31m${this}\x1B[0m';
      17             : }
      18             : 
      19             : /// It is the main [DartFormatter]
      20             : extension _SpecExtensions on Spec {
      21           3 :   String toDartCodeString() => DartFormatter().format(
      22           1 :         accept(
      23           1 :           DartEmitter(allocator: Allocator.none, useNullSafetySyntax: true),
      24           1 :         ).toString(),
      25             :       );
      26             : }
      27             : 
      28             : extension _StateFieldElement on FieldElement {
      29           3 :   String get stateFieldName => '_${name}State';
      30             : 
      31           4 :   String get stateMethodName => '_mapTo${name.capitalize()}State';
      32             : }
      33             : 
      34             : extension _EventMethodElement on MethodElement {
      35             :   /// The event field name in the generated file
      36           3 :   String get eventFieldName => '_\$${name}Event';
      37             : 
      38             :   /// The name of the arguments class that will be generated if
      39             :   /// the event contains more than one parameter
      40           4 :   String get eventArgumentsClassName => '_${name.capitalize()}EventArgs';
      41             : 
      42             :   /// Is the the [RxBlocEvent.seed] annotation is provided
      43           2 :   bool get hasSeedAnnotation => RegExp(r'(?<=seed: ).*(?=\)|,)')
      44           3 :       .hasMatch(_rxBlocEventAnnotation?.toSource() ?? '');
      45             : 
      46             :   /// Provides the stream generic type
      47             :   ///
      48             :   /// Example:
      49             :   /// if `fetchNews(int param)` then -> PublishSubject<int>
      50             :   /// if `fetchNews(String param)` then -> PublishSubject<String>
      51             :   /// if `fetchNews(int p1, int p2)` then -> PublishSubject<_FetchNewsEventArgs>
      52           4 :   List<Reference> get streamTypeArguments => [refer(publishSubjectGenericType)];
      53             : 
      54             :   /// Provides the BehaviorSubject.seeded arguments as [List] of [Expression]
      55             :   /// Throws an [_RxBlocGeneratorException] if a seed is provided but
      56             :   /// the stream is not  [RxBlocEventType.behaviour]
      57           1 :   List<Expression> get seedPositionalArguments {
      58           2 :     if (hasSeedAnnotation && !isBehavior) {
      59           3 :       throw _RxBlocGeneratorException('Event `$name` with type `PublishSubject`'
      60             :           ' can not have a `seed` parameter.');
      61             :     }
      62             : 
      63           2 :     return [_seededArgument];
      64             :   }
      65             : 
      66             :   /// Provides the BehaviorSubject.seeded arguments as an [Expression]
      67           1 :   Expression get _seededArgument {
      68           1 :     var seedArgumentsMatch = RegExp(r'(?<=seed: ).*(?=\)|,)')
      69           3 :         .allMatches(_rxBlocEventAnnotation?.toSource() ?? '')
      70           3 :         .map<String>((m) => m.group(0) ?? '');
      71             : 
      72           1 :     if (seedArgumentsMatch.isEmpty) {
      73           0 :       throw _RxBlocGeneratorException(
      74           0 :           'Event `$name` seed value is missing or is null.');
      75             :     }
      76             : 
      77           1 :     var seedArguments = seedArgumentsMatch.toString();
      78           2 :     return refer(
      79             :       // ignore: lines_longer_than_80_chars
      80           2 :       '${isUsingArgumentClass && !seedArguments.contains('(const') ? 'const ' : ''}'
      81           3 :       '${seedArguments.substring(1, seedArguments.length - 1)}',
      82             :     );
      83             :   }
      84             : 
      85             :   /// Provides the stream type based on the [RxBlocEventType] annotation
      86           2 :   String get eventStreamType => isBehavior
      87           1 :       ? _BlocEventStreamTypes.behavior +
      88           3 :           (hasSeedAnnotation ? '<$publishSubjectGenericType>' : '')
      89             :       : _BlocEventStreamTypes.publish;
      90             : 
      91             :   /// Provides the first annotation as [ElementAnnotation] if exists
      92           1 :   ElementAnnotation? get _eventAnnotation =>
      93             :       // TODO(Diev): Check if
      94           5 :       metadata.isNotEmpty && metadata.first is ElementAnnotation
      95           2 :           ? metadata.first
      96             :           : null;
      97             : 
      98             :   /// Provides the [RxBlocEvent] annotation as [DartObject] if exists
      99           1 :   DartObject? get _computedRxBlocEventAnnotation =>
     100           2 :       _rxBlocEventAnnotation?.computeConstantValue();
     101             : 
     102             :   /// Provides the [RxBlocEvent] annotation as [ElementAnnotation] if exists
     103           2 :   ElementAnnotation? get _rxBlocEventAnnotation => _eventAnnotation
     104           1 :               ?.computeConstantValue()
     105           1 :               ?.type
     106           2 :               ?.getDisplayString(withNullability: true) ==
     107           1 :           (RxBlocEvent).toString()
     108           1 :       ? _eventAnnotation
     109             :       : null;
     110             : 
     111             :   /// Is the event stream type a BehaviorSubject
     112           1 :   bool get isBehavior =>
     113           3 :       _computedRxBlocEventAnnotation.toString().contains('behaviour');
     114             : 
     115             :   /// Use argument class when the event's parameters are more than 1
     116           4 :   bool get isUsingArgumentClass => parameters.length > 1;
     117             : 
     118             :   /// Provides the stream type based on the number of the parameters
     119             :   /// Example:
     120             :   /// if `fetchNews(int param)` then -> int
     121             :   /// if `fetchNews(String param)` then -> String
     122             :   /// if `fetchNews(int param1, int param2)` -> _FetchNewsEventArgs
     123           1 :   String get publishSubjectGenericType {
     124           1 :     if (isUsingArgumentClass) {
     125           1 :       return eventArgumentsClassName;
     126             :     }
     127           2 :     return parameters.isNotEmpty
     128             :         // The only parameter's type
     129           4 :         ? parameters.first.type.getDisplayString(withNullability: true)
     130             :         // Default type
     131             :         : 'void';
     132             :   }
     133             : 
     134             :   /// Builds the stream body
     135             :   /// Example 1:
     136             :   /// _${EventMethodName}EventName.add(param)
     137             :   ///
     138             :   /// Example 2:
     139             :   /// _${EventMethodName}EventName.add(_MethodEventArgs(param1, param2))
     140             :   ///
     141           1 :   Code buildBody() {
     142           3 :     var requiredParams = parameters.whereRequired().clone();
     143           3 :     var optionalParams = parameters.whereOptional().clone();
     144             : 
     145           2 :     if (requiredParams.isEmpty && optionalParams.isEmpty) {
     146             :       // Provide null if we don't have any parameters
     147           1 :       return _callStreamAddMethod(literalNull);
     148             :     }
     149             : 
     150             :     // Provide the first if it's just one required parameter
     151           3 :     if (optionalParams.isEmpty && requiredParams.length == 1) {
     152           4 :       return _callStreamAddMethod(refer(requiredParams.first.name));
     153             :     }
     154             : 
     155             :     // Provide the first if it's just one optional parameter
     156           3 :     if (requiredParams.isEmpty && optionalParams.length == 1) {
     157           4 :       return _callStreamAddMethod(refer(optionalParams.first.name));
     158             :     }
     159             : 
     160           1 :     return _callStreamAddMethod(
     161           5 :       refer('_${name.capitalize()}EventArgs').newInstance(
     162           1 :         _positionalArguments,
     163           1 :         _namedArguments,
     164             :       ),
     165             :     );
     166             :   }
     167             : 
     168             :   /// Example:
     169             :   /// _${methodName}Event.add()
     170           1 :   Code _callStreamAddMethod(Expression argument) =>
     171           6 :       refer('$eventFieldName.add').call([argument]).code;
     172             : 
     173             :   /// Provides the event's positional arguments as a [Map] of [Expression]
     174           2 :   List<Expression> get _positionalArguments => parameters
     175           3 :       .where((ParameterElement parameter) => parameter.isPositional)
     176           1 :       .map(
     177           3 :         (ParameterElement parameter) => refer(parameter.name),
     178             :       )
     179           1 :       .toList();
     180             : 
     181             :   /// Provides the event's name arguments as a [Map] of [Expression]
     182           1 :   Map<String, Expression> get _namedArguments {
     183           2 :     var params = parameters.clone();
     184             : 
     185             :     // Only named are not positional parameters
     186           1 :     var namedArguments = <String, Expression>{};
     187           4 :     for (var param in params.where((Parameter param) => param.named)) {
     188           4 :       namedArguments[param.name] = refer(param.name);
     189             :     }
     190             :     // For methods with more than 1 parameters provide the new param class
     191             :     return namedArguments;
     192             :   }
     193             : }
     194             : 
     195             : extension _ListParameterElementWhere on List<ParameterElement> {
     196           2 :   Iterable<ParameterElement> whereRequired() => where(
     197           3 :       (parameter) => !parameter.isNamed && !parameter.isOptionalPositional);
     198             : 
     199           1 :   Iterable<ParameterElement> whereOptional() =>
     200           4 :       where((parameter) => parameter.isOptionalPositional || parameter.isNamed);
     201             : }
     202             : 
     203             : extension _ListParameterElementClone on Iterable<ParameterElement> {
     204           2 :   List<Parameter> clone({bool toThis = false}) => map(
     205           2 :         (ParameterElement parameter) => Parameter(
     206           1 :           (b) => b
     207           1 :             ..toThis = toThis
     208           2 :             ..required = parameter.isRequiredNamed
     209           2 :             ..defaultTo = parameter.defaultValueCode != null
     210           2 :                 ? Code(parameter.defaultValueCode ?? '')
     211             :                 : null
     212           2 :             ..named = parameter.isNamed
     213           2 :             ..name = parameter.name
     214           1 :             ..type = toThis
     215             :                 ? null // We don't need the type in the constructor
     216           2 :                 : refer(parameter.getTypeDisplayName()),
     217             :         ),
     218           1 :       ).toList();
     219             : }
     220             : 
     221             : extension _ParameterElementToString on ParameterElement {
     222           3 :   String getTypeDisplayName() => type.getDisplayString(withNullability: true);
     223             : }

Generated by: LCOV version 1.15