@@ -28,10 +28,12 @@ import com.squareup.anvil.compiler.internal.asClassName
2828import com.squareup.anvil.compiler.internal.buildFile
2929import com.squareup.anvil.compiler.internal.fqName
3030import com.squareup.anvil.compiler.internal.reference.*
31+ import com.squareup.anvil.compiler.internal.reference.ClassReference.Psi
3132import com.squareup.kotlinpoet.*
3233import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
3334import dagger.BindsOptionalOf
3435import dagger.Provides
36+ import dagger.multibindings.IntoSet
3537import java.io.File
3638import javax.inject.Inject
3739import org.jetbrains.kotlin.descriptors.ModuleDescriptor
@@ -169,6 +171,36 @@ class ContributesRemoteFeatureCodeGenerator : CodeGenerator {
169171 .build(),
170172 )
171173 }
174+ addFunction(
175+ FunSpec .builder(" provides${boundType.shortName} Inventory" )
176+ .addAnnotation(Provides ::class .asClassName())
177+ .addAnnotation(
178+ AnnotationSpec .builder(singleInstanceAnnotationFqName.asClassName(module))
179+ .addMember(" scope = %T::class" , scope.asClassName())
180+ .build(),
181+ )
182+ .addAnnotation(IntoSet ::class .asClassName())
183+ .addParameter(" feature" , boundType.asClassName())
184+ .addCode(
185+ CodeBlock .of(
186+ """
187+ return object : FeatureTogglesInventory {
188+ override suspend fun getAll(): List<Toggle> {
189+ return feature.javaClass.declaredMethods.mapNotNull { method ->
190+ if (method.genericReturnType.toString().contains(Toggle::class.java.canonicalName!!)) {
191+ method.invoke(feature) as Toggle
192+ } else {
193+ null
194+ }
195+ }
196+ }
197+ }
198+ """ .trimIndent(),
199+ ),
200+ )
201+ .returns(FeatureTogglesInventory ::class .asClassName())
202+ .build(),
203+ )
172204 }
173205 .build(),
174206 )
@@ -871,6 +903,31 @@ class ContributesRemoteFeatureCodeGenerator : CodeGenerator {
871903 vmClass : ClassReference .Psi ,
872904 module : ModuleDescriptor ,
873905 ): CustomStorePresence {
906+ fun requireFeatureAndStoreCrossReference (vmClass : Psi , storeClass : ClassReference ) {
907+ // check if the store is annotated with RemoteFeatureStoreNamed
908+ if (storeClass.annotations.none { it.fqName == RemoteFeatureStoreNamed ::class .fqName }) {
909+ throw AnvilCompilationException (
910+ " ${storeClass.fqName} shall be annotated with [RemoteFeatureStoreNamed]" ,
911+ element = vmClass.clazz.identifyingElement,
912+ )
913+ } else {
914+ // lastly, check that both the feature and store reference each other
915+ val storedDefineFeature = storeClass.annotations
916+ .first { it.fqName == RemoteFeatureStoreNamed ::class .fqName }
917+ .remoteFeatureStoreValueOrNull()
918+
919+ // check the boundType to ensure triggers work as expected
920+ val annotation = vmClass.annotations.first { it.fqName == ContributesRemoteFeature ::class .fqName }
921+ val featureClass = annotation.boundTypeOrNull() ? : vmClass
922+ if (storedDefineFeature?.fqName != featureClass.fqName) {
923+ throw AnvilCompilationException (
924+ " ${vmClass.fqName} and ${featureClass.fqName} don't reference each other" ,
925+ element = vmClass.clazz.identifyingElement,
926+ )
927+ }
928+ }
929+ }
930+
874931 var exceptionStore = false
875932 var settingsStore = false
876933 var toggleStore = false
@@ -912,33 +969,44 @@ class ContributesRemoteFeatureCodeGenerator : CodeGenerator {
912969 }
913970 with (annotation.settingsStoreOrNull()) {
914971 settingsStore = this != null
915- if (this != null && this .directSuperTypeReferences()
916- .none { it.asClassReferenceOrNull()?.fqName == FeatureSettings .Store ::class .fqName }
917- ) {
918- throw AnvilCompilationException (
919- " ${vmClass.fqName} [settingsStore] must extend [FeatureSettings.Store]" ,
920- element = vmClass.clazz.identifyingElement,
921- )
972+ if (this != null ) {
973+ // check that the Store is actually a [FeatureSettings.Store]
974+ if (this .directSuperTypeReferences()
975+ .none { it.asClassReferenceOrNull()?.fqName == FeatureSettings .Store ::class .fqName }
976+ ) {
977+ throw AnvilCompilationException (
978+ " ${vmClass.fqName} [settingsStore] must extend [FeatureSettings.Store]" ,
979+ element = vmClass.clazz.identifyingElement,
980+ )
981+ }
982+
983+ requireFeatureAndStoreCrossReference(vmClass, this )
922984 }
923985 }
924986 with (annotation.exceptionsStoreOrNull()) {
925987 exceptionStore = this != null
926- if (this != null && this .directSuperTypeReferences()
927- .none { it.asClassReferenceOrNull()?.fqName == FeatureExceptions .Store ::class .fqName }
928- ) {
929- throw AnvilCompilationException (
930- " ${vmClass.fqName} [exceptionsStore] must extend [FeatureExceptions.Store]" ,
931- element = vmClass.clazz.identifyingElement,
932- )
988+ if (this != null ) {
989+ if (this .directSuperTypeReferences()
990+ .none { it.asClassReferenceOrNull()?.fqName == FeatureExceptions .Store ::class .fqName }
991+ ) {
992+ throw AnvilCompilationException (
993+ " ${vmClass.fqName} [exceptionsStore] must extend [FeatureExceptions.Store]" ,
994+ element = vmClass.clazz.identifyingElement,
995+ )
996+ }
997+ requireFeatureAndStoreCrossReference(vmClass, this )
933998 }
934999 }
9351000 with (annotation.toggleStoreOrNull()) {
9361001 toggleStore = this != null
937- if (this != null && this .directSuperTypeReferences().none { it.asClassReferenceOrNull()?.fqName == Toggle .Store ::class .fqName }) {
938- throw AnvilCompilationException (
939- " ${vmClass.fqName} [toggleStore] must extend [Toggle.Store]" ,
940- element = vmClass.clazz.identifyingElement,
941- )
1002+ if (this != null ) {
1003+ if (this .directSuperTypeReferences().none { it.asClassReferenceOrNull()?.fqName == Toggle .Store ::class .fqName }) {
1004+ throw AnvilCompilationException (
1005+ " ${vmClass.fqName} [toggleStore] must extend [Toggle.Store]" ,
1006+ element = vmClass.clazz.identifyingElement,
1007+ )
1008+ }
1009+ requireFeatureAndStoreCrossReference(vmClass, this )
9421010 }
9431011 }
9441012
@@ -993,6 +1061,10 @@ class ContributesRemoteFeatureCodeGenerator : CodeGenerator {
9931061 )
9941062 }
9951063
1064+ private fun AnnotationReference.remoteFeatureStoreValueOrNull (): ClassReference ? {
1065+ return argumentAt(" value" , 0 )?.value()
1066+ }
1067+
9961068 private fun AnnotationReference.featureNameOrNull (): String? {
9971069 return argumentAt(" featureName" , 2 )?.value()
9981070 }
0 commit comments