Skip to content
Merged
Show file tree
Hide file tree
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
Expand Up @@ -3,11 +3,19 @@ package io.github.cdsap.projectgenerator
/**
* Global lookup for layer and module names. When not provided, defaults
* to the conventional names `layer_<number>` and the original module id.
*
* Prefer computing maps with [ProjectNameMappingFactory] and applying them
* via [configure] rather than mutating these properties ad hoc.
*/
object NameMappings {
var layerNames: Map<Int, String> = emptyMap()
var moduleNames: Map<String, String> = emptyMap()

fun configure(maps: ProjectNameMaps) {
layerNames = maps.layerNames
moduleNames = maps.moduleNames
}

fun layerName(layer: Int): String = layerNames[layer] ?: "layer_${layer}"
fun moduleName(id: String): String = moduleNames[id] ?: id

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,6 @@ class ProjectGenerator(
println("Creating project $projectName in $projectRootPath")
println("Calculating layer Distribution")

// Generate name mappings for layers and modules
NameMappings.layerNames = (0..layers).associateWith { index ->
if (index == layers) {
"app"
} else {
layerNames.getOrNull(index) ?: "layer_$index"
}
}

val distributions = LayerDistribution(modules, layers).get(shape)
println("Generating Project Dependency Graph")
val nodes = ProjectGraphGenerator(
Expand All @@ -46,15 +37,14 @@ class ProjectGenerator(
classesPerModule
).generate()

NameMappings.moduleNames = nodes
.sortedBy { it.id.substringAfterLast("_").toInt() }
.mapIndexed { index, node ->
if (node.layer == layers) {
node.id to "app"
} else {
node.id to generateModuleName(index)
}
}.toMap()
NameMappings.configure(
ProjectNameMappingFactory.create(
layers = layers,
nodes = nodes,
layerNames = layerNames,
moduleNameParts = moduleNameParts
)
)

val projectLanguageAttributes = getProjectLanguageAttributes()
ProjectWriter(
Expand Down Expand Up @@ -90,15 +80,4 @@ class ProjectGenerator(
)
}
}

private fun generateModuleName(index: Int): String {
var remaining = index
val base = moduleNameParts.size
val parts = mutableListOf<String>()
do {
parts.add(moduleNameParts[remaining % base])
remaining /= base
} while (remaining > 0)
return parts.joinToString("-")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package io.github.cdsap.projectgenerator

import io.github.cdsap.projectgenerator.model.ProjectGraph

/**
* Immutable layer and module name maps produced by [ProjectNameMappingFactory].
*/
data class ProjectNameMaps(
val layerNames: Map<Int, String>,
val moduleNames: Map<String, String>
)

/**
* Builds layer and module name maps from generation inputs without mutating
* the global [NameMappings] singleton.
*/
object ProjectNameMappingFactory {

fun create(
layers: Int,
nodes: List<ProjectGraph>,
layerNames: List<String>,
moduleNameParts: List<String>
): ProjectNameMaps {
val layerMap = (0..layers).associateWith { index ->
if (index == layers) {
"app"
} else {
layerNames.getOrNull(index) ?: "layer_$index"
}
}

val moduleMap = nodes
.sortedBy { it.id.substringAfterLast("_").toInt() }
.mapIndexed { index, node ->
if (node.layer == layers) {
node.id to "app"
} else {
node.id to generateModuleName(index, moduleNameParts)
}
}.toMap()

return ProjectNameMaps(layerMap, moduleMap)
}

private fun generateModuleName(index: Int, moduleNameParts: List<String>): String {
var remaining = index
val base = moduleNameParts.size
val parts = mutableListOf<String>()
do {
parts.add(moduleNameParts[remaining % base])
remaining /= base
} while (remaining > 0)
return parts.joinToString("-")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,27 @@ class ProjectGeneratorTest {
assert(File("$tempDir/project_kts/graph.dot").exists())
assert(File("$tempDir/project_groovy/graph.dot").exists())
}

@Test
fun `projectGenerator configures NameMappings before writing project files`() {
val layers = 2
ProjectGenerator(
modules = 6,
shape = Shape.RECTANGLE,
language = Language.KTS,
classesPerModule = ClassesPerModule(ClassesPerModuleType.FIXED, 10),
layers = layers,
layerNames = listOf("platform"),
moduleNameParts = listOf("alpha", "beta"),
projectRootPath = tempDir.toString(),
projectName = "named_project"
).write()

assert(NameMappings.layerName(0) == "platform")
assert(NameMappings.layerName(1) == "layer_1")
assert(NameMappings.layerName(layers) == "app")
assert(NameMappings.moduleNames.values.contains("app"))
assert(NameMappings.moduleNames.values.any { it == "alpha" || it == "beta" || it.contains("-") })
assert(File("$tempDir/settings.gradle.kts").readText().contains(":app:app"))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package io.github.cdsap.projectgenerator

import io.github.cdsap.projectgenerator.model.ProjectGraph
import io.github.cdsap.projectgenerator.model.TypeProject
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test

class ProjectNameMappingFactoryTest {

@Test
fun `maps app layer to app and honors custom and fallback layer names`() {
val maps = ProjectNameMappingFactory.create(
layers = 3,
nodes = emptyList(),
layerNames = listOf("core", "feature"),
moduleNameParts = listOf("push", "contact")
)

assertEquals("core", maps.layerNames[0])
assertEquals("feature", maps.layerNames[1])
assertEquals("layer_2", maps.layerNames[2])
assertEquals("app", maps.layerNames[3])
}

@Test
fun `generates module names from module name parts in sequence`() {
val nodes = listOf(
node("module_0_1", layer = 0),
node("module_0_2", layer = 0),
node("module_1_3", layer = 1),
node("module_2_4", layer = 2)
)
val parts = listOf("push", "contact", "login")

val maps = ProjectNameMappingFactory.create(
layers = 2,
nodes = nodes,
layerNames = listOf("core", "feature"),
moduleNameParts = parts
)

assertEquals("push", maps.moduleNames["module_0_1"])
assertEquals("contact", maps.moduleNames["module_0_2"])
assertEquals("login", maps.moduleNames["module_1_3"])
assertEquals("app", maps.moduleNames["module_2_4"])
}

@Test
fun `module names wrap with hyphenated parts beyond the alphabet size`() {
val nodes = (1..4).map { index ->
node("module_0_$index", layer = 0)
}
val parts = listOf("alpha", "beta")

val maps = ProjectNameMappingFactory.create(
layers = 1,
nodes = nodes,
layerNames = listOf("core"),
moduleNameParts = parts
)

assertEquals("alpha", maps.moduleNames["module_0_1"])
assertEquals("beta", maps.moduleNames["module_0_2"])
assertEquals("alpha-beta", maps.moduleNames["module_0_3"])
assertEquals("beta-beta", maps.moduleNames["module_0_4"])
}

@Test
fun `configure applies computed maps to NameMappings`() {
val previousLayers = NameMappings.layerNames
val previousModules = NameMappings.moduleNames
try {
val maps = ProjectNameMappingFactory.create(
layers = 1,
nodes = listOf(node("module_0_1", layer = 0), node("module_1_2", layer = 1)),
layerNames = listOf("domain"),
moduleNameParts = listOf("push")
)
NameMappings.configure(maps)

assertEquals("domain", NameMappings.layerName(0))
assertEquals("app", NameMappings.layerName(1))
assertEquals("push", NameMappings.moduleName("module_0_1"))
assertEquals("app", NameMappings.moduleName("module_1_2"))
} finally {
NameMappings.layerNames = previousLayers
NameMappings.moduleNames = previousModules
}
}

private fun node(id: String, layer: Int) = ProjectGraph(
id = id,
layer = layer,
nodes = emptyList(),
type = TypeProject.ANDROID_LIB,
classes = 1
)
}
Loading