Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions build.mill
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ object Config {
val oslib = ivy"com.lihaoyi::os-lib:0.11.5"
val scalatags = ivy"com.lihaoyi::scalatags::0.13.1"
val fansi = ivy"com.lihaoyi::fansi::0.5.1"

val pprint = ivy"com.lihaoyi::pprint::0.9.3"


val laminar = ivy"com.raquo::laminar::17.2.1"
Expand All @@ -29,7 +29,8 @@ trait Common extends ScalaModule {
override def ivyDeps = super.ivyDeps() ++ Agg(
Config.scalatags,
Config.oslib,
Config.fansi
Config.fansi,
Config.pprint
)
override def scalacOptions: T[Seq[String]] = super.scalacOptions() ++ Seq("-Xmax-inlines", "128")
}
Expand Down
19 changes: 11 additions & 8 deletions examples/src/titanic.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import io.github.quafadas.scautable.ColumnTyped.IsNumeric
import io.github.quafadas.scautable.ColumnTyped.GetTypeAtName
import io.github.quafadas.scautable.ColumnTyped.AllAreColumns
import scala.concurrent.Future
import io.github.quafadas.scautable.CsvIteratorTPrint
import scala.util.Try

enum Gender:
case Male, Female, Unknown
Expand Down Expand Up @@ -46,15 +48,16 @@ end Gender
*/
@main def titanic =

val titanic = CSV.resource("titanic.csv")
val titanic = CSV.resource("titanic.csv", TypeInferrer.Auto)

val data = titanic.toSeq
.mapColumn["Sex", Gender]((x: String) => Gender.valueOf(x.capitalize))
.dropColumn["PassengerId"]
.mapColumn["Age", Option[Double]](_.toDoubleOption)
.mapColumn["Survived", Boolean](_ == "1")
.mapColumn["Fare", Double](_.toDouble)
.addColumn["AgeIsDefined", Boolean](_.Age.isDefined)
val data = LazyList.from(
titanic
.mapColumn["Sex", Gender]((x: String) => Gender.valueOf(x.capitalize))
.dropColumn["PassengerId"]
.mapColumn["Age", Option[Double]](a =>Try(a.toDouble).toOption)
.mapColumn["Survived", Boolean](_ == 1)
.addColumn["AgeIsDefined", Boolean](_.Age.isDefined)
)

val surived: (survivied: Int, total: Int, pct: Double) = data
.column["Survived"]
Expand Down
10 changes: 10 additions & 0 deletions scautable/src/columnExtensions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ object NamedTupleIteratorExtensions:

extension [K <: Tuple, V <: Tuple](itr: Iterator[NamedTuple[K, V]])

inline def info: fansi.Str =
val headers = constValueTuple[K].toList.map(_.toString())

val colTypes = CsvIteratorTPrint.getTypeNames[V]
val coltypes = headers.zipAll(colTypes, "<missing>", "<unknown>")
.map { case (name, colType) => fansi.Color.Red(name) ++ fansi.Str(": ") ++ fansi.Color.Green(colType) }
.mkString("[\n\t", ",\n\t", "\n]")
fansi.Str(coltypes)


inline def numericTypeTest: (List[ConversionAcc], Long) =
val headers = constValueTuple[K].toList.map(_.toString())
val headerAcc = headers.map(_ => ConversionAcc(0, 0, 0))
Expand Down
29 changes: 29 additions & 0 deletions scautable/src/tprint.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package io.github.quafadas.scautable

import pprint.TPrint
import pprint.TPrintColors

object CsvIteratorTPrint:
given csvIterTPrint[K <: Tuple, V <: Tuple]: TPrint[CsvIterator[K, V]] = new TPrint[CsvIterator[K, V]]:
def render(implicit tpc: TPrintColors): fansi.Str =
fansi.Str("CsvIterator[K <: Tuple, V <: Tuple]")

import scala.compiletime.{erasedValue, summonInline}
import scala.deriving.Mirror
import scala.annotation.tailrec


inline def getTypeNames[T <: Tuple](using tpc: TPrintColors): List[String] =
inline erasedValue[T] match
case _: EmptyTuple => Nil
case _: (h *: t) => summonInline[TPrint[h]].render(tpc).toString :: getTypeNames[t]

extension [K <: Tuple, V <: Tuple](csvIterator: CsvIterator[K, V])
inline def prettyPrint(using tpc: TPrintColors): fansi.Str =
val columnNames = csvIterator.headers

val colTypes = getTypeNames[V]
val coltypes = columnNames.zipAll(colTypes, "<missing>", "<unknown>")
.map { case (name, colType) => fansi.Str(s"$name: $colType") }
.mkString("[\n\t", ",\n\t", "\n]")
fansi.Str("CsvIterator") ++ fansi.Str(coltypes)
73 changes: 73 additions & 0 deletions scautable/test/src-jvm/tprintTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package io.github.quafadas.scautable

import pprint.TPrint
import pprint.TPrintColors

class TPrintSuite extends munit.FunSuite:

test("CsvIterator TPrint should render type info") {
val csv: CsvIterator[("col1", "col2", "col3"), (String, String, String)] = CSV.resource("simple.csv")
val tprint = summon[TPrint[CsvIterator[("col1", "col2", "col3"), (String, String, String)]]]
implicit val colors: TPrintColors = TPrintColors.BlackWhite
val rendered = tprint.render.toString()
// Just verify the output contains CsvIterator - the exact format doesn't matter as much
assert(rendered.contains("CsvIterator"), s"Expected 'CsvIterator' in '$rendered'")
}

test("CsvIterator prettyPrint extension should show column details") {
import io.github.quafadas.scautable.CsvIteratorTPrint.*

// Create a CSV iterator with known columns
val csv: CsvIterator[("col1", "col2", "col3"), (String, String, String)] = CSV.resource("simple.csv")

implicit val colors: TPrintColors = TPrintColors.BlackWhite

// Use the extension method directly
val prettyOutput = prettyPrint(csv).plainText


// Verify the output contains the expected column information
assert(prettyOutput.contains("CsvIterator"), s"Expected 'CsvIterator' in '$prettyOutput'")
assert(prettyOutput.contains("\tcol1: String,"), s"Expected 'col1: String' in '$prettyOutput'")
assert(prettyOutput.contains("\tcol2: String,"), s"Expected 'col2: String' in '$prettyOutput'")
assert(prettyOutput.contains("col3: String"), s"Expected 'col3: String' in '$prettyOutput'")

// The prettyPrint output looks good from the debug output
assert(prettyOutput.startsWith("CsvIterator["))
assert(prettyOutput.endsWith("]"))
}

test("CsvIterator prettyPrint should work with different column names") {
import io.github.quafadas.scautable.CsvIteratorTPrint.*

// Test with different column names
val csv: CsvIterator[("name", "age"), (String, String)] = CSV.fromString("name,age\nAlice,25\nBob,30")

implicit val colors: TPrintColors = TPrintColors.BlackWhite

val prettyOutput = prettyPrint(csv).plainText

assert(prettyOutput.contains("CsvIterator"), s"Expected 'CsvIterator' in '$prettyOutput'")
assert(prettyOutput.contains("\tname: String,"), s"Expected 'name: String' in '$prettyOutput'")
assert(prettyOutput.contains("\tage: String"), s"Expected 'age: String' in '$prettyOutput'")

assert(prettyOutput.startsWith("CsvIterator["))
assert(prettyOutput.endsWith("]"))
}

test("CsvIterator prettyPrint should handle single column") {
import io.github.quafadas.scautable.CsvIteratorTPrint.*

// Test with a single column CSV from string
val csv = CSV.fromString("singleCol\nvalue1\nvalue2")

implicit val colors: TPrintColors = TPrintColors.BlackWhite

val prettyOutput = prettyPrint(csv).plainText

assert(prettyOutput.contains("CsvIterator"), s"Expected 'CsvIterator' in '$prettyOutput'")
assert(prettyOutput.contains("singleCol: String"), s"Expected 'singleCol: String' in '$prettyOutput'")

assert(prettyOutput.startsWith("CsvIterator["))
assert(prettyOutput.endsWith("]"))
}