--- /dev/null
+package jp.ymir.taskReporter.core
+import scala.collection._
+import scala.collection.immutable.StringLike
+
+object TSV {
+ type Header = Seq[Symbol]
+ type Field = String
+ type Record = Map[Symbol, Field]
+
+ def decode[A: FromRecord](tsv: StringLike[_]): Seq[A] = {
+ val lines = tsv.split('\n').filter(!_.isEmpty)
+ val header = lines.head.split('\t').map(Symbol(_))
+ val body = lines.tail.map(line => (header zip line.split('\t')).toMap)
+ return body.map(implicitly[FromRecord[A]].parseRecord)
+ }
+
+ trait FromRecord[A] {
+ def parseRecord(r: Record): A
+ }
+
+ trait FromField[A] {
+ def parseField(f: Field): A
+ }
+ def parseField[A: FromField](f: Field): A
+ = implicitly[FromField[A]].parseField(f)
+
+ trait ToRecord[A] {
+ def toRecord(a: A): Record
+ }
+
+ trait ToField[A] {
+ def toField(a: A): Field
+ }
+
+ // Option[A]
+ implicit def OptionFromField[A: FromField] = new FromField[Option[A]] {
+ def parseField(f: Field): Option[A] = {
+ if (f.isEmpty) {
+ return None
+ }
+ else {
+ return Some(implicitly[FromField[A]].parseField(f))
+ }
+ }
+ }
+
+ // String
+ implicit def StringFromField = new FromField[String] {
+ def parseField(f: Field): String = f
+ }
+
+ // Int
+ implicit def IntFromField = new FromField[Int] {
+ def parseField(f: Field): Int = f.toInt
+ }
+}