Skip to content

Commit

Permalink
- Feat: add support for passing records as @factoryParam
Browse files Browse the repository at this point in the history
- chore: update dependencies
  • Loading branch information
Milad-Akarie committed Mar 12, 2024
1 parent 9f96df0 commit 54a97a7
Show file tree
Hide file tree
Showing 10 changed files with 338 additions and 152 deletions.
2 changes: 2 additions & 0 deletions injectable_generator/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# ChangeLog
## [2.5.0]
- Feat: add support for passing records as @factoryParam
## [2.4.2]
- Fix Postpone singleton initialisation to respect environment filters by @lrampazzo
## [2.4.1]
Expand Down
46 changes: 26 additions & 20 deletions injectable_generator/lib/code_builder/builder_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ class DependencySet with IterableMixin<DependencyConfig> {

final did = dep.id;
hasAsyncDepsMap[did] = hasAsyncDeps;
isAsyncOrHasAsyncDepsMap[did] =
(dep.isAsync && !dep.preResolve) || hasAsyncDeps;
isAsyncOrHasAsyncDepsMap[did] = (dep.isAsync && !dep.preResolve) || hasAsyncDeps;
}

_hasAsyncDeps = hasAsyncDepsMap;
Expand Down Expand Up @@ -102,23 +101,19 @@ Set<DependencyConfig> sortDependencies(Iterable<DependencyConfig> it) {
return s;
}

void _sortByDependents(
Set<DependencyConfig> unSorted, Set<DependencyConfig> sorted) {
void _sortByDependents(Set<DependencyConfig> unSorted, Set<DependencyConfig> sorted) {
for (var dep in unSorted) {
if (dep.dependencies.every(
(iDep) {
if (iDep.isFactoryParam) {
return true;
}
// if dep is already in sorted return true
if (lookupDependencyWithNoEnvOrHasAny(iDep, sorted, dep.environments) !=
null) {
if (lookupDependencyWithNoEnvOrHasAny(iDep, sorted, dep.environments) != null) {
return true;
}
// if dep is in unSorted we skip it in this iteration, if not we include it
return lookupDependencyWithNoEnvOrHasAny(
iDep, unSorted, dep.environments) ==
null;
return lookupDependencyWithNoEnvOrHasAny(iDep, unSorted, dep.environments) == null;
},
)) {
sorted.add(dep);
Expand All @@ -129,8 +124,7 @@ void _sortByDependents(
}
}

DependencyConfig? lookupDependency(
InjectedDependency iDep, Set<DependencyConfig> allDeps) {
DependencyConfig? lookupDependency(InjectedDependency iDep, Set<DependencyConfig> allDeps) {
return allDeps.firstWhereOrNull(
(d) => d.type == iDep.type && d.instanceName == iDep.instanceName,
);
Expand All @@ -153,11 +147,8 @@ DependencyConfig? lookupDependencyWithNoEnvOrHasAny(
);
}

Set<DependencyConfig> lookupPossibleDeps(
InjectedDependency iDep, Iterable<DependencyConfig> allDeps) {
return allDeps
.where((d) => d.type == iDep.type && d.instanceName == iDep.instanceName)
.toSet();
Set<DependencyConfig> lookupPossibleDeps(InjectedDependency iDep, Iterable<DependencyConfig> allDeps) {
return allDeps.where((d) => d.type == iDep.type && d.instanceName == iDep.instanceName).toSet();
}

bool hasPreResolvedDependencies(Iterable<DependencyConfig> deps) {
Expand All @@ -174,15 +165,30 @@ TypeReference nullableRefer(
..url = url
..isNullable = nullable);

Reference typeRefer(ImportableType type,
[Uri? targetFile, bool withNullabilitySuffix = true]) {
final relativeImport = targetFile == null
Reference typeRefer(ImportableType type, [Uri? targetFile, bool withNullabilitySuffix = true]) {
final import = targetFile == null
? ImportableTypeResolver.resolveAssetImport(type.import)
: ImportableTypeResolver.relative(type.import, targetFile);

if (type.isRecordType) {
return RecordType(
(b) => b
..url = import
..isNullable = type.isNullable
..positionalFieldTypes.addAll(
type.typeArguments.where((e) => !e.isNamedRecordField).map(typeRefer),
)
..namedFieldTypes.addAll({
for (final entry in [...type.typeArguments.where((e) => e.isNamedRecordField)])
entry.nameInRecord!: typeRefer(entry)
}),
);
}

return TypeReference((reference) {
reference
..symbol = type.name
..url = relativeImport
..url = import
..isNullable = withNullabilitySuffix && type.isNullable;
if (type.typeArguments.isNotEmpty) {
reference.types.addAll(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ class InjectableGenerator implements Generator {
).resolve(clazz));
}
}

return allDepsInStep.isNotEmpty ? jsonEncode(allDepsInStep) : null;
}

Expand Down
21 changes: 9 additions & 12 deletions injectable_generator/lib/models/dependency_config.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// holds extracted data from annotation & element
// to be used later when generating the register function

import 'dart:convert';

import 'package:collection/collection.dart';
import 'package:injectable_generator/models/module_config.dart';

Expand Down Expand Up @@ -53,9 +55,7 @@ class DependencyConfig {

// used for testing
factory DependencyConfig.factory(String type,
{List<String> deps = const [],
List<String> envs = const [],
int order = 0}) {
{List<String> deps = const [], List<String> envs = const [], int order = 0}) {
return DependencyConfig(
type: ImportableType(name: type),
typeImpl: ImportableType(name: type),
Expand All @@ -73,8 +73,7 @@ class DependencyConfig {
}

// used for testing
factory DependencyConfig.singleton(String type,
{List<String> deps = const [], int order = 0}) {
factory DependencyConfig.singleton(String type, {List<String> deps = const [], int order = 0}) {
return DependencyConfig(
type: ImportableType(name: type),
typeImpl: ImportableType(name: type),
Expand All @@ -93,7 +92,8 @@ class DependencyConfig {

@override
String toString() {
return 'DependencyConfig{type: $type, typeImpl: $typeImpl, injectableType: $injectableType, dependencies: $dependencies, instanceName: $instanceName, signalsReady: $signalsReady, environments: $environments, constructorName: $constructorName, postConstruct: $postConstruct, isAsync: $isAsync, postConstructReturnsSelf: $postConstructReturnsSelf, dependsOn: $dependsOn, preResolve: $preResolve, canBeConst: $canBeConst, moduleConfig: $moduleConfig, disposeFunction: $disposeFunction, orderPosition: $orderPosition, scope: $scope}';
final prettyJson = JsonEncoder.withIndent(' ').convert(toJson());
return 'DependencyConfig $prettyJson';
}

@override
Expand Down Expand Up @@ -199,8 +199,7 @@ class DependencyConfig {
"canBeConst": canBeConst,
"injectableType": injectableType,
if (moduleConfig != null) 'moduleConfig': moduleConfig!.toJson(),
if (disposeFunction != null)
'disposeFunction': disposeFunction!.toJson(),
if (disposeFunction != null) 'disposeFunction': disposeFunction!.toJson(),
"dependsOn": dependsOn.map((v) => v.toJson()).toList(),
"environments": environments,
"dependencies": dependencies.map((v) => v.toJson()).toList(),
Expand All @@ -214,9 +213,7 @@ class DependencyConfig {

bool get isFromModule => moduleConfig != null;

List<InjectedDependency> get positionalDependencies =>
dependencies.where((d) => d.isPositional).toList();
List<InjectedDependency> get positionalDependencies => dependencies.where((d) => d.isPositional).toList();

List<InjectedDependency> get namedDependencies =>
dependencies.where((d) => !d.isPositional).toList();
List<InjectedDependency> get namedDependencies => dependencies.where((d) => !d.isPositional).toList();
}
55 changes: 45 additions & 10 deletions injectable_generator/lib/models/importable_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,43 @@ class ImportableType {

String get identity => "$import#$name";

final bool _isRecordType;

bool get isRecordType => _isRecordType;

/// the name of the field in the record
final String? nameInRecord;

/// whether the type is for a named record field
bool get isNamedRecordField => nameInRecord != null;

const ImportableType({
required this.name,
this.import,
this.isNullable = false,
this.typeArguments = const [],
this.otherImports,
});
this.nameInRecord,
}) : _isRecordType = false;

const ImportableType._({
required this.name,
this.import,
this.typeArguments = const [],
this.isNullable = false,
required bool isRecordType,
this.otherImports,
this.nameInRecord,
}) : _isRecordType = isRecordType;

const ImportableType.record({
required this.name,
this.import,
this.isNullable = false,
this.typeArguments = const [],
this.nameInRecord,
this.otherImports,
}) : _isRecordType = true;

Set<String?> get allImports => {import, ...?otherImports};

Expand All @@ -36,14 +66,18 @@ class ImportableType {
allImports.intersection(other.allImports).isNotEmpty &&
name == other.name &&
isNullable == other.isNullable &&
nameInRecord == other.nameInRecord &&
_isRecordType == other._isRecordType &&
ListEquality().equals(typeArguments, other.typeArguments));

@override
int get hashCode =>
SetEquality().hash(allImports) ^
name.hashCode ^
isNullable.hashCode ^
ListEquality().hash(typeArguments);
ListEquality().hash(typeArguments) ^
_isRecordType.hashCode ^
nameInRecord.hashCode;

factory ImportableType.fromJson(Map<String, dynamic> json) {
List<ImportableType> typeArguments = [];
Expand All @@ -52,26 +86,27 @@ class ImportableType {
typeArguments.add(ImportableType.fromJson(v));
});
}
return ImportableType(
return ImportableType._(
import: json['import'],
name: json['name'],
isNullable: json['isNullable'],
otherImports:
(json['otherImports'] as List<dynamic>?)?.toSet().cast<String>(),
otherImports: (json['otherImports'] as List<dynamic>?)?.toSet().cast<String>(),
typeArguments: typeArguments,
isRecordType: json['isRecordType'],
nameInRecord: json['nameInRecord'],
);
}

Map<String, dynamic> toJson() {
// ignore: unnecessary_cast
// ignore: unnecessary_cast
return {
'import': import,
'name': name,
'isNullable': isNullable,
if (typeArguments.isNotEmpty)
"typeArguments": typeArguments.map((v) => v.toJson()).toList(),
if (otherImports?.isNotEmpty == true)
"otherImports": otherImports?.toList(),
'isRecordType': _isRecordType,
'nameInRecord': nameInRecord,
if (typeArguments.isNotEmpty) "typeArguments": typeArguments.map((v) => v.toJson()).toList(),
if (otherImports?.isNotEmpty == true) "otherImports": otherImports?.toList(),
} as Map<String, dynamic>;
}
}
Loading

0 comments on commit 54a97a7

Please sign in to comment.