blob: e4bd356bae7bc7f983c86fd3194f266b15d387dd [file] [log] [blame]
/*
* Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package bench.flame.diff.ui
import bench.flame.diff.interop.FileWithId
import bench.flame.diff.interop.exitProcessWithError
import bench.flame.diff.interop.file
import bench.flame.diff.interop.id
import bench.flame.diff.interop.withId
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.core.terminal
import com.github.ajalt.mordant.rendering.Whitespace
import com.github.ajalt.mordant.table.table
import com.github.ajalt.mordant.terminal.ConversionResult
import com.github.ajalt.mordant.terminal.YesNoPrompt
import java.io.File
import java.text.SimpleDateFormat
import java.util.Date
internal fun CliktCommand.printFileTable(files: List<FileWithId>, trimBaseDir: File?) {
val trimPrefix: String = if (trimBaseDir == null) "" else "${trimBaseDir.canonicalPath}/"
terminal.println(table {
whitespace = Whitespace.PRE_WRAP
header { row("Id", "Name", "Last Modified") }
body {
files.map {
val lastModified = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
.format(Date(it.file.lastModified()))
row(it.id, it.file.canonicalPath.removePrefix(trimPrefix), lastModified)
}
}
})
}
internal fun CliktCommand.promptPickFile(candidates: List<FileWithId>, trimBaseDir: File?): File {
printFileTable(candidates, trimBaseDir)
return terminal.prompt("Choose a file by id") {
val number = it.toIntOrNull()
val min = candidates.minOf { f -> f.id }
val max = candidates.maxOf { f -> f.id }
when {
number == null || candidates.none { f -> f.id == number } ->
ConversionResult.Invalid("Choose a number between $min and $max")
else ->
ConversionResult.Valid(candidates.single { f -> f.id == number }.file)
}
}!!
}
internal fun CliktCommand.promptProvideFile(
prompt: String,
includePattern: String = ".*",
excludePattern: String? = null,
srcDir: File? = null,
defaultSrcDir: File? = null
): File {
check(srcDir == null || srcDir.isDirectory)
check(defaultSrcDir == null || defaultSrcDir.isDirectory)
val baseDir = run {
val src = srcDir ?: terminal.prompt(prompt, defaultSrcDir) {
when {
it.isBlank() || !File(it).exists() -> ConversionResult.Invalid("Invalid value")
else -> ConversionResult.Valid(File(it))
}
}!!
if (src.isFile) return src
src
}
check(baseDir.isDirectory)
echo("Looking for files in '${baseDir.absolutePath}' matching '$includePattern'...")
val candidates = baseDir.walkTopDown()
.filter { it.isFile }
.filter { it.name.matches(Regex(includePattern)) }
.filter { if (excludePattern == null) true else !it.name.matches(Regex(excludePattern)) }
.sortedBy { -it.lastModified() }
.withId()
.toList()
if (candidates.isEmpty()) exitProcessWithError(
"No files matching '$includePattern'" +
(if (excludePattern == null) "" else " (and excluding $excludePattern)") +
" in '$baseDir'"
)
return promptPickFile(candidates, baseDir)
}
internal fun CliktCommand.promptOverwriteFile(file: File): Boolean = YesNoPrompt(
"Overwrite existing file '${file.absolutePath}'",
terminal
).ask()!!