Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Annotations @FunctionalCore and @NeedsRefactoring #644

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -14,20 +14,26 @@ fun addMissingEntriesInVersionsProperties(project: Project) {

val versionsMap = RefreshVersionsConfigHolder.readVersionsMap()
val versionKeyReader = RefreshVersionsConfigHolder.versionKeyReader

val newEntries: Map<String, ExternalDependency> = findMissingEntries(
configurations = configurationsWithHardcodedDependencies,
versionsMap = versionsMap,
versionKeyReader = versionKeyReader
) + UsedPluginsTracker.usedPluginsWithoutEntryInVersionsFile
.associateBy { d -> pluginDependencyNotationToVersionKey(d.name) }
.filterKeys { key -> key != null && key !in versionsMap }
.mapKeys { (k, _) -> k!! }
) + externalDependencyMap(UsedPluginsTracker.usedPluginsWithoutEntryInVersionsFile, versionsMap)


VersionsPropertiesModel.writeWithNewEntries(newEntries)
OutputFile.VERSIONS_PROPERTIES.logFileWasModified()
}

@InternalRefreshVersionsApi
@FunctionalCore(testName = "TODO")
private fun externalDependencyMap(usedPluginsWithoutEntryInVersionsFile: List<ExternalDependency>, versionsMap: Map<String, String>) =
usedPluginsWithoutEntryInVersionsFile
.associateBy { d -> pluginDependencyNotationToVersionKey(d.name) }
.filterKeys { key -> key != null && key !in versionsMap }
.mapKeys { (k, _) -> k!! }


@InternalRefreshVersionsApi
fun Configuration.countDependenciesWithHardcodedVersions(
@@ -77,6 +83,7 @@ fun Configuration.shouldBeIgnored(): Boolean {
// implementation, api, compileOnly, runtimeOnly, kapt, plus test, MPP and MPP test variants.
}

@FunctionalCore(testName = "TODO")
internal fun findMissingEntries(
configurations: List<Configuration>,
versionsMap: Map<String, String>,
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package de.fayard.refreshVersions.core.internal

import de.fayard.refreshVersions.core.ModuleId
import java.io.File

@InternalRefreshVersionsApi
abstract class ArtifactVersionKeyReader private constructor(
@@ -11,6 +12,7 @@ abstract class ArtifactVersionKeyReader private constructor(

companion object {

@FunctionalCore(testName = "TODO")
fun fromRules(
filesContent: List<String>,
getRemovedDependenciesVersionsKeys: () -> Map<ModuleId.Maven, String> = { emptyMap() }
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ import de.fayard.refreshVersions.core.ModuleId
import org.gradle.api.artifacts.Dependency
import org.gradle.api.internal.artifacts.dependencies.AbstractDependency

@NeedsRefactoring("Simpler name and use it everywhere")
internal class ConfigurationLessDependency(
private val group: String,
private val name: String,
@@ -20,6 +21,7 @@ internal class ConfigurationLessDependency(
)

companion object {
@FunctionalCore(testName = "TODO")
operator fun invoke(dependencyNotation: String): ConfigurationLessDependency {
val beforeFirstColon = dependencyNotation.substringBefore(':')
val afterFirstColon = dependencyNotation.substringAfter(':')
Original file line number Diff line number Diff line change
@@ -30,6 +30,8 @@ val MEANING_LESS_NAMES: MutableList<String> = mutableListOf(
)

@InternalRefreshVersionsApi
@NeedsRefactoring("Use Dependency instead of Library")
@FunctionalCore(testName = "TODO")
fun List<Library>.computeAliases(
configured: List<String>,
byDefault: List<String> = MEANING_LESS_NAMES
@@ -71,6 +73,7 @@ fun Project.findDependencies(): List<Library> {


@InternalRefreshVersionsApi
@NeedsRefactoring("Use Dependency instead")
data class Library(
val group: String = "",
val module: String = "",
@@ -102,6 +105,7 @@ data class Library(
}

@InternalRefreshVersionsApi
@NeedsRefactoring("Use Dependency instead")
class Deps(
val libraries: List<Library>,
val names: Map<Library, String>
@@ -114,6 +118,8 @@ enum class VersionMode {
}

@InternalRefreshVersionsApi
@NeedsRefactoring("Use Dependency instead of Library and Deps")
@FunctionalCore(testName = "TODO")
fun List<Library>.checkModeAndNames(useFdqnByDefault: List<String>, case: Case): Deps {
val dependencies = this

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package de.fayard.refreshVersions.core.internal

/**
* FunctionalCore/ImperativeShell is a simple but powerful testing strategy
* where you express your business logic as pure functions
* and you use the tested functions in a simple imperative shell that actually do things.
*
* In practical terms, a function annotated by @FunctionalCore must be tested
*
* See https://www.destroyallsoftware.com/screencasts/catalog/functional-core-imperative-shell
*/
@InternalRefreshVersionsApi
annotation class FunctionalCore(
val testName: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package de.fayard.refreshVersions.core.internal

/**
* For refactorings we don't want to do right now but want to keep track of
*/
@InternalRefreshVersionsApi
annotation class NeedsRefactoring(
val issueAndMessage: String,
)
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ import de.fayard.refreshVersions.core.internal.VersionsCatalogs.LIBS_VERSIONS_TO
import java.io.File

@InternalRefreshVersionsApi
@NeedsRefactoring("Design a better API")
enum class OutputFile(
val path: String,
var existed: Boolean = false,
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@ package de.fayard.refreshVersions.core.internal
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty

@FunctionalCore(testName = "TODO")
internal class ResettableDelegates {

fun reset() {
@@ -26,7 +27,7 @@ internal class ResettableDelegates {

override fun getValue(thisRef: Any?, property: KProperty<*>): T = field ?: error(
"Property ${property.name} not initialized yet! " +
"Has it been used after reset or before init?"
"Has it been used after reset or before init?"
)

operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package de.fayard.refreshVersions.core.internal

@FunctionalCore(testName = "VersionsCatalogUpdaterTest")
internal data class Toml(
val sections: MutableMap<TomlSection, List<TomlLine>>
) {
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@ import org.gradle.api.artifacts.Dependency
* Despite TOML supporting braces in its syntax, they must open and close on
* the same line, so having a per-line model is fine.
*/
@FunctionalCore(testName = "TomlLineTest")
internal data class TomlLine(
val section: TomlSection,
val text: String,
Original file line number Diff line number Diff line change
@@ -43,6 +43,7 @@ internal sealed class VersionManagementKind {
object NoMatch : VersionManagementKind()
}

@FunctionalCore(testName = "TODO")
internal fun Dependency.versionManagementKind(
versionMap: Map<String, String>,
versionKeyReader: ArtifactVersionKeyReader,
@@ -55,6 +56,7 @@ internal fun Dependency.versionManagementKind(
this is ExternalDependency && versionPlaceholder in this.versionConstraint.rejectedVersions -> {
Match.VersionsFile.VersionPlaceholder
}

name.endsWith(".gradle.plugin") -> {
when (val moduleId = moduleId()) {
is ModuleId.Maven -> {
@@ -68,23 +70,29 @@ internal fun Dependency.versionManagementKind(
versionsCatalogLibraries = versionsCatalogLibraries,
versionsCatalogPlugins = versionsCatalogPlugins
) -> Match.VersionsCatalog.MatchingVersionConstraint

else -> NoMatch
}

version -> Match.VersionsFile.MatchingPluginVersion
else -> NoMatch
}
}

else -> NoMatch
}
}

else -> when {
hasVersionInVersionCatalog(
versionsCatalogLibraries = versionsCatalogLibraries
) -> Match.VersionsCatalog.MatchingVersionConstraint

else -> NoMatch
}
}

@FunctionalCore(testName = "TODO")
private fun Dependency.hasVersionInVersionCatalog(
versionsCatalogLibraries: Collection<MinimalExternalModuleDependency>,
versionsCatalogPlugins: Set<PluginDependencyCompat> = emptySet()
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@ object VersionsCatalogs {

fun isSupported(): Boolean = GradleVersion.current() >= minimumGradleVersion

@FunctionalCore(testName = "TODO")
fun defaultCatalogName(): String = try {
@Suppress("UnstableApiUsage")
RefreshVersionsConfigHolder.settings.dependencyResolutionManagement.defaultLibrariesExtensionName.get()
@@ -27,11 +28,13 @@ object VersionsCatalogs {
"libs"
}

@FunctionalCore(testName = "TODO")
fun getDefault(project: Project): VersionCatalog? {
val versionCatalogs = project.extensions.findByType<VersionCatalogsExtension>()
return versionCatalogs?.find(defaultCatalogName())?.orElse(null)
}

@FunctionalCore(testName = "TODO")
internal fun libraries(versionCatalog: VersionCatalog?): Set<MinimalExternalModuleDependency> {
if (versionCatalog == null) return emptySet()
val aliases = versionCatalog.libraryAliases
@@ -40,6 +43,7 @@ object VersionsCatalogs {
}
}

@FunctionalCore(testName = "TODO")
internal fun plugins(versionCatalog: VersionCatalog?): Set<PluginDependencyCompat> {
if (versionCatalog == null) return emptySet()
val aliases = versionCatalog.pluginAliases
@@ -48,6 +52,7 @@ object VersionsCatalogs {
}
}

@FunctionalCore(testName = "TODO")
fun dependencyAliases(versionCatalog: VersionCatalog?): Map<ModuleId.Maven, String> = when {
FeatureFlag.VERSIONS_CATALOG.isNotEnabled -> emptyMap()
versionCatalog == null -> emptyMap()
@@ -64,6 +69,7 @@ object VersionsCatalogs {
}.toMap()
}

@FunctionalCore("VersionsCatalogUpdaterTest")
internal fun parseToml(toml: String): Toml {
val map = parseTomlInSections(toml)
.map { (sectionName, paragraph) ->
@@ -76,6 +82,7 @@ object VersionsCatalogs {
/**
* Returns a map where the key is the section name, and the value, the section content.
*/
@FunctionalCore("TomlSectionTest")
internal fun parseTomlInSections(toml: String): Map<String, String> {
val result = mutableMapOf<String, StringBuilder>()
result["root"] = StringBuilder()
@@ -96,6 +103,7 @@ object VersionsCatalogs {
return result.mapValues { it.value.toString() }
}

@FunctionalCore("VersionsCatalogUpdaterTest")
fun generateVersionsCatalogText(
versionsMap: Map<String, String> = RefreshVersionsConfigHolder.readVersionsMap(),
versionKeyReader: ArtifactVersionKeyReader = RefreshVersionsConfigHolder.versionKeyReader,
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package de.fayard.refreshVersions.core.internal.codeparsing

import de.fayard.refreshVersions.core.internal.FunctionalCore
import de.fayard.refreshVersions.core.internal.NeedsRefactoring
import de.fayard.refreshVersions.core.internal.codeparsing.SourceCodeSection.*

@NeedsRefactoring("parameterName and expressionWithCommentsIfAny properties are never used")
internal class FunctionArgument(
val parameterName: String?,
val expressionWithCommentsIfAny: String
@@ -13,6 +16,7 @@ internal class FunctionArgument(
* or if the passed [functionCallText] lacks the parentheses,
* it might fail in unexpected ways.
*/
@FunctionalCore(testName = "TODO")
internal fun extractArgumentsOfFunctionCall(
programmingLanguage: ProgrammingLanguage,
functionCallText: CharSequence
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package de.fayard.refreshVersions.core.internal.codeparsing

import de.fayard.refreshVersions.core.internal.FunctionalCore
import de.fayard.refreshVersions.core.internal.NeedsRefactoring
import de.fayard.refreshVersions.core.internal.TaggedRange

@FunctionalCore(testName = "TODO")
internal fun CharSequence.rangesOfCode(
code: String,
startIndex: Int = 0,
@@ -33,6 +36,7 @@ internal fun CharSequence.rangesOfCode(
return list
}

@FunctionalCore(testName = "TODO")
internal fun CharSequence.rangeOfCode(
code: String,
startIndex: Int = 0,
@@ -46,6 +50,8 @@ internal fun CharSequence.rangeOfCode(
sectionsRanges = sectionsRanges
)

@FunctionalCore(testName = "TODO")
@NeedsRefactoring("Dead code")
internal fun CharSequence.rangeOfStringLiteral(
stringLiteral: String,
startIndex: Int = 0,
@@ -59,6 +65,7 @@ internal fun CharSequence.rangeOfStringLiteral(
sectionsRanges = sectionsRanges
)

@FunctionalCore(testName = "TODO")
internal fun CharSequence.rangeOf(
text: String,
sectionKind: SourceCodeSection,
@@ -83,6 +90,7 @@ internal fun CharSequence.rangeOf(
/**
* Returns -1 if there's no import statement
*/
@FunctionalCore(testName = "TODO")
internal fun CharSequence.findFirstImportStatement(
sectionsRanges: List<TaggedRange<SourceCodeSection>>
): Int {
@@ -103,6 +111,7 @@ internal fun CharSequence.findFirstImportStatement(
/**
* Returns lastIndex + 1 (i.e. [CharSequence.length]) if no non-blank code chunk is found.
*/
@FunctionalCore(testName = "TODO")
internal fun CharSequence.indexOfFirstNonBlankCodeChunk(
sectionsRanges: List<TaggedRange<SourceCodeSection>>
): Int {
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package de.fayard.refreshVersions.core.internal.codeparsing

import de.fayard.refreshVersions.core.extensions.text.forEachIndexedSkippable
import de.fayard.refreshVersions.core.internal.FunctionalCore
import de.fayard.refreshVersions.core.internal.TaggedRange

/**
@@ -9,6 +10,7 @@ string literals, and the rest, separately.

The returned list covers the entire passed CharSequence.
*/
@FunctionalCore(testName = "TODO")
internal fun CharSequence.findRanges(
programmingLanguage: ProgrammingLanguage
): List<TaggedRange<SourceCodeSection>> = mutableListOf<TaggedRange<SourceCodeSection>>().also {
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package de.fayard.refreshVersions.core.internal.codeparsing.gradle

import de.fayard.refreshVersions.core.internal.FunctionalCore
import de.fayard.refreshVersions.core.internal.TaggedRange
import de.fayard.refreshVersions.core.internal.codeparsing.ProgrammingLanguage
import de.fayard.refreshVersions.core.internal.codeparsing.SourceCodeSection
import de.fayard.refreshVersions.core.internal.codeparsing.findRanges

@FunctionalCore(testName = "SettingsPluginUpdaterTest")
internal fun CharSequence.extractGradleScriptSections(
isKotlinDsl: Boolean
): List<TaggedRange<SourceCodeSection>> = findRanges(
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package de.fayard.refreshVersions.core.internal.codeparsing.gradle

import de.fayard.refreshVersions.core.internal.FunctionalCore
import de.fayard.refreshVersions.core.internal.TaggedRange
import de.fayard.refreshVersions.core.internal.codeparsing.SourceCodeSection
import de.fayard.refreshVersions.core.internal.codeparsing.SymbolLocationFindingRule
import de.fayard.refreshVersions.core.internal.codeparsing.findSymbolsRanges

@FunctionalCore(testName = "TODO")
internal fun CharSequence.findPluginBlocksRanges(
ranges: List<TaggedRange<SourceCodeSection>>
): List<TaggedRange<*>> = findSymbolsRanges(
Loading