0
0
mirror of https://github.com/thunderbird/thunderbird-android.git synced 2024-09-19 11:42:14 +02:00

Add String resource mover cli script

This commit is contained in:
Wolf-Martell Montwé 2024-06-04 13:52:42 +02:00
parent 06a6aa0ec3
commit d8ca1c5144
No known key found for this signature in database
GPG Key ID: 6D45B21512ACBF72
7 changed files with 252 additions and 0 deletions

View File

@ -0,0 +1,17 @@
# Resource Mover CLI
This is a command line interface that will move resources from one module to another.
## Usage
You can run the script with the following command:
```bash
./scripts/resource-mover --from <source-module-path> --to <target-module-path> --keys <keys-to-move>
```
The **source-module-path** should be the path to the module that contains the resources you want to move.
The **target-module-path** should be the path to the module where you want to move the resources.
The **keys-to-move** should be the keys of the resources you want to move. You can pass multiple keys separated by a comma.

View File

@ -0,0 +1,13 @@
plugins {
id(ThunderbirdPlugins.App.jvm)
}
version = "unspecified"
application {
mainClass.set("net.thunderbird.cli.resource.mover.MainKt")
}
dependencies {
implementation(libs.clikt)
}

View File

@ -0,0 +1,3 @@
package net.thunderbird.cli.resource.mover
fun main(args: Array<String>) = ResourceMoverCli().main(args)

View File

@ -0,0 +1,29 @@
package net.thunderbird.cli.resource.mover
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.options.required
import com.github.ajalt.clikt.parameters.options.split
class ResourceMoverCli(
private val stringResourceMover: StringResourceMover = StringResourceMover(),
) : CliktCommand(
name = "resource-mover",
help = "Move string resources from one file to another",
) {
private val from: String by option(
help = "Source module path",
).required()
private val to: String by option(
help = "Target module path",
).required()
private val keys: List<String> by option(
help = "Keys to move",
).split(",").required()
override fun run() {
stringResourceMover.moveKeys(from, to, keys)
}
}

View File

@ -0,0 +1,186 @@
package net.thunderbird.cli.resource.mover
import java.io.File
import kotlin.system.exitProcess
@Suppress("TooManyFunctions")
class StringResourceMover {
fun moveKeys(source: String, target: String, keys: List<String>) {
val sourcePath = File(source + RESOURCE_PATH)
val targetPath = File(target + RESOURCE_PATH)
if (!sourcePath.exists()) {
println("\nSource path does not exist: $sourcePath\n")
return
}
println("\nMoving keys $keys")
println(" from \"$sourcePath\" -> \"$targetPath\"\n")
for (key in keys) {
moveKey(sourcePath, targetPath, key)
}
}
private fun moveKey(sourcePath: File, targetPath: File, key: String) {
println("\nMoving key: $key\n")
sourcePath.walk()
.filter { it.name.startsWith(VALUES_PATH) }
.forEach { sourceDir ->
val sourceFile = sourceDir.resolve(STRING_RESOURCE_FILE_NAME)
if (sourceFile.exists()) {
moveKeyDeclaration(sourceFile, targetPath, key)
}
}
}
private fun moveKeyDeclaration(sourceFile: File, targetPath: File, key: String) {
if (containsKey(sourceFile, key)) {
println("\nFound key in file: ${sourceFile.path}\n")
val targetFile = getOrCreateTargetFile(targetPath, sourceFile)
val keyDeclaration = extractKeyDeclaration(sourceFile, key)
println(" Key declaration: $keyDeclaration")
copyKeyToTarget(targetFile, keyDeclaration, key)
deleteKeyFromSource(sourceFile, keyDeclaration)
if (isSourceFileEmpty(sourceFile)) {
println(" Source file is empty: ${sourceFile.path} -> deleting it.")
sourceFile.delete()
}
}
}
private fun containsKey(sourceFile: File, key: String): Boolean {
val keyPattern = createKeyPattern(key)
val sourceContent = sourceFile.readText()
return keyPattern.containsMatchIn(sourceContent)
}
private fun extractKeyDeclaration(sourceFile: File, key: String): String {
val keyPattern = createKeyPattern(key)
val declaration = StringBuilder()
var isTagClosed = true
sourceFile.forEachLine { line ->
if (keyPattern.containsMatchIn(line)) {
declaration.appendLine(line)
isTagClosed = isTagClosed(line)
} else if (!isTagClosed) {
declaration.appendLine(line)
isTagClosed = isTagClosed(line)
}
}
return declaration.toString()
}
private fun createKeyPattern(key: String): Regex {
return KEY_PATTERN.replace(KEY_PLACEHOLDER, Regex.escape(key)).toRegex()
}
private fun isTagClosed(line: String): Boolean {
return line.contains(STRING_CLOSING_TAG) || line.contains(PLURALS_CLOSING_TAG)
}
private fun copyKeyToTarget(targetFile: File, keyDeclaration: String, key: String) {
println(" Moving key to file: ${targetFile.path}")
if (containsKey(targetFile, key)) {
println(" Key already exists in target file: ${targetFile.path} replacing it.")
replaceKeyInTarget(targetFile, keyDeclaration, key)
} else {
addKeyToTarget(targetFile, keyDeclaration)
}
}
private fun addKeyToTarget(targetFile: File, keyDeclaration: String) {
val targetContent = StringBuilder()
targetFile.forEachLine { line ->
if (line.contains(RESOURCE_CLOSING_TAG)) {
targetContent.appendLine(keyDeclaration.trimEnd())
targetContent.appendLine(line)
} else {
targetContent.appendLine(line)
}
}
targetFile.writeText(targetContent.toString())
}
private fun replaceKeyInTarget(targetFile: File, keyDeclaration: String, key: String) {
println(" Replacing key in file: ${targetFile.path}")
val oldKeyDeclaration = extractKeyDeclaration(targetFile, key)
val targetContent = targetFile.readText()
targetFile.writeText(targetContent.replace(oldKeyDeclaration, keyDeclaration))
}
private fun deleteKeyFromSource(sourceFile: File, keyDeclaration: String) {
println(" Deleting key from file: ${sourceFile.path}")
val sourceContent = sourceFile.readText()
sourceFile.writeText(sourceContent.replace(keyDeclaration, ""))
}
private fun isSourceFileEmpty(sourceFile: File): Boolean {
val sourceContent = sourceFile.readText()
return sourceContent.contains(STRING_CLOSING_TAG).not() && sourceContent.contains(PLURALS_CLOSING_TAG).not()
}
private fun getOrCreateTargetFile(targetPath: File, sourceFile: File): File {
val targetFilePath = targetPath.resolve(sourceFile.parentFile.name)
val targetFile = File(targetFilePath, sourceFile.name)
val targetDirectory = targetFile.parentFile
if (!targetDirectory.exists()) {
targetDirectory.mkdirs()
println(" Target directory created: ${targetDirectory.path}")
}
if (!targetFile.exists()) {
createTargetFile(targetFile)
}
return targetFile
}
private fun createTargetFile(targetFile: File) {
val isNewFileCreated: Boolean = targetFile.createNewFile()
if (!isNewFileCreated) {
printError("Target file could not be created: ${targetFile.path}")
exitProcess(-1)
}
targetFile.writeText(TARGET_FILE_CONTENT)
println("Target file ${targetFile.path} created")
}
private fun printError(message: String) {
System.err.println("\n$message\n")
}
private companion object {
const val RESOURCE_PATH = "/src/main/res/"
const val KEY_PLACEHOLDER = "{KEY}"
const val KEY_PATTERN = """name="$KEY_PLACEHOLDER""""
const val VALUES_PATH = "values"
const val STRING_RESOURCE_FILE_NAME = "strings.xml"
const val STRING_CLOSING_TAG = "</string>"
const val PLURALS_CLOSING_TAG = "</plurals>"
const val RESOURCE_CLOSING_TAG = "</resources>"
val TARGET_FILE_CONTENT = """
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
</resources>
""".trimIndent()
}
}

3
scripts/resource_mover Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
./gradlew --quiet ":cli:resource-mover-cli:installDist" < /dev/null && ./cli/resource-mover-cli/build/install/resource-mover-cli/bin/resource-mover-cli "$@"

View File

@ -115,6 +115,7 @@ include(":plugins:openpgp-api-lib:openpgp-api")
include(
":cli:autodiscovery-cli",
":cli:html-cleaner-cli",
":cli:resource-mover-cli",
":cli:translation-cli",
)