diff --git a/.mill-version b/.mill-version index d61567c..6799343 100644 --- a/.mill-version +++ b/.mill-version @@ -1 +1 @@ -0.12.3 \ No newline at end of file +0.12.5 \ No newline at end of file diff --git a/.scalafmt.conf b/.scalafmt.conf index cd8e6b3..12b677c 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,9 +1,14 @@ -version = "3.6.1" +version = "3.8.4" +project.git = true runner.dialect = scala3 - +rewrite.scala3.convertToNewSyntax = true +rewrite.rules = [RedundantBraces] +rewrite.scala3.removeOptionalBraces = yes +runner.dialectOverride.withAllowToplevelTerms = true +runner.dialectOverride.withAllowEndMarker = true +runner.dialectOverride.allowSignificantIndentation = true +rewrite.scala3.countEndMarkerLines = lastBlockOnly +rewrite.scala3.insertEndMarkerMinLines = 1 indent.main = 2 -indent.defnSite = 2 - -align.preset = more // For pretty alignment. -maxColumn = 150 // For my wide 30" display. -runner.dialect = scala3 +maxColumn = 120 +project.excludeFilters = [ ".*/build\\.mill"] \ No newline at end of file diff --git a/README.md b/README.md index 1c8ee19..3db2c9f 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,16 @@ +[docs](http://localhost:3002/docs/index.html) + # SCala AUto TABLE -Auto-magically generate html tables from case classes + +- Strongly typed compile-time CSV +- pretty printing to console for `Product` types +- Auto-magically generate html tables from case classes +- Searchable, sortable browser GUI for your tables ## Elevator Pitch +One line CSV import. + Insta-auto-magically generate a [scalatags](https://github.com/com-lihaoyi/scalatags) table from a `Seq[A]` (case classes). with scala-cli @@ -28,17 +36,28 @@ It cross compiles, and gives you back a scalatags table ## Infrequently Asked Questions ### Is this project a good idea -Unclear. But I wanted to play with Mirrors. - -And have a "real" library to test build cross building stuff, GHA etc on. +Unclear. One of it's purposes is to push the boundary of my metaprogramming knowledge. If you use this, it exposes you to the very real risk of the reality that this is an educational project I run on my own time. ### How does it work + +The macro stuff came from smashing my head against chatGPT and a wall. A lot. + +The table derivation stuff comes from here; I aggressively copy pasted everything from here and poked it with a sharp stick until it did what I wanted. https://blog.philipp-martini.de/blog/magic-mirror-scala3/ ### Limitations -See tests; + +For the desktop show part - - Formatting is implied by the type. To format your own types, you'll need to write a given for it. - Extension is through the type system, have a look at the JVM tests for an example if writing a given for your own custom type - As I don't _really_ understand how it works, it's unlikely to get extended further... -- Extending it further is probably a really bad idea anyway \ No newline at end of file +- Extending it further is probably a really bad idea anyway + +For the CSV part : +- It is assumed you have one header row, and that your headers are reasonably representaable by the compiler. +- As of early 2024 there is a compiler bug that reverses the order of large named tuples. CSV files over 22 might get weird - I don't believe the limitation to be fundaemntal, just need to wait for the fix. + +// TODO: Docs + +// TODO: Sample (graduated) \ No newline at end of file diff --git a/build.sc b/build.sc index a41a534..f8c8c94 100644 --- a/build.sc +++ b/build.sc @@ -1,7 +1,9 @@ import $ivy.`com.github.lolgab::mill-crossplatform::0.2.4` import $ivy.`io.github.quafadas:millSite_mill0.12_2.13:0.0.38` import $ivy.`de.tototec::de.tobiasroeser.mill.vcs.version::0.4.0` +import $ivy.`com.lihaoyi::mill-contrib-buildinfo:` +import mill.contrib.buildinfo.BuildInfo import de.tobiasroeser.mill.vcs.version._ import com.github.lolgab.mill.crossplatform._ import mill._, mill.scalalib._, mill.scalajslib._, mill.scalanativelib._ @@ -19,8 +21,7 @@ trait Common extends ScalaModule with PublishModule { ivy"com.lihaoyi::os-lib:0.11.3", ivy"com.lihaoyi::fansi::0.5.0" ) - - def ammoniteVersion = "3.0.0" + override def scalacOptions: T[Seq[String]] = super.scalacOptions() ++ Seq("-experimental", "-language:experimental.namedTuples", "-Xmax-inlines", "128") def publishVersion = VcsVersion.vcsState().format() @@ -60,7 +61,32 @@ object scautable extends CrossPlatform { } object jvm extends Shared { // jvm specific settings here - object test extends ScalaTests with SharedTests + object test extends ScalaTests with SharedTests with BuildInfo { + + def buildInfoPackageName: String = "io.github.quafadas.scautable" + + + override def generatedSources: T[Seq[PathRef]] = T{ + val resourceDir = resources().map(_.path).zipWithIndex.map{case (str, i) => s"""final val resourceDir$i = \"\"\"$str${java.io.File.separator}\"\"\"""" }.mkString("\n\t") + val fileName = "BuildInfo.scala" + val code = s""" + +package io.github.quafadas.scautable + +/** +Resources are not available at compile time. This is a workaround to get the path to the resource directory, (allowing unit testing of a macro based on a local file). +*/ + +object Generated {$resourceDir +} +""" + val dest = T.ctx().dest / "BuildInfo.scala" + os.write(dest , code) + Seq(PathRef(dest)) + + } + + } } object js extends Shared with CommonJS { // js specific settings here @@ -73,6 +99,8 @@ object site extends SiteModule { def scalaVersion = scautable.jvm.scalaVersion + override def scalacOptions: T[Seq[String]] = super.scalacOptions() ++ scautable.jvm.scalacOptions() + override def moduleDeps = Seq(scautable.jvm) } diff --git a/scautable/jvm/src/jvmSpecific.scala b/scautable/jvm/src/jvmSpecific.scala index cf846a6..63b6903 100644 --- a/scautable/jvm/src/jvmSpecific.scala +++ b/scautable/jvm/src/jvmSpecific.scala @@ -2,14 +2,16 @@ package io.github.quafadas.scautable import java.awt.Desktop import io.github.quafadas.scautable.scautable.HtmlTableRender +import NamedTuple.* // import almond.api.JupyterApi // import almond.interpreter.api.DisplayData // import almond.api.JupyterAPIHolder.value -trait PlatformSpecific { +trait PlatformSpecific: - private def openBrowserWindow(uri: java.net.URI): Unit = { - if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) then Desktop.getDesktop().browse(uri) + private def openBrowserWindow(uri: java.net.URI): Unit = + if Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE) then + Desktop.getDesktop().browse(uri) else /* Hail Mary... https://stackoverflow.com/questions/5226212/how-to-open-the-default-webbrowser-using-java @@ -29,16 +31,47 @@ trait PlatformSpecific { */ val runtime = java.lang.Runtime.getRuntime() runtime.exec(Array[String](s"""xdg-open $uri]""")) - } - /** - * Attempts to open a browser window, and display this Seq of `Product` as a table. + inline def desktopShowNt[K <: Tuple, V <: Tuple](a: Seq[NamedTuple[K, V]])(using + tableDeriveInstance: HtmlTableRender[V] + ): os.Path = + val asString = scautable.nt(a).toString() + val theHtml = raw""" + + +
+ + + + + + +