Created
February 25, 2018 14:19
-
-
Save Pooh3Mobi/a9fa9b35930c5cc9d7192ded6a0d843f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Row.kt | |
sealed class Row { | |
data class Header( | |
val columns: List<String> | |
) : Row() | |
data class Item( | |
val id: Long, | |
val name: String, | |
val price: Long? | |
) : Row() | |
} | |
// Parser.kt | |
interface Parser { | |
fun parse(content: String): List<Row> | |
class IllegalFormatException(override val message: String) : Exception(message) | |
} | |
class CSVParser : Parser { | |
override fun parse(content: String): List<Row> { | |
val rows = content.split(System.getProperty("line.separator")) | |
val header = rows.getOrNull(0) | |
?.let { Row.Header(it.split(",")) } | |
?: throw Parser.IllegalFormatException("no header at row 1") | |
val items = rows.drop(1) | |
.mapIndexed { index, s -> | |
val l = s.split(",") | |
val id = l.getOrThrow(0, { "there are no 'id' at ${index + 1}"}) | |
.toLongOrThrow { "${index + 1} is not a number: $it" } | |
val name = l.getOrThrow(1, {"there are not 'name' at ${index + 1} "}) | |
val price = l.getOrNull(2)?.toLongOrNull() | |
Row.Item(id, name, price) | |
} | |
return listOfNotNull(header) + items | |
} | |
} | |
private inline fun List<String>.getOrThrow(index: Int, lazyMessage: () -> String): String { | |
return this.getOrElse(index, { throw Parser.IllegalFormatException(lazyMessage())}) | |
} | |
private inline fun String.toLongOrThrow(radix: Int = 10, lazyMessage: (String) -> String): Long { | |
try { | |
return toLong(radix) | |
} catch (e: NumberFormatException) { | |
throw Parser.IllegalFormatException(lazyMessage(this)) | |
} | |
} | |
// Formatter.ket | |
interface Formatter { | |
val items: List<Row> | |
fun format(): String | |
} | |
class TextFormatter( | |
override val items: List<Row> | |
) : Formatter { | |
override fun format(): String = items.toString() | |
} | |
class MarkdownFormatter( | |
override val items: List<Row> | |
) : Formatter { | |
override fun format(): String { | |
return items.mapNotNull { it as? Row.Item } | |
.joinToString(separator = "") { | |
if (it.price == null) { | |
""" | |
- ${it.id} | |
- ${it.name} | |
""" | |
} else { | |
""" | |
- ${it.id} | |
- ${it.name} | |
- ${it.price} | |
""" | |
} | |
} | |
} | |
} | |
class JSONFormatter( | |
override val items: List<Row>, | |
private val gson: Gson = GsonBuilder().create()) : Formatter { | |
override fun format(): String = gson.toJoson(items) | |
} | |
class MarkdownTableFormatter( | |
override val items: List<Row> | |
) : Formatter { | |
override fun format(): String = | |
items.joinToString( | |
separator = System.getProperty("line.separator")) { | |
when(it) { | |
is Row.Header -> { | |
val (header, line) = | |
it | |
.columns | |
.fold(StringBuilder("|") to StringBuilder("|") | |
, {(header, line), column -> (header.append("$column|")) to (line.append("---|")) | |
}) | |
header | |
.append(System.getProperty("line.separator")) | |
.append(line) | |
} | |
is Row.Item -> { | |
val r = "|${it.id}|${it.name}|" | |
if (it.price != null) { | |
r + "${it.price}|" | |
} else { | |
r + "|" | |
} | |
} | |
} | |
} | |
} | |
enum class Format { | |
JSON, MARKDOWN, TEXT, TABLE | |
} | |
// Loader.kt | |
interface Loader { | |
fun load(): String? | |
} | |
class FileLoader(private val file: File) : Loader { | |
init { | |
require(file.exists()) | |
} | |
override fun load(): String? = file.readText() | |
} | |
// Writer.kt | |
interface Writer { | |
fun write(formatter: Formatter) | |
} | |
class StdOutWriter: Writer { | |
override fun write(formatter: Formatter) = println(formatter.format()) | |
} | |
class FileWriter ( | |
private val file: File | |
) : Writer { | |
init { | |
require(file.parentFile?.exists() ?: true) | |
} | |
override fun write(formatter: Formatter) { | |
file.createNewFile() | |
file.printWriter().use { | |
it.println(formatter.format()) | |
} | |
} | |
} | |
// App.kt | |
object App { | |
@JvmStatic | |
fun main(args: Array<String>) { | |
val filePath = requireNotNull(args.get(0), {"you must set file path."}) | |
val outType = requireNotNull(args.get(1), {"you must set out type. chose... ${Format.values()}"}) | |
val destination = args.getOrNull(2) | |
println("$filePath $outType $destination") | |
val content = FileLoader(File(filePath).also { if (!it.exists()) throw IllegalArgumentException("$filePath not found.") }) | |
.load() | |
?.let { CSVParser().parse(it) } | |
?: throw IllegalArgumentException("file is empty") | |
val format = enumValueOf<Format>(outType.toUpperCase()) | |
val formatter = when(format) { | |
Format.JSON -> JSONFormatter(content) | |
Format.MARKDOWN -> MarkdownFormatter(content) | |
Format.TEXT -> TextFormatter(content) | |
Format.TABLE -> MarkdownTableFormatter(content) | |
} | |
val writer = if (destination == null) { | |
StdOutWriter() | |
} else { | |
FileWriter(File(filePath).also { | |
it.parentFile?.mkdir() | |
}) | |
} | |
writer.write(formatter) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment