Skip to content

Commit a557c31

Browse files
authored
Merge branch 'main' into update/munit-cats-effect-2.0.0-M4
2 parents 91a1969 + 1794c33 commit a557c31

File tree

4 files changed

+139
-22
lines changed

4 files changed

+139
-22
lines changed

build.sbt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ lazy val toolkit = crossProject(JVMPlatform, JSPlatform, NativePlatform)
2222
libraryDependencies ++= Seq(
2323
"org.typelevel" %%% "cats-core" % "2.10.0",
2424
"org.typelevel" %%% "cats-effect" % "3.5.2",
25-
"co.fs2" %%% "fs2-io" % "3.9.2",
25+
"co.fs2" %%% "fs2-io" % "3.9.3",
2626
"org.gnieh" %%% "fs2-data-csv" % "1.9.1",
2727
"org.gnieh" %%% "fs2-data-csv-generic" % "1.9.1",
2828
"org.http4s" %%% "http4s-ember-client" % "0.23.23",
@@ -55,9 +55,9 @@ lazy val tests = crossProject(JVMPlatform, JSPlatform, NativePlatform)
5555
},
5656
libraryDependencies ++= Seq(
5757
"org.typelevel" %%% "munit-cats-effect" % "2.0.0-M4" % Test,
58-
"co.fs2" %%% "fs2-io" % "3.9.2" % Test,
58+
"co.fs2" %%% "fs2-io" % "3.9.3" % Test,
5959
// https://github.com/VirtusLab/scala-cli/issues/2421
60-
"org.virtuslab.scala-cli" %% "cli" % "1.0.5" cross (CrossVersion.for2_13Use3) excludeAll (
60+
"org.virtuslab.scala-cli" %% "cli" % "1.0.6" cross (CrossVersion.for2_13Use3) excludeAll (
6161
ExclusionRule("com.lihaoyi:geny_2.13"),
6262
ExclusionRule(
6363
"org.scala-lang.modules",

docs/examples.md

Lines changed: 132 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ This example was written by [Koroeskohr] and taken from the [Virtuslab Blog](htt
1010
@:choice(scala-3)
1111

1212
```scala mdoc:silent
13-
//> using lib "org.typelevel::toolkit::@VERSION@"
13+
//> using toolkit typelevel:@VERSION@
1414

1515
import cats.effect.*
1616
import io.circe.Decoder
@@ -47,7 +47,7 @@ object Main extends IOApp.Simple:
4747
@:choice(scala-2)
4848

4949
```scala mdoc:reset:silent
50-
//> using lib "org.typelevel::toolkit::@VERSION@"
50+
//> using toolkit typelevel:@VERSION@
5151

5252
import cats.effect._
5353
import io.circe.Decoder
@@ -108,7 +108,7 @@ $ echo -e "foo\nbar" | ./mkString --prefix "[" -d "," --suffix "]"
108108

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

113113
import cats.effect.*
114114
import cats.syntax.all.*
@@ -136,7 +136,7 @@ object Main extends CommandIOApp("mkString", "Concatenates strings from stdin"):
136136
@:choice(scala-2)
137137

138138
```scala mdoc:reset:silent
139-
//> using lib "org.typelevel::toolkit::@VERSION@"
139+
//> using toolkit typelevel:@VERSION@
140140

141141
import cats.effect._
142142
import cats.syntax.all._
@@ -166,7 +166,7 @@ object Main extends CommandIOApp("mkString", "Concatenates strings from stdin")
166166

167167
## Parsing and transforming a CSV file
168168

169-
Here, [fs2-data-csv] is used to read and parse a comma separated file.
169+
Here, [fs2-data-csv] is used to read and parse a comma separated file.
170170
Manual encoders and decoders are defined for our `Passenger`s to show you how to do everything from scratch.
171171

172172
Let's start with a CSV file that has records of fictious passengers registered for a flight:
@@ -183,7 +183,7 @@ id,First Name,Age,flight number,destination
183183

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

188188
import cats.effect.*
189189
import fs2.text
@@ -233,7 +233,7 @@ val input = Files[IO]
233233

234234
object CSVPrinter extends IOApp.Simple:
235235

236-
/** First we'll do some logging for each row,
236+
/** First we'll do some logging for each row,
237237
* and then calculate and print the mean age */
238238
val run =
239239
input
@@ -254,7 +254,7 @@ object CSVPrinter extends IOApp.Simple:
254254

255255
@:choice(scala-2)
256256
```scala mdoc:reset:silent
257-
//> using lib "org.typelevel::toolkit::@VERSION@"
257+
//> using toolkit typelevel:@VERSION@
258258

259259
import cats.effect._
260260
import fs2.text
@@ -310,7 +310,7 @@ object CSVPrinter extends IOApp.Simple {
310310
.through(decodeUsingHeaders[Passenger]())
311311

312312

313-
/** First we'll do some logging for each row,
313+
/** First we'll do some logging for each row,
314314
* and then calculate and print the mean age */
315315
val run =
316316
input
@@ -358,7 +358,7 @@ perft 6 227689589
358358
@:choice(scala-3)
359359

360360
```scala mdoc:reset:silent
361-
//> using lib "org.typelevel::toolkit::@VERSION@"
361+
//> using toolkit typelevel:@VERSION@
362362

363363
import cats.effect.{IO, IOApp}
364364
import fs2.{Stream, text}
@@ -388,7 +388,7 @@ object PerftConverter extends IOApp.Simple:
388388

389389
@:choice(scala-2)
390390
```scala mdoc:reset:silent
391-
//> using lib "org.typelevel::toolkit::@VERSION@"
391+
//> using toolkit typelevel:@VERSION@
392392

393393
import cats.effect.{IO, IOApp}
394394
import fs2.{Stream, text}
@@ -420,6 +420,127 @@ object PerftConverter extends IOApp.Simple {
420420
```
421421
@:@
422422

423+
## Writing data to a CSV file
424+
425+
If you want to save a list of a case class into a CSV file this utility may aid you:
426+
427+
@:select(scala-version)
428+
429+
@:choice(scala-3)
430+
```scala
431+
// Define your case class and derive an encoder for it
432+
case class YourCaseClass(n: String, i: Int)
433+
given CsvRowEncoder[YourCaseClass, String] = deriveCsvRowEncoder
434+
435+
// Writes a case class as a csv given a path.
436+
def writeCaseClassToCsv[A](
437+
path: Path
438+
)(using CsvRowEncoder[A, String]): Pipe[IO, A, Nothing] =
439+
_.through(encodeUsingFirstHeaders(fullRows = true))
440+
.through(fs2.text.utf8.encode)
441+
.through(Files[IO].writeAll(path))
442+
```
443+
444+
@:choice(scala-2)
445+
```scala
446+
case class YourCaseClass(n: String, i: Int)
447+
implicit val csvRowEncoder: CsvRowEncoder[YourCaseClass, String] = deriveCsvRowEncoder
448+
449+
object Helpers {
450+
// Writes a case class as a csv given a path.
451+
def writeCaseClassToCsv[A](
452+
path: Path
453+
)(implicit encoder: CsvRowEncoder[A, String]): Pipe[IO, A, Nothing] =
454+
_.through(encodeUsingFirstHeaders(fullRows = true))
455+
.through(fs2.text.utf8.encode)
456+
.through(Files[IO].writeAll(path))
457+
}
458+
```
459+
@:@
460+
461+
As an example, let's imagine we have a `Book` class we would like to write to a `.csv` file.
462+
463+
@:select(scala-version)
464+
465+
@:choice(scala-3)
466+
```scala mdoc:reset:silent
467+
//> using toolkit typelevel:@VERSION@
468+
469+
import fs2.data.csv.*
470+
import fs2.data.csv.generic.semiauto.*
471+
import fs2.io.file.{Files, Path}
472+
import cats.effect.{IO, IOApp}
473+
import fs2.{Pipe, Stream}
474+
475+
def writeCaseClassToCsv[A](
476+
path: Path
477+
)(using CsvRowEncoder[A, String]): Pipe[IO, A, Nothing] =
478+
_.through(encodeUsingFirstHeaders(fullRows = true))
479+
.through(fs2.text.utf8.encode)
480+
.through(Files[IO].writeAll(path))
481+
482+
483+
object WriteBooksToCsv extends IOApp.Simple:
484+
case class Book(id: Long, name: String, isbn: String)
485+
given CsvRowEncoder[Book, String] = deriveCsvRowEncoder
486+
487+
val input = Seq(
488+
Book(1, "Programming in Scala", "9780997148008"),
489+
Book(2, "Hands-on Scala Programming", "9798387677205"),
490+
Book(3, "Functional Programming in Scala", "9781617299582")
491+
)
492+
493+
def run: IO[Unit] =
494+
Stream
495+
.emits(input)
496+
.through(writeCaseClassToCsv(Path("books.csv")))
497+
.compile
498+
.drain *> IO.println("Finished writing books to books.csv.")
499+
```
500+
501+
@:choice(scala-2)
502+
503+
```scala mdoc:reset:silent
504+
//> using toolkit typelevel:@VERSION@
505+
506+
import fs2.data.csv._
507+
import fs2.data.csv.generic.semiauto._
508+
import fs2.io.file.{Files, Path}
509+
import cats.effect.{IO, IOApp}
510+
import fs2.{Pipe, Stream}
511+
512+
object Helpers {
513+
def writeCaseClassToCsv[A](
514+
path: Path
515+
)(implicit encoder: CsvRowEncoder[A, String]): Pipe[IO, A, Nothing] =
516+
_.through(encodeUsingFirstHeaders(fullRows = true))
517+
.through(fs2.text.utf8.encode)
518+
.through(Files[IO].writeAll(path))
519+
}
520+
521+
object WriteBooksToCsv extends IOApp.Simple {
522+
case class Book(id: Long, name: String, isbn: String)
523+
implicit val csvRowEncoder: CsvRowEncoder[Book, String] = deriveCsvRowEncoder
524+
525+
val input = Seq(
526+
Book(1, "Programming in Scala", "9780997148008"),
527+
Book(2, "Hands-on Scala Programming", "9798387677205"),
528+
Book(3, "Functional Programming in Scala", "9781617299582")
529+
)
530+
531+
def run: IO[Unit] =
532+
Stream
533+
.emits(input)
534+
.through(Helpers.writeCaseClassToCsv(Path("books.csv")))
535+
.compile
536+
.drain *> IO.println("Finished writing books to books.csv.")
537+
}
538+
```
539+
540+
@:@
541+
542+
543+
423544
[fs2]: https://fs2.io/#/
424545
[fs2-data-csv]: https://fs2-data.gnieh.org/documentation/csv/
425546
[decline]: https://ben.kirw.in/decline/

project/plugins.sbt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
val sbtTlVersion = "0.6.1"
1+
val sbtTlVersion = "0.6.2"
22
addSbtPlugin("org.typelevel" % "sbt-typelevel" % sbtTlVersion)
33
addSbtPlugin("org.typelevel" % "sbt-typelevel-mergify" % sbtTlVersion)
44
addSbtPlugin("org.typelevel" % "sbt-typelevel-site" % sbtTlVersion)
55
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.14.0")
6-
addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.15")
6+
addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.16")
77
addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.11.0")

tests/shared/src/test/scala/org/typelevel/toolkit/ScalaCliProcess.scala

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,9 @@ object ScalaCliProcess {
7575
def command(args: List[String]): IO[Unit] = scalaCli(args)
7676

7777
def run(body: String): IO[Unit] =
78-
writeToFile(body)(false).use(f =>
79-
scalaCli("run" :: "--native-version" :: "0.4.15" :: f :: Nil)
80-
)
78+
writeToFile(body)(false).use(f => scalaCli("run" :: f :: Nil))
8179

8280
def test(body: String): IO[Unit] =
83-
writeToFile(body)(true).use(f =>
84-
scalaCli("test" :: "--native-version" :: "0.4.15" :: f :: Nil)
85-
)
81+
writeToFile(body)(true).use(f => scalaCli("test" :: f :: Nil))
8682

8783
}

0 commit comments

Comments
 (0)