From 46c13d25a4a4335033254900a2858fa2f3b76ddc Mon Sep 17 00:00:00 2001
From: Justin Brooks <justin@jzbrooks.com>
Date: Fri, 11 Oct 2024 21:13:53 -0400
Subject: [PATCH] Assign a file extension at the last possible second

---
 vgo/src/main/kotlin/com/jzbrooks/vgo/Vgo.kt | 144 ++++++++++----------
 1 file changed, 70 insertions(+), 74 deletions(-)

diff --git a/vgo/src/main/kotlin/com/jzbrooks/vgo/Vgo.kt b/vgo/src/main/kotlin/com/jzbrooks/vgo/Vgo.kt
index 78ffa043..2e4bda8a 100644
--- a/vgo/src/main/kotlin/com/jzbrooks/vgo/Vgo.kt
+++ b/vgo/src/main/kotlin/com/jzbrooks/vgo/Vgo.kt
@@ -16,8 +16,13 @@ import org.w3c.dom.Document
 import java.io.ByteArrayInputStream
 import java.io.ByteArrayOutputStream
 import java.io.File
+import java.nio.file.Path
 import java.nio.file.Paths
 import javax.xml.parsers.DocumentBuilderFactory
+import kotlin.io.path.exists
+import kotlin.io.path.isDirectory
+import kotlin.io.path.isRegularFile
+import kotlin.io.path.isSameFileAs
 import kotlin.math.absoluteValue
 import kotlin.math.roundToInt
 
@@ -53,17 +58,7 @@ class Vgo(
             inputs = standardInPaths
         }
 
-        val inputOutputMap =
-            if (options.output.isNotEmpty()) {
-                inputs.zip(options.output) { a, b ->
-                    Pair(File(a), File(b))
-                }
-            } else {
-                inputs.zip(inputs) { a, b ->
-
-                }
-            }.toMap()
-
+        val inputOutputMap = pairOutputs()
         val files = inputOutputMap.count { (input, _) -> input.isFile }
         val containsDirectory = inputOutputMap.any { (input, _) -> input.isDirectory }
         printFileNames = options.printStats && (files > 1 || containsDirectory)
@@ -71,67 +66,21 @@ class Vgo(
         return handleFiles(inputOutputMap, writerOptions)
     }
 
-    private fun pairOutputs(): Map<File, File> {
-        val inputFiles = options.input.map(::File)
+    private fun pairOutputs(): Map<File, Path> {
         return if (options.output.isNotEmpty()) {
-            inputFiles.zip(options.output.map(::File)).toMap()
+            options.input.zip(options.output) { a, b ->
+                Pair(File(a), Paths.get(b))
+            }
         } else {
-            inputFiles.mapIndexed { index, input ->
-                if (!input.isDirectory) {
-                    val outputExtension = when (options.format) {
-                        "svg" -> "svg"
-                        "vd" -> "xml"
-                        else -> input.extension
-                    }
-                    val outputFilePath = options.output.dropLastWhile { it != '.' } + outputExtension
-                }
-            }.toMap()
-        }
-    }
-
-    private fun handleFiles(
-        inputOutputMap: Map<File, File>,
-        writerOptions: Set<Writer.Option>,
-    ): Int {
-        for (entry in inputOutputMap) {
-            val (input, output) = entry
-
-            when {
-                entry.isFilePair -> handleFile(input, output, writerOptions)
-                entry.isDirectoryPair -> handleDirectory(input, output, writerOptions)
-                !entry.inputExists -> {
-                    System.err.println("${input.path} does not exist.")
-                    return 65
-                }
-                else -> {
-                    System.err.println(
-                        """
-                        A given input and output pair (grouped positionally)
-                        must be either files or directories.
-                        Input is a ${if (input.isFile) "file" else "directory"}
-                            path: ${input.absolutePath}
-                            exists: ${input.exists()}
-                            isWritable: ${input.canWrite()}
-                        Output is a ${if (output.isFile) "file" else "directory"}
-                            path: ${output.absolutePath}
-                            exists: ${input.exists()}
-                            isWritable: ${input.canWrite()}
-
-                        Storage: ${output.usableSpace} / ${output.totalSpace} is usable.
-                        """.trimIndent(),
-                    )
-
-                    return 65
-                }
+            options.input.zip(options.input) { a, b ->
+                Pair(File(a), Paths.get(b))
             }
-        }
-
-        return 0
+        }.toMap()
     }
 
     private fun handleFile(
         input: File,
-        output: File,
+        outputPath: Path,
         options: Set<Writer.Option>,
     ) {
         input.inputStream().use { inputStream ->
@@ -186,7 +135,7 @@ class Vgo(
                         com.jzbrooks.vgo.vd
                             .parse(rootNodes.first())
                     }
-                    else -> if (input == output) return else null
+                    else -> if (outputPath.isSameFileAs(input.toPath())) return else null
                 }
 
             if (graphic is VectorDrawable && this.options.format == "svg") {
@@ -204,6 +153,12 @@ class Vgo(
                 optimizationRegistry?.apply(graphic)
             }
 
+            val output = when(this.options.format) {
+                "vd" -> outputPath.resolveSibling("${outputPath.fileName}.xml")
+                "svg" -> outputPath.resolveSibling("${outputPath.fileName}.svg")
+                else -> outputPath
+            }.toFile()
+
             if (output.parentFile?.exists() == false) output.parentFile.mkdirs()
             if (!output.exists()) output.createNewFile()
 
@@ -239,16 +194,57 @@ class Vgo(
         }
     }
 
+    private fun handleFiles(
+        inputOutputMap: Map<File, Path>,
+        writerOptions: Set<Writer.Option>,
+    ): Int {
+        for (entry in inputOutputMap) {
+            val (input, output) = entry
+
+            when {
+                entry.isFilePair -> handleFile(input, output, writerOptions)
+                entry.isDirectoryPair -> handleDirectory(input, output, writerOptions)
+                !entry.inputExists -> {
+                    System.err.println("${input.path} does not exist.")
+                    return 65
+                }
+                else -> {
+                    val output = output.toFile()
+                    System.err.println(
+                        """
+                        A given input and output pair (grouped positionally)
+                        must be either files or directories.
+                        Input is a ${if (input.isFile) "file" else "directory"}
+                            path: ${input.absolutePath}
+                            exists: ${input.exists()}
+                            isWritable: ${input.canWrite()}
+                        Output is a ${if (output.isFile) "file" else "directory"}
+                            path: ${output.absolutePath}
+                            exists: ${input.exists()}
+                            isWritable: ${input.canWrite()}
+
+                        Storage: ${output.usableSpace} / ${output.totalSpace} is usable.
+                        """.trimIndent(),
+                    )
+
+                    return 65
+                }
+            }
+        }
+
+        return 0
+    }
+
     private fun handleDirectory(
         input: File,
-        output: File,
+        output: Path,
         options: Set<Writer.Option>,
     ) {
         assert(input.isDirectory)
-        assert(output.isDirectory || !output.exists())
+        assert(output.isDirectory() || !output.exists())
 
         for (file in input.walkTopDown().filter { file -> !file.isHidden && !file.isDirectory }) {
-            handleFile(file, File(output, file.name), options)
+            handleFile(file, output.resolve(file.name), options)
         }
 
         if (this.options.printStats) {
@@ -282,14 +278,14 @@ class Vgo(
             else -> "$bytes B"
         }
 
-    private val Map.Entry<File, File>.inputExists
+    private val Map.Entry<File, Path>.inputExists
         get() = key.exists()
 
-    private val Map.Entry<File, File>.isFilePair
-        get() = key.isFile && (value.isFile || !value.exists())
+    private val Map.Entry<File, Path>.isFilePair
+        get() = key.isFile && (value.isRegularFile() || !value.exists())
 
-    private val Map.Entry<File, File>.isDirectoryPair
-        get() = key.isDirectory && (value.isDirectory || !value.exists())
+    private val Map.Entry<File, Path>.isDirectoryPair
+        get() = key.isDirectory && (value.isDirectory() || !value.exists())
 
     data class Options(
         val printVersion: Boolean = false,