aboutsummaryrefslogtreecommitdiff
path: root/buildSrc/src/main/kotlin/ca/allanwang/kau/ChangelogGenerator.kt
blob: 66f28f99544b13711e290aabecb72adf6295ea06 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
package ca.allanwang.kau

import groovy.util.Node
import groovy.util.XmlParser
import org.gradle.api.GradleException
import java.io.File

/**
 * Given an xml of the format
 *
 * <?xml version="1.0" encoding="utf-8"?>
 *  <resources>
 *  <version title="v0.1" />
 *  <item text="Initial Changelog" />
 *  <item text="Bullet point here" />
 *  <item text="More points" />
 *  <item text="" /> <!-- this one is empty and therefore ignored -->
 * </resources>
 *
 * Outputs a changelog in markdown format
 */
object ChangelogGenerator {

    class ChangelogException(message: String) : GradleException(message)

    private fun fail(message: String): Nothing =
        throw ChangelogException(message)

    class ChangelogEntry(val version: String, val items: Array<String>)

    private fun Node.forEachNode(action: (Node) -> Unit) {
        children().forEach {
            action(it as Node)
        }
    }

    fun read(inputUri: String): List<ChangelogEntry> {
        val input = File(inputUri)
        if (!input.exists()) {
            fail("Could not generate changelog from ${input.absolutePath}")
        }

        val parser = XmlParser().parse(inputUri)

        val entries: MutableList<ChangelogEntry> = mutableListOf()
        var version: String? = null
        val items: MutableList<String> = mutableListOf()

        fun addEntry() {
            version?.also { v ->
                entries.add(ChangelogEntry(v, items.toTypedArray()))
                items.clear()
            }
        }

        parser.depthFirst().mapNotNull { it as? Node }.forEach { n ->
            when (n.name()) {
                "version" -> {
                    addEntry()
                    version = n.attribute("title")?.toString() ?: ""
                }
                "item" -> {
                    n.attribute("text")?.toString()?.takeIf(String::isNotBlank)?.let {
                        items.add(it)
                    }
                }
            }
        }
        addEntry()
        return entries
    }

    fun generate(inputUri: String, outputUri: String): List<ChangelogEntry> {
        val entries = read(inputUri)
        val output = File(outputUri)
        if (output.exists()) {
            if (output.isDirectory) {
                fail("Cannot save changelog at directory ${output.absolutePath}")
            }
            if (output.isFile && !output.delete()) {
                fail("Could not delete changelog at ${output.absolutePath}")
            }
        } else {
            output.parentFile.mkdirs()
        }

        if (!output.createNewFile()) {
            fail("Could not create changelog file at ${output.absolutePath}")
        }
        val markdown = buildString {
            append("# Changelog\n")
            entries.forEach { e ->
                append("\n## ${e.version}\n")
                e.items.forEach {
                    append("* $it\n")
                }
            }
        }
        output.writeText(markdown)
        println("Generated changelog at ${output.absolutePath}")
        return entries
    }
}