Skip to content

Instantly share code, notes, and snippets.

@Pooh3Mobi
Created February 25, 2018 14:19
Show Gist options
  • Save Pooh3Mobi/a9fa9b35930c5cc9d7192ded6a0d843f to your computer and use it in GitHub Desktop.
Save Pooh3Mobi/a9fa9b35930c5cc9d7192ded6a0d843f to your computer and use it in GitHub Desktop.
// 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