Line data Source code
1 : part of rx_bloc_generator; 2 : 3 : /// Validates the main bloc file and provides the generator the needed data 4 : class _BuildController { 5 1 : _BuildController({ 6 : required this.rxBlocClass, 7 : required this.eventClass, 8 : required this.stateClass, 9 : }); 10 : 11 : final ClassElement rxBlocClass; 12 : final ClassElement? eventClass; 13 : final ClassElement? stateClass; 14 : 15 1 : String generate() { 16 : // Check for any broken rules 17 1 : _validate(); 18 : 19 3 : final blocTypeClassName = '${rxBlocClass.displayName}Type'; 20 3 : final blocClassName = '\$${rxBlocClass.displayName}'; 21 2 : final eventClassName = eventClass!.displayName; 22 2 : final stateClassName = stateClass!.displayName; 23 4 : final blocFilePath = rxBlocClass.location?.components.first ?? ''; 24 : final mainBlocFileName = 25 3 : Uri.tryParse(blocFilePath, (blocFilePath.lastIndexOf('/') + 1)) 26 1 : ?.toString() ?? 27 : ''; 28 : 29 : /// The output buffer containing all the generated code 30 1 : final _output = StringBuffer(); 31 : 32 1 : <String>[ 33 : /// .. part of '[rx_bloc_name]_bloc.dart' 34 : // TODO(Diev): Use [Directive.partOf] instead once `part of` is supported 35 1 : "part of '$mainBlocFileName';", 36 : 37 : // abstract class [RxBlocName]BlocType 38 1 : _BlocTypeClass( 39 : blocTypeClassName, 40 : eventClassName, 41 : stateClassName, 42 2 : ).build().toDartCodeString(), 43 : 44 : // abstract class $[RxBlocName]Bloc 45 1 : _BlocClass( 46 : blocClassName, 47 : blocTypeClassName, 48 : eventClassName, 49 : stateClassName, 50 2 : eventClass!.methods, 51 2 : stateClass!.fields 52 : // Skip @RxBlocIgnoreState() ignored states 53 2 : .where((FieldElement field) => 54 2 : field.getter is PropertyAccessorElement && 55 1 : field.getter != null && 56 3 : (field.getter!.metadata.isEmpty || 57 : !const TypeChecker.fromRuntime(RxBlocIgnoreState) 58 2 : .hasAnnotationOf(field.getter!))) 59 1 : .toList(), 60 2 : ).build().toDartCodeString(), 61 : 62 : // class _[EventMethodName]EventArgs 63 : // ..._eventMethodsArgsClasses(), 64 2 : ...eventClass!.methods 65 3 : .where((MethodElement method) => method.isUsingArgumentClass) 66 2 : .map((MethodElement method) { 67 3 : return _EventArgumentsClass(method).build().toDartCodeString(); 68 1 : }).toList() 69 2 : ].forEach(_output.writeln); 70 : 71 1 : return _output.toString(); 72 : } 73 : 74 : /// Checks and logs if there is anything missed 75 1 : void _validate() { 76 1 : _validateEvents(); 77 1 : _validateStates(); 78 : } 79 : 80 1 : void _validateEvents() { 81 : // Events class required 82 1 : if (eventClass == null) { 83 1 : throw _RxBlocGeneratorException( 84 1 : _generateMissingClassError( 85 1 : eventClass?.displayName ?? '', 86 2 : rxBlocClass.name, 87 : ), 88 : ); 89 : } 90 : 91 : // Methods only - No fields should exist 92 4 : eventClass!.fields.forEach((field) { 93 2 : throw _RxBlocGeneratorException( 94 2 : '${eventClass!.name} should contain methods only,' 95 1 : ' while ${field.name} seems to be a field.'); 96 : }); 97 : } 98 : 99 1 : void _validateStates() { 100 : // States class required 101 1 : if (stateClass == null) { 102 1 : throw _RxBlocGeneratorException( 103 1 : _generateMissingClassError( 104 2 : eventClass?.displayName ?? '', 105 2 : rxBlocClass.name, 106 : ), 107 : ); 108 : } 109 : 110 : // Fields only - No methods should exist 111 4 : stateClass!.methods.forEach((method) { 112 1 : throw _RxBlocGeneratorException( 113 2 : 'State ${method.name}should be defined using the get keyword.'); 114 : }); 115 : 116 4 : stateClass!.accessors.forEach((fieldElement) { 117 1 : if (!fieldElement.isAbstract) { 118 2 : final name = fieldElement.name.replaceAll('=', ''); 119 1 : throw _RxBlocGeneratorException( 120 1 : 'State $name should not contain a body definition.'); 121 : } 122 : }); 123 : } 124 : 125 : /// Generate string that represents error when a missing class is detected. 126 : /// 127 : /// The function takes a [className] which represents the missing class that 128 : /// is tied closely to the [blocName] bloc. The [className] represents either 129 : /// the user-defined or the default value for the events/states class that 130 : /// is vital for proper bloc generation. 131 1 : String _generateMissingClassError(String className, String blocName) => 132 1 : (StringBuffer() 133 2 : ..writeAll(<String>[ 134 1 : '$blocName$className class missing.', 135 : 'Please make sure you have properly named and specified', 136 : 'your class in the same file where the $blocName resides.' 137 1 : ], '\n\t')) 138 1 : .toString(); 139 : }