Skip to content
Merged
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
51 changes: 49 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,11 @@ jobs:

- name: Make target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
run: mkdir -p toolkit/js/target target toolkit/native/target .js/target toolkit/jvm/target .jvm/target .native/target project/target
run: mkdir -p toolkit/js/target target toolkit/native/target .js/target site/target toolkit/jvm/target .jvm/target .native/target project/target

- name: Compress target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
run: tar cf targets.tar toolkit/js/target target toolkit/native/target .js/target toolkit/jvm/target .jvm/target .native/target project/target
run: tar cf targets.tar toolkit/js/target target toolkit/native/target .js/target site/target toolkit/jvm/target .jvm/target .native/target project/target

- name: Upload target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
Expand Down Expand Up @@ -293,3 +293,50 @@ jobs:

- name: Submit Dependencies
uses: scalacenter/sbt-dependency-submission@v2

site:
name: Generate Site
strategy:
matrix:
os: [ubuntu-latest]
scala: [3.2.2]
java: [temurin@17]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout current branch (full)
uses: actions/checkout@v3
with:
fetch-depth: 0

- name: Download Java (temurin@17)
id: download-java-temurin-17
if: matrix.java == 'temurin@17'
uses: typelevel/download-java@v2
with:
distribution: temurin
java-version: 17

- name: Setup Java (temurin@17)
id: setup-java-temurin-17
if: matrix.java == 'temurin@17'
uses: actions/setup-java@v3
with:
distribution: jdkfile
java-version: 17
jdkFile: ${{ steps.download-java-temurin-17.outputs.jdkFile }}
cache: sbt

- name: sbt update
if: matrix.java == 'temurin@17' && steps.setup-java-temurin-17.outputs.cache-hit == 'false'
run: sbt '++ ${{ matrix.scala }}' reload +update

- name: Generate site
run: sbt '++ ${{ matrix.scala }}' docs/tlSite

- name: Publish site
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
uses: peaceiris/actions-gh-pages@v3.9.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: site/target/docs/site
keep_files: true
8 changes: 8 additions & 0 deletions .mergify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ pull_request_rules:
- status-success=Build and Test (ubuntu-latest, 3, temurin@17, rootNative)
actions:
merge: {}
- name: Label site PRs
conditions:
- files~=^site/
actions:
label:
add:
- site
remove: []
- name: Label toolkit PRs
conditions:
- files~=^toolkit/
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ It currently includes:

* [Cats] and [Cats Effect]
* [FS2] and [FS2 I/O]
* [FS2 Data Csv] and its generic module
* [http4s Ember client]
* [Circe] and http4s integration
* [Decline Effect]
Expand All @@ -25,6 +26,7 @@ It currently includes:
[Cats Effect]: https://typelevel.org/cats-effect
[FS2]: https://fs2.io/#/
[FS2 I/O]: https://fs2.io/#/io
[FS2 Data Csv]: https://fs2-data.gnieh.org/documentation/csv/
[http4s Ember Client]: https://http4s.org/v0.23/docs/client.html
[Circe]: https://circe.github.io/circe/
[Decline Effect]: https://ben.kirw.in/decline/effect.html
Expand Down
46 changes: 45 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import laika.helium.config._
import laika.rewrite.nav.{ChoiceConfig, Selections, SelectionConfig}

ThisBuild / tlBaseVersion := "0.0"
ThisBuild / startYear := Some(2023)

ThisBuild / tlSitePublishBranch := Some("main")
ThisBuild / githubWorkflowJavaVersions := Seq(JavaSpec.temurin("17"))
ThisBuild / mergifyStewardConfig ~= {
_.map(_.copy(author = "typelevel-steward[bot]"))
Expand All @@ -26,3 +29,44 @@ lazy val toolkit = crossProject(JVMPlatform, JSPlatform, NativePlatform)
),
mimaPreviousArtifacts := Set()
)

lazy val docs = project
.in(file("site"))
.enablePlugins(TypelevelSitePlugin)
.dependsOn(toolkit.jvm)
.settings(
scalaVersion := "3.2.2",
tlSiteHelium ~= {
_.site.mainNavigation(
appendLinks = List(
ThemeNavigationSection(
"Related Projects",
TextLink.external("https://github.com/typelevel/fs2", "fs2"),
TextLink.external("https://github.com/typelevel/cats", "Cats"),
TextLink.external("https://github.com/circe/circe", "Circe"),
TextLink.external("https://github.com/http4s/http4s", "Http4s"),
TextLink.external("https://github.com/bkirwi/decline", "Decline"),
TextLink.external(
"https://github.com/typelevel/cats-effect",
"Cats Effect"
),
TextLink.external(
"https://github.com/typelevel/munit-cats-effect",
"Munit Cats Effect"
)
)
)
)
},
laikaConfig ~= {
_.withConfigValue(
Selections(
SelectionConfig(
"scala-version",
ChoiceConfig("scala-3", "Scala 3"),
ChoiceConfig("scala-2", "Scala 2")
)
)
)
}
)
4 changes: 4 additions & 0 deletions docs/directory.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
laika.navigationOrder = [
index.md
examples.md
]
172 changes: 172 additions & 0 deletions docs/examples.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
# Examples

This page contains examples of how typelevel-toolkit and [Scala CLI] work together to write single file scripts using all the power of the Typelevel's libraries.

## POSTing data and writing the response to a file
This example was written by [Koroeskohr] and taken from the [Virtuslab Blog](https://virtuslab.com/blog/scala-toolkit-makes-scala-powerful-straight-out-of-the-box/).

@:select(scala-version)

@:choice(scala-3)

```scala mdoc:silent
//> using lib "org.typelevel::toolkit::@VERSION@"

import cats.effect.*
import io.circe.Decoder
import fs2.Stream
import fs2.io.file.*
import org.http4s.ember.client.*
import org.http4s.*
import org.http4s.implicits.*
import org.http4s.circe.*

object Main extends IOApp.Simple:
case class Data(value: String)
given Decoder[Data] = Decoder.forProduct1("data")(Data.apply)
given EntityDecoder[IO, Data] = jsonOf[IO, Data]

def run = EmberClientBuilder.default[IO].build.use { client =>
val request: Request[IO] =
Request(Method.POST, uri"https://httpbin.org/anything")
.withEntity("file.txt bunchofdata")

client
.expect[Data](request)
.map(_.value.split(" "))
.flatMap { case Array(fileName, content) =>
IO.println(s"Writing data to $fileName") *>
Stream(content)
.through(fs2.text.utf8.encode)
.through(Files[IO].writeAll(Path(fileName)))
.compile
.drain
}
}
```
@:choice(scala-2)

```scala mdoc:reset:silent
//> using lib "org.typelevel::toolkit::0.0.2"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
//> using lib "org.typelevel::toolkit::0.0.2"
//> using lib "org.typelevel::toolkit::@VERSION"


import cats.effect._
import io.circe.Decoder
import fs2.Stream
import fs2.io.file._
import org.http4s.ember.client._
import org.http4s._
import org.http4s.implicits._
import org.http4s.circe._

object Main extends IOApp.Simple {
case class Data(value: String)
implicit val json: Decoder[Data] = Decoder.forProduct1("data")(Data.apply)
implicit val enc: EntityDecoder[IO, Data] = jsonOf[IO, Data]

def run = EmberClientBuilder.default[IO].build.use { client =>
val request: Request[IO] =
Request(Method.POST, uri"https://httpbin.org/anything")
.withEntity("file.txt bunchofdata")

client
.expect[Data](request)
.map(_.value.split(" "))
.flatMap { case Array(fileName, content) =>
IO.println(s"Writing data to $fileName") *>
Stream(content)
.through(fs2.text.utf8.encode)
.through(Files[IO].writeAll(Path(fileName)))
.compile
.drain
}
}
}
```

@:@

## Command line version of mkString

In this example, [fs2] is used to read a stream of newline delimited strings from standard input and to reconcatenate them with comma by default, while [decline] is leveraged to parse the command line options.

Compiling this example with [scala-native], adding these directives

```scala mdoc:reset:silent
//> using packaging.output "mkString"
//> using platform "native"
//> using nativeMode "release-fast"
```

will produce a native executable that can be used in a similar way as the Scala's standard library `.mkString`:

```sh
$ echo -e "foo\nbar" | ./mkString --prefix "[" -d "," --suffix "]"
// [foo,bar]
```

@:select(scala-version)

@:choice(scala-3)
```scala mdoc:reset:silent
//> using lib "org.typelevel::toolkit::@VERSION@"

import cats.effect.*
import cats.syntax.all.*
import com.monovore.decline.*
import com.monovore.decline.effect.*
import fs2.*
import fs2.io.*

val prefix = Opts.option[String]("prefix", "").withDefault("")
val delimiter = Opts.option[String]("delimiter", "", "d").withDefault(",")
val suffix = Opts.option[String]("suffix", "The suffix").withDefault("")

val stringStream: Stream[IO, String] = stdinUtf8[IO](1024 * 1024 * 10)
.repartition(s => Chunk.array(s.split("\n", -1)))
.filter(_.nonEmpty)

// inspired by list.mkString
object Main extends CommandIOApp("mkString", "Concatenates strings from stdin"):
def main = (prefix, delimiter, suffix).mapN { (pre, delim, post) =>
val stream = Stream(pre) ++ stringStream.intersperse(delim) ++ Stream(post)
stream.foreach(IO.print).compile.drain.as(ExitCode.Success)
}
```

@:choice(scala-2)

```scala mdoc:reset:silent
//> using lib "org.typelevel::toolkit::0.0.2"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
//> using lib "org.typelevel::toolkit::0.0.2"
//> using lib "org.typelevel::toolkit::@VERSION"


import cats.effect._
import cats.syntax.all._
import com.monovore.decline._
import com.monovore.decline.effect._
import fs2._
import fs2.io._

// inspired by list.mkString
object Main extends CommandIOApp("mkString", "Concatenates strings from stdin") {
val prefix = Opts.option[String]("prefix", "").withDefault("")
val delimiter = Opts.option[String]("delimiter", "", "d").withDefault(",")
val suffix = Opts.option[String]("suffix", "The suffix").withDefault("")

val stringStream: Stream[IO, String] = stdinUtf8[IO](1024 * 1024 * 10)
.repartition(s => Chunk.array(s.split("\n", -1)))
.filter(_.nonEmpty)

def main = (prefix, delimiter, suffix).mapN { (pre, delim, post) =>
val stream = Stream(pre) ++ stringStream.intersperse(delim) ++ Stream(post)
stream.foreach(IO.print).compile.drain.as(ExitCode.Success)
}
}
```

@:@


[fs2]: https://fs2.io/#/
[decline]: https://ben.kirw.in/decline/
[scala-native]: https://scala-native.org/en/stable/
[Scala CLI]: https://scala-cli.virtuslab.org/
[Koroeskohr]: https://github.com/Koroeskohr
66 changes: 66 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# typelevel-toolkit

A toolkit of **great libraries** to start building **Typelevel** apps on JVM, Node.js, and Native!

Our very own flavour of the [Scala Toolkit].

## Overview

Typelevel toolkit is a meta library that currently includes these libraries:

- [Cats] and [Cats Effect]
- [fs2] and [fs2 I/O]
- [fs2 data csv] and its generic module
- [Http4s Ember client]
- [Circe] and http4s integration
- [Decline Effect]
- [Munit Cats Effect]

and it's published for Scala 2.12, 2.13 and 3.2.2.

To use it with [Scala CLI] use this directive:
```scala
//> using lib "org.typelevel::toolkit::@VERSION@"
```

Albeit being created to be used with [Scala CLI], typelevel-toolkit can be imported into your `build.sbt` using:
```scala
libraryDependencies += "org.typelevel" %% "toolkit" % "@VERSION@"
// for native and js
libraryDependencies += "org.typelevel" %%% "toolkit" % "@VERSION@"
```

## Quick Start Example
@:select(scala-version)
@:choice(scala-3)
```scala mdoc:reset:silent
//> using lib "org.typelevel::toolkit::@VERSION@"

import cats.effect.*

object Hello extends IOApp.Simple:
def run = IO.println("Hello toolkit!")
```
@:choice(scala-2)
```scala mdoc:reset:silent
//> using lib "org.typelevel::toolkit::@VERSION@"

import cats.effect._

object Hello extends IOApp.Simple {
def run = IO.println("Hello toolkit!")
}
```
@:@

[Scala CLI]: https://scala-cli.virtuslab.org/
[Scala Toolkit]: https://github.com/VirtusLab/toolkit
[Cats]: https://typelevel.org/cats
[Cats Effect]: https://typelevel.org/cats-effect
[fs2]: https://fs2.io/#/
[fs2 I/O]: https://fs2.io/#/io
[fs2 data csv]: https://fs2-data.gnieh.org/documentation/csv/
[Http4s Ember Client]: https://http4s.org/v0.23/docs/client.html
[Circe]: https://circe.github.io/circe/
[Decline Effect]: https://ben.kirw.in/decline/effect.html
[Munit Cats Effect]: https://github.com/typelevel/munit-cats-effect
Loading