From 11561c4ae5d99e63f6a099a467b76fc00f8a5b01 Mon Sep 17 00:00:00 2001 From: Michel Davit Date: Tue, 9 Jan 2024 15:30:17 +0100 Subject: [PATCH] Paradox site (#879) * Paradox magnolify site * Enable site in CI * Fix mapping table * Remove extra setup step --- .github/workflows/ci.yml | 42 +++++- README.md | 9 +- build.sbt | 131 ++++++++++++++++-- docs/derivation.md | 71 ---------- project/plugins.sbt | 8 +- site/src/main/paradox/_template/scaladoc.st | 10 ++ {docs => site/src/main/paradox}/avro.md | 37 +++-- {docs => site/src/main/paradox}/bigquery.md | 24 ++-- {docs => site/src/main/paradox}/bigtable.md | 13 +- site/src/main/paradox/cats.md | 47 +++++++ {docs => site/src/main/paradox}/datastore.md | 29 ++-- {docs => site/src/main/paradox}/enums.md | 9 +- site/src/main/paradox/guava.md | 29 ++++ site/src/main/paradox/images/favicon.ico | Bin 0 -> 5430 bytes site/src/main/paradox/images/logo.png | Bin 0 -> 16260 bytes site/src/main/paradox/index.md | 52 +++++++ {docs => site/src/main/paradox}/mapping.md | 65 +++++---- site/src/main/paradox/neo4j.md | 3 + {docs => site/src/main/paradox}/parquet.md | 33 +++-- {docs => site/src/main/paradox}/protobuf.md | 21 ++- {docs => site/src/main/paradox}/refined.md | 23 +-- site/src/main/paradox/scalacheck.md | 30 ++++ site/src/main/paradox/scaladoc.md | 5 + {docs => site/src/main/paradox}/tensorflow.md | 5 +- 24 files changed, 494 insertions(+), 202 deletions(-) delete mode 100644 docs/derivation.md create mode 100644 site/src/main/paradox/_template/scaladoc.st rename {docs => site/src/main/paradox}/avro.md (76%) rename {docs => site/src/main/paradox}/bigquery.md (79%) rename {docs => site/src/main/paradox}/bigtable.md (82%) create mode 100644 site/src/main/paradox/cats.md rename {docs => site/src/main/paradox}/datastore.md (74%) rename {docs => site/src/main/paradox}/enums.md (85%) create mode 100644 site/src/main/paradox/guava.md create mode 100644 site/src/main/paradox/images/favicon.ico create mode 100755 site/src/main/paradox/images/logo.png create mode 100644 site/src/main/paradox/index.md rename {docs => site/src/main/paradox}/mapping.md (54%) create mode 100644 site/src/main/paradox/neo4j.md rename {docs => site/src/main/paradox}/parquet.md (73%) rename {docs => site/src/main/paradox}/protobuf.md (73%) rename {docs => site/src/main/paradox}/refined.md (66%) create mode 100644 site/src/main/paradox/scalacheck.md create mode 100644 site/src/main/paradox/scaladoc.md rename {docs => site/src/main/paradox}/tensorflow.md (92%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d3d3f4975..766ced322 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -95,11 +95,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 refined/target shared/target tensorflow/target parquet/target tools/target protobuf/target jmh/target bigquery/target avro/target scalacheck/target datastore/target neo4j/target cats/target bigtable/target guava/target project/target + run: mkdir -p refined/target shared/target unidocs/target tensorflow/target parquet/target site/target tools/target protobuf/target jmh/target bigquery/target avro/target scalacheck/target datastore/target neo4j/target cats/target bigtable/target guava/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 refined/target shared/target tensorflow/target parquet/target tools/target protobuf/target jmh/target bigquery/target avro/target scalacheck/target datastore/target neo4j/target cats/target bigtable/target guava/target project/target + run: tar cf targets.tar refined/target shared/target unidocs/target tensorflow/target parquet/target site/target tools/target protobuf/target jmh/target bigquery/target avro/target scalacheck/target datastore/target neo4j/target cats/target bigtable/target guava/target project/target - name: Upload target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') @@ -338,3 +338,41 @@ jobs: env: JAVA_OPTS: '-Davro.version=1.8.2' run: sbt '++ ${{ matrix.scala }}' avro/test + + site: + name: Generate Site + strategy: + matrix: + os: [ubuntu-latest] + scala: [2.13] + java: [corretto@17] + runs-on: ${{ matrix.os }} + steps: + - name: Checkout current branch (full) + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Java (corretto@17) + id: setup-java-corretto-17 + if: matrix.java == 'corretto@17' + uses: actions/setup-java@v4 + with: + distribution: corretto + java-version: 17 + cache: sbt + + - name: sbt update + if: matrix.java == 'corretto@17' && steps.setup-java-corretto-17.outputs.cache-hit == 'false' + run: sbt +update + + - name: Generate site + run: sbt '++ ${{ matrix.scala }}' site/makeSite + + - name: Publish site + if: startsWith(github.ref, 'refs/tags/v') + env: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: site/target + keep_files: true + uses: peaceiris/actions-gh-pages@v3.9.3 diff --git a/README.md b/README.md index 90685e986..be5bbd90e 100644 --- a/README.md +++ b/README.md @@ -36,14 +36,7 @@ This library includes the following modules. # Usage -See [derivation.md](https://github.com/spotify/magnolify/tree/master/docs/derivation.md) for type class derivation for Cats, Scalacheck, and Guava. - -See [avro.md](https://github.com/spotify/magnolify/tree/master/docs/avro.md) -[bigquery.md](https://github.com/spotify/magnolify/tree/master/docs/bigquery.md) -[bigtable.md](https://github.com/spotify/magnolify/tree/master/docs/bigtable.md) -[datastore.md](https://github.com/spotify/magnolify/tree/master/docs/datastore.md) -[protobuf.md](https://github.com/spotify/magnolify/tree/master/docs/protobuf.md) -[tensorflow.md](https://github.com/spotify/magnolify/tree/master/docs/tensorflow.md) for data type conversions for these libraries. See [parquet.md](https://github.com/spotify/magnolify/tree/master/docs/parquet.md) for Parquet IO support. Also see [enums.md](https://github.com/spotify/magnolify/tree/master/docs/enums.md) for enum types and [refined.md](https://github.com/spotify/magnolify/tree/master/docs/derivation.md) for refinement types support. Finally see [mapping.md](https://github.com/spotify/magnolify/blob/master/docs/mapping.md) for a mapping table of Scala types supported by conversion and IO modules. +See [micro-site](https://spotify.github.io/magnolify/) for documentation. # How to Release diff --git a/build.sbt b/build.sbt index 7800d8fc3..9d169e4f6 100644 --- a/build.sbt +++ b/build.sbt @@ -15,6 +15,7 @@ */ import sbt._ import sbtprotoc.ProtocPlugin.ProtobufConfig +import com.github.sbt.git.SbtGit.GitKeys.gitRemoteRepo import com.typesafe.tools.mima.core._ val magnoliaScala2Version = "1.1.6" @@ -105,17 +106,21 @@ val scala3 = "3.3.1" val scala213 = "2.13.12" val scala212 = "2.12.18" val scalaDefault = scala213 +val scala3Projects = List( + "shared", + "test" +) // github actions val java17 = JavaSpec.corretto("17") val java11 = JavaSpec.corretto("11") val javaDefault = java17 -val scala3Cond = "matrix.scala == '3'" -val scala3Projects = List( - "shared", - "test" -) +val condIsScala3 = "matrix.scala == '3'" +val condNotScala3 = s"!($condIsScala3)" +val condIsMain = "github.ref == 'refs/heads/main'" +val condIsTag = "startsWith(github.ref, 'refs/tags/v')" + ThisBuild / scalaVersion := scalaDefault ThisBuild / crossScalaVersions := Seq(scala3, scala213, scala212) ThisBuild / githubWorkflowTargetBranches := Seq("main") @@ -130,11 +135,11 @@ ThisBuild / githubWorkflowBuild ~= { steps: Seq[WorkflowStep] => steps.flatMap { case s if s.name.contains("Test") => Seq( - s.withCond(Some(s"!($scala3Cond)")), + s.withCond(Some(condNotScala3)), WorkflowStep.Sbt( scala3Projects.map(p => s"$p/test"), name = Some("Test"), - cond = Some(scala3Cond) + cond = Some(condIsScala3) ) ) case s => @@ -142,7 +147,7 @@ ThisBuild / githubWorkflowBuild ~= { steps: Seq[WorkflowStep] => s.name.contains("Check binary compatibility") || s.name.contains("Generate API documentation") ) { - Seq(s.withCond(Some(s"!($scala3Cond)"))) + Seq(s.withCond(Some(condNotScala3))) } else { Seq(s) } @@ -181,6 +186,35 @@ ThisBuild / githubWorkflowAddedJobs ++= Seq( ), scalas = List(CrossVersion.binaryScalaVersion(scalaDefault)), javas = List(javaDefault) + ), + WorkflowJob( + "site", + "Generate Site", + WorkflowStep.CheckoutFull :: + WorkflowStep.SetupJava(List(javaDefault)) ::: + List( + WorkflowStep.Sbt( + List("site/makeSite"), + name = Some("Generate site") + ), + WorkflowStep.Use( + UseRef.Public("peaceiris", "actions-gh-pages", "v3.9.3"), + env = Map( + "github_token" -> "${{ secrets.GITHUB_TOKEN }}", + "publish_dir" -> { + val path = (ThisBuild / baseDirectory).value.toPath.toAbsolutePath + .relativize((site / target).value.toPath) + // os-independent path rendering ... + (0 until path.getNameCount).map(path.getName).mkString("/") + }, + "keep_files" -> "true" + ), + name = Some("Publish site"), + cond = Some(condIsTag) + ) + ), + scalas = List(CrossVersion.binaryScalaVersion(scalaDefault)), + javas = List(javaDefault) ) ) @@ -624,3 +658,84 @@ lazy val jmh: Project = project "org.tensorflow" % "tensorflow-core-api" % tensorflowVersion % Test ) ) + +// ======================================================================= +// Site settings +// ======================================================================= +lazy val site = project + .in(file("site")) + .enablePlugins( + ParadoxSitePlugin, + ParadoxMaterialThemePlugin, + GhpagesPlugin, + SiteScaladocPlugin, + MdocPlugin + ) + .dependsOn( + avro % "compile->compile,provided", + bigquery % "compile->compile,provided", + bigtable % "compile->compile,provided", + cats % "compile->compile,provided", + datastore % "compile->compile,provided", + guava % "compile->compile,provided", + neo4j % "compile->compile,provided", + parquet % "compile->compile,provided", + protobuf % "compile->compile,provided", + refined % "compile->compile,provided", + shared, + scalacheck % "compile->compile,provided", + tensorflow % "compile->compile,provided", + unidocs + ) + .settings(commonSettings) + .settings( + description := "Magnolify - Documentation", + fork := false, + publish / skip := true, + autoAPIMappings := true, + gitRemoteRepo := "git@github.com:spotify/magnolify.git", + // mdoc + // pre-compile md using mdoc + mdocIn := (paradox / sourceDirectory).value, + mdocExtraArguments ++= Seq("--no-link-hygiene"), + // paradox + Compile / paradox / sourceManaged := mdocOut.value, + paradoxProperties ++= Map( + "github.base_url" -> "https://github.com/spotify/magnolify" + ), + Compile / paradoxMaterialTheme := ParadoxMaterialTheme() + .withFavicon("images/favicon.ico") + .withColor("white", "indigo") + .withLogo("images/logo.png") + .withCopyright("Copyright (C) 2024 Spotify AB") + .withRepository(uri("https://github.com/spotify/magnolify")) + .withSocial(uri("https://github.com/spotify"), uri("https://twitter.com/spotifyeng")), + // sbt-site + addMappingsToSiteDir( + unidocs / ScalaUnidoc / packageDoc / mappings, + unidocs / ScalaUnidoc / siteSubdirName + ), + makeSite := makeSite.dependsOn(mdoc.toTask("")).value + ) + +lazy val unidocs = project + .in(file("unidocs")) + .enablePlugins(TypelevelUnidocPlugin) + .settings(commonSettings) + .settings( + moduleName := "magnolify-docs", + crossScalaVersions := Seq(scalaDefault), + scalaVersion := scalaDefault, + // unidoc + ScalaUnidoc / siteSubdirName := "api", + ScalaUnidoc / scalacOptions := Seq.empty, + ScalaUnidoc / unidoc / unidocProjectFilter := inAnyProject -- inProjects(test, jmh), + ScalaUnidoc / unidoc / unidocAllClasspaths ~= { cp => + // somehow protobuf 2 is in classpath and fails doc + cp.map(_.filterNot(_.data.getName.endsWith("protobuf-java-2.5.0.jar"))) + }, + ScalaUnidoc / unidoc / unidocAllSources ~= { sources => + // filter out doc from generated proto TFMD sources + sources.map(_.filterNot(_.getPath.contains("compiled_proto"))) + } + ) diff --git a/docs/derivation.md b/docs/derivation.md deleted file mode 100644 index ec03b0764..000000000 --- a/docs/derivation.md +++ /dev/null @@ -1,71 +0,0 @@ -Type Class Derivation -===================== - -Type class derivation for Cats, ScalaCheck, and Guava can be performed both automatically and semi-automatically. - -Automatic derivation are provided as implicits through `import magnolify.$module.auto._`. It works with context bounds i.e. `def plus[T: Semigroup](x: T, y: T)` and implicit parameters i.e. `def f[T](x: T, y: T)(implicit sg: Semigroup[T])`. The following examples summon them via `implicitly`. - -```scala -case class Inner(int: Int, str: String) -case class Outer(inner: Inner) - -// Cats Semigroup -import magnolify.cats.auto._ -import cats._ - -val sg: Semigroup[Outer] = implicitly[Semigroup[Outer]] -sg.join(Outer(Inner(1, "hello, ")), Outer(Inner(100, "world!"))) -// = Outer(Inner(101,hello, world!)) - -// ScalaCheck Arbitrary -import magnolify.scalacheck.auto._ -import org.scalacheck._ // implicit instances for Arbitrary[Int], etc. - -val arb: Arbitrary[Outer] = implicitly[Arbitrary[Outer]] -arb.arbitrary.sample -// = Some(Outer(Inter(12345, abcde))) - -// Guava Funnel -import magnolify.guava.auto._ // includes implicit instances for Funnel[Int], etc. -import com.google.common.hash._ -val fnl: Funnel[Outer] = implicitly[Funnel[Outer]] -val bf: BloomFilter[Outer] = BloomFilter.create[Outer](fnl, 1000) -``` - -Some [Algebird](https://github.com/twitter/algebird) instances extend those from Cats and can be derived as well. - -```scala -import magnolify.cats.auto._ -import com.twitter.{algebird => a} - -case class Record(i: Int, o: Option[Int], l: List[Int], s: Set[Int], m: Map[String, Int]) - -// implicit conversion from cats.{Semigroup,Monoid} to com.twitter.algebird.{Semigroup,Monoid} -val sg: a.Semigroup[Record] = implicitly[a.Semigroup[Record]] -val mon: a.Monoid[Record] = implicitly[a.Monoid[Record]] -``` - -Semi-automatic derivation needs to be called explicitly. - -```scala -import magnolify.cats.semiauto._ -import cats._ - -val eq: Eq[Outer] = EqDerivation[Outer] -val hash: Hash[Outer] = HashDerivation[Outer] -val sg: Semigroup[Outer] = SemigroupDerivation[Outer] -val mon: Monoid[Outer] = MonoidDerivation[Outer] - -// this fails due to missing `Group[String]` instance -val group: Group[Outer] = GroupDerivation[Outer] - -import magnolify.scalacheck.semiauto._ -import org.scalacheck._ - -val arb: Arbitrary[Outer] = ArbitraryDerivation[Outer] -val cogen: Cogen[Outer] = CogenDerivation[Outer] - -import magnolify.guava.semiauto._ - -val fnl: Funnel[Outer] = FunnelDerivation[Outer] -``` diff --git a/project/plugins.sbt b/project/plugins.sbt index 5ebc512eb..d3e178e51 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,4 +1,10 @@ -addSbtPlugin("org.typelevel" % "sbt-typelevel" % "0.6.3") +addSbtPlugin("com.github.sbt" % "sbt-ghpages" % "0.8.0") +addSbtPlugin("com.github.sbt" % "sbt-site-paradox" % "1.5.0") +addSbtPlugin("com.github.sbt" % "sbt-unidoc" % "0.5.0") +addSbtPlugin("com.lightbend.paradox" % "sbt-paradox" % "0.10.5") addSbtPlugin("com.thesamet" % "sbt-protoc" % "1.0.6") +addSbtPlugin("io.github.jonas" % "sbt-paradox-material-theme" % "0.6.0") +addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.5.1") addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.0.9") +addSbtPlugin("org.typelevel" % "sbt-typelevel" % "0.6.3") addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.6") diff --git a/site/src/main/paradox/_template/scaladoc.st b/site/src/main/paradox/_template/scaladoc.st new file mode 100644 index 000000000..1a07036a1 --- /dev/null +++ b/site/src/main/paradox/_template/scaladoc.st @@ -0,0 +1,10 @@ + + + + + + + +

The page has moved to: this page

+ + diff --git a/docs/avro.md b/site/src/main/paradox/avro.md similarity index 76% rename from docs/avro.md rename to site/src/main/paradox/avro.md index 93729c66f..503bd20b3 100644 --- a/docs/avro.md +++ b/site/src/main/paradox/avro.md @@ -1,23 +1,23 @@ -AvroType -======== +# Avro `AvroType[T]` provides conversion between Scala type `T` and Avro `GenericRecord`. Custom support for type `T` can be added with an implicit instance of `AvroField[T]`. -```scala +```scala mdoc:compile-only import java.net.URI + case class CountryCode(code: String) case class Inner(long: Long, str: String, uri: URI, cc: CountryCode) case class Outer(inner: Inner) -val record = Outer(Inner(1L, "hello", URI.create("https://www.spotify.com"), "US")) +val record = Outer(Inner(1L, "hello", URI.create("https://www.spotify.com"), CountryCode("US"))) import magnolify.avro._ import org.apache.avro.generic.GenericRecord // Encode custom type URI as String -implicit val uriField = AvroField.from[String](URI.create)(_.toString) +implicit val uriField: AvroField[URI] = AvroField.from[String](URI.create)(_.toString) // Encode country code as fixed type -implicit val afCountryCode = +implicit val afCountryCode: AvroField[CountryCode] = AvroField.fixed[CountryCode](2)(bs => CountryCode(new String(bs)))(cc => cc.code.getBytes) val avroType = AvroType[Outer] @@ -25,14 +25,14 @@ val genericRecord: GenericRecord = avroType.to(record) val copy: Outer = avroType.from(genericRecord) // Avro Schema -avroType.schema +val schema = avroType.schema ``` -Enum-like types map to Avro enums. See [enums.md](https://github.com/spotify/magnolify/tree/master/docs/enums.md) for more details. Additional `AvroField[T]` instances for `Byte`, `Char`, `Short`, and `UnsafeEnum[T]` are available from `import magnolify.avro.unsafe._`. These conversions are unsafe due to potential overflow. +Enum-like types map to Avro enums. See @ref:[EnumType](enums.md) for more details. Additional `AvroField[T]` instances for `Byte`, `Char`, `Short`, and `UnsafeEnum[T]` are available from `import magnolify.avro.unsafe._`. These conversions are unsafe due to potential overflow. Achieving backward compatibility when adding new fields to the case class: new fields must have a default parameter value in order to generate backward compatible Avro schema `avroType.schema`. -```scala +```scala mdoc:compile-only case class Record(oldField: String, newField: String = "") // OR case class Record2(oldField: String, newField: Option[String] = None) @@ -40,20 +40,24 @@ case class Record2(oldField: String, newField: Option[String] = None) To populate Avro type and field `doc`s, annotate the case class and its fields with the `@doc` annotation. -```scala +```scala mdoc:compile-only +import magnolify.avro._ + @doc("My record") case class Record(@doc("int field") i: Int, @doc("string field") s: String) @doc("My enum") object Color extends Enumeration { type Type = Value - value Red, Gree, Blue = Value + val Red, Green, Blue = Value } ``` The `@doc` annotation can also be extended to support custom format. -```scala +```scala mdoc:compile-only +import magnolify.avro._ + class myDoc(doc: String, version: Int) extends doc(s"doc: $doc, version: $version") @myDoc("My record", 2) @@ -62,7 +66,8 @@ case class Record(@myDoc("int field", 1) i: Int, @myDoc("string field", 2) s: St To use a different field case format in target records, add an optional `CaseMapper` argument to `AvroType`. The following example maps `firstName` & `lastName` to `first_name` & `last_name`. -```scala +```scala mdoc:compile-only +import magnolify.avro._ import magnolify.shared.CaseMapper import com.google.common.base.CaseFormat @@ -75,8 +80,10 @@ avroType.to(LowerCamel("John", "Doe")) Avro `decimal` and `uuid` logical types map to `BigDecimal` and `java.util.UUID`. Additionally `decimal` requires `precision` and optional `scale` parameter. -```scala -implicit val afBigDecimal = AvroField.bigDecimal(20, 4) +```scala mdoc:compile-only +import magnolify.avro._ + +implicit val afBigDecimal: AvroField[BigDecimal] = AvroField.bigDecimal(20, 4) ``` Among the date/time types, `date` maps to `java.time.LocalDate`. The other types, `timestamp`, `time` and `local-timestamp`, map to `Instant`, `LocalTime` and `LocalDateTime` in either micro or milliseconds precision with `import magnolify.avro.logical.micros._` or `import magnolify.avro.logical.millis._`. diff --git a/docs/bigquery.md b/site/src/main/paradox/bigquery.md similarity index 79% rename from docs/bigquery.md rename to site/src/main/paradox/bigquery.md index 3a1491e9c..bbff8b8c8 100644 --- a/docs/bigquery.md +++ b/site/src/main/paradox/bigquery.md @@ -1,9 +1,8 @@ -TableRowType -============ +# BigQuery `TableRowType[T]` provides conversion between Scala type `T` and BigQuery `TableRow`. Custom support for type `T` can be added with an implicit instance of `TableRowField[T]`. -```scala +```scala mdoc:compile-only import java.net.URI case class Inner(long: Long, str: String, uri: URI) case class Outer(inner: Inner) @@ -13,30 +12,34 @@ import magnolify.bigquery._ import com.google.api.services.bigquery.model.TableRow // Encode custom type URI as String -implicit val uriField = TableRowField.from[String](URI.create)(_.toString) +implicit val uriField: TableRowField[URI] = TableRowField.from[String](URI.create)(_.toString) val tableRowType = TableRowType[Outer] val tableRow: TableRow = tableRowType.to(record) val copy: Outer = tableRowType.from(tableRow) // BigQuery TableSchema -tableRowType.schema +val schema = tableRowType.schema ``` -Additional `TableRowField[T]` instances for `Byte`, `Char`, `Short`, `Int`, `Float`, and enum-like types are available from `import magnolify.bigquery.unsafe._`. These conversions are unsafe due to potential overflow or encoding errors. See [enums.md](https://github.com/spotify/magnolify/tree/master/docs/enums.md) for more details. +Additional `TableRowField[T]` instances for `Byte`, `Char`, `Short`, `Int`, `Float`, and enum-like types are available from `import magnolify.bigquery.unsafe._`. These conversions are unsafe due to potential overflow or encoding errors. See @ref:[EnumType](enums.md) for more details. To populate BigQuery table and field `description`s, annotate the case class and its fields with the `@description` annotation. -```scala +```scala mdoc:compile-only +import magnolify.bigquery._ + @description("My record") case class Record(@description("int field") i: Int, @description("string field") s: String) ``` The `@description` annotation can also be extended to support custom format. -```scala +```scala mdoc:compile-only +import magnolify.bigquery._ + class myDesc(description: String, version: Int) - extends description(s"description: description, version: $version") + extends description(s"description: $description, version: $version") @myDesc("My record", 2) case class Record(@myDesc("int field", 1) i: Int, @myDesc("string field", 2) s: String) @@ -44,7 +47,8 @@ case class Record(@myDesc("int field", 1) i: Int, @myDesc("string field", 2) s: To use a different field case format in target records, add an optional `CaseMapper` argument to `TableRowType`. The following example maps `firstName` & `lastName` to `first_name` & `last_name`. -```scala +```scala mdoc:compile-only +import magnolify.bigquery._ import magnolify.shared.CaseMapper import com.google.common.base.CaseFormat diff --git a/docs/bigtable.md b/site/src/main/paradox/bigtable.md similarity index 82% rename from docs/bigtable.md rename to site/src/main/paradox/bigtable.md index 6c5dad5ac..5dd301620 100644 --- a/docs/bigtable.md +++ b/site/src/main/paradox/bigtable.md @@ -1,9 +1,8 @@ -BigtableType -============ +# Bigtable `BigtableType[T]` provides conversion between Scala type `T` and Bigtable `Row`/`Seq[Mutation]` for read/write. Custom support for type `T` can be added with an implicit instance of `BigtableField[T]`. -```scala +```scala mdoc:compile-only import java.net.URI case class Inner(long: Long, str: String, uri: URI) case class Outer(inner: Inner) @@ -11,9 +10,10 @@ val record = Outer(Inner(1L, "hello", URI.create("https://www.spotify.com"))) import magnolify.bigtable._ import com.google.bigtable.v2.{Mutation, Row} +import com.google.protobuf.ByteString // Encode custom type URI as String -implicit val uriField = BigtableField.from[String](URI.create)(_.toString) +implicit val uriField: BigtableField[URI] = BigtableField.from[String](URI.create)(_.toString) val bigtableType = BigtableType[Outer] val mutations: Seq[Mutation] = bigtableType(record, "ColumnFamily") @@ -21,11 +21,12 @@ val row: Row = BigtableType.mutationsToRow(ByteString.copyFromUtf8("RowKey"), mu val copy: Outer = bigtableType(row, "ColumnFamily") ``` -`BigtableType` encodes each field in a separate column qualifier of the same name. It encodes nested fields by joining field names as `field_a.field_b.field_c`. Repeated types are not supported. Enum-like types map to strings. See [enums.md](https://github.com/spotify/magnolify/tree/master/docs/enums.md) for more details. +`BigtableType` encodes each field in a separate column qualifier of the same name. It encodes nested fields by joining field names as `field_a.field_b.field_c`. Repeated types are not supported. Enum-like types map to strings. See @ref:[EnumType](enums.md) for more details. To use a different field case format in target records, add an optional `CaseMapper` argument to `BigtableType`. The following example maps `firstName` & `lastName` to `first_name` & `last_name`. -```scala +```scala mdoc:compile-only +import magnolify.bigtable._ import magnolify.shared.CaseMapper import com.google.common.base.CaseFormat diff --git a/site/src/main/paradox/cats.md b/site/src/main/paradox/cats.md new file mode 100644 index 000000000..a4f49cc5c --- /dev/null +++ b/site/src/main/paradox/cats.md @@ -0,0 +1,47 @@ +# Cats + +Type class derivation for Cats can be performed both automatically and semi-automatically. + +Automatic derivation are provided as implicits through `import magnolify.cats.auto._`. + +```scala mdoc:compile-only +case class Inner(int: Int, str: String) +case class Outer(inner: Inner) + +// Cats Semigroup +import magnolify.cats.auto._ +import cats._ + +val sg: Semigroup[Outer] = implicitly[Semigroup[Outer]] +sg.combine(Outer(Inner(1, "hello, ")), Outer(Inner(100, "world!"))) // = Outer(Inner(101,hello, world!)) +``` + +Some [Algebird](https://github.com/twitter/algebird) instances extend those from Cats and can be derived as well. + +```scala mdoc:compile-only +import magnolify.cats.auto._ +import com.twitter.{algebird => a} + +case class Record(i: Int, o: Option[Int], l: List[Int], s: Set[Int], m: Map[String, Int]) + +// implicit conversion from cats.{Semigroup,Monoid} to com.twitter.algebird.{Semigroup,Monoid} +val sg: a.Semigroup[Record] = implicitly[a.Semigroup[Record]] +val mon: a.Monoid[Record] = implicitly[a.Monoid[Record]] +``` +Semi-automatic derivation needs to be called explicitly. + +```scala mdoc:fail +import magnolify.cats.semiauto._ +import cats._ + +case class Inner(int: Int, str: String) +case class Outer(inner: Inner) + +val eq: Eq[Outer] = EqDerivation[Outer] +val hash: Hash[Outer] = HashDerivation[Outer] +val sg: Semigroup[Outer] = SemigroupDerivation[Outer] +val mon: Monoid[Outer] = MonoidDerivation[Outer] + +// this fails due to missing `Group[String]` instance +val group: Group[Outer] = GroupDerivation[Outer] +``` \ No newline at end of file diff --git a/docs/datastore.md b/site/src/main/paradox/datastore.md similarity index 74% rename from docs/datastore.md rename to site/src/main/paradox/datastore.md index 7fdb8907e..f130fbd63 100644 --- a/docs/datastore.md +++ b/site/src/main/paradox/datastore.md @@ -1,55 +1,60 @@ -EntityType -========== +# Datastore `EntityType[T]` provides conversion between Scala type `T` and Datastore `Entity`. Custom support for type `T` can be added with an implicit instance of `EntityField[T]`. -```scala +```scala mdoc:compile-only import java.net.URI + case class Inner(long: Long, str: String, uri: URI) case class Outer(inner: Inner) val record = Outer(Inner(1L, "hello", URI.create("https://www.spotify.com"))) import magnolify.datastore._ -import com.google.datastore.v1.{Entiyt, Entity.Builder} +import com.google.datastore.v1.Entity // Encode custom type URI as String -implicit val uriField = EntityField.from[String](URI.create)(_.toString) +implicit val uriField: EntityField[URI] = EntityField.from[String](URI.create)(_.toString) val entityType = EntityType[Outer] val entityBuilder: Entity.Builder = entityType.to(record) val copy: Outer = entityType.from(entityBuilder.build) ``` -Additional `EntityField[T]` instances for `Byte`, `Char`, `Short`, `Int`, `Float`, and enum-like types are available from `import magnolify.datastore.unsafe._`. These conversions are unsafe due to potential overflow or encoding errors. Enum like types map to strings. See [enums.md](https://github.com/spotify/magnolify/tree/master/docs/enums.md) for more details. +Additional `EntityField[T]` instances for `Byte`, `Char`, `Short`, `Int`, `Float`, and enum-like types are available from `import magnolify.datastore.unsafe._`. These conversions are unsafe due to potential overflow or encoding errors. Enum like types map to strings. See @ref:[EnumType](enums.md) for more details. To set a field as key, annotate the field with `key` annotation. -```scala +```scala mdoc:compile-only +import magnolify.datastore._ +import com.google.protobuf.ByteString + // Leave projectId as empty, use current package as namespaceId and class name "Record" as kind case class Record(@key k: String, v: Long) // Custom projectId, namespaceId and kind. -case class Record(@key("my-project", "com.spotify", "MyKind") k: String, v: Long) +case class ExplicitRecord(@key("my-project", "com.spotify", "MyKind") k: String, v: Long) // Encode custom key type ByteString as String case class ByteStringKey(@key k: ByteString) -implicit val kfByteString = KeyField.at[ByteString](_.toStringUtf8) +implicit val kfByteString: KeyField[ByteString] = KeyField.at[ByteString](_.toStringUtf8) // Encode custom key type RecordKey as String case class RecordKey(s: String, l: Long) case class NestedKey(@key k: RecordKey) -implicit val kfRecord = KeyField.at[RecordKey](r => r.s + r.l) +implicit val kfRecord: KeyField[RecordKey] = KeyField.at[RecordKey](r => r.s + r.l) ``` To exclude a property from indexes, annotate the field with `excludeFromIndexes` annotation. -```scala +```scala mdoc:compile-only +import magnolify.datastore._ case class Record(@excludeFromIndexes i: Int, @excludeFromIndexes(true) s: String) ``` To use a different field case format in target records, add an optional `CaseMapper` argument to `EntityType`. The following example maps `firstName` & `lastName` to `first_name` & `last_name`. -```scala +```scala mdoc:compile-only +import magnolify.datastore._ import magnolify.shared.CaseMapper import com.google.common.base.CaseFormat diff --git a/docs/enums.md b/site/src/main/paradox/enums.md similarity index 85% rename from docs/enums.md rename to site/src/main/paradox/enums.md index ee5c36116..f0fd39c5b 100644 --- a/docs/enums.md +++ b/site/src/main/paradox/enums.md @@ -1,11 +1,10 @@ -EnumType -======== +# EnumType `EnumType[T]` provides conversion between enum-like types and their string names. Supported enum-like types are Java `enum`, Scala `Enumeration`, and `sealed trait` with `case object`s. `AvroType[T]` and `ProtobufType[T]` use it to map to their native enum types, i.e. `EnumSymbol` and `ProtocolMessageEnum`, while other converters map to strings. `CaseMapper` supports enums too. -```scala +```scala mdoc object Color extends Enumeration { type Type = Value val Red, Green, Blue = Value @@ -13,12 +12,12 @@ object Color extends Enumeration { import magnolify.shared._ // Encode as ["red", "green", "blue"] -implicit val enumType = EnumType[Color.Type](CaseMapper(_.toLowerCase)) +implicit val enumType: EnumType[Color.Type] = EnumType.scalaEnumType[Color.Type].map(CaseMapper(_.toLowerCase)) ``` An enum-like type can be wrapped inside a `magnolify.shared.UnsafeEnum[T]` to handle unknown cases. This could be useful for scenarios like schema evolution, or working with bad data. -```scala +```scala mdoc UnsafeEnum(Color.Red) // Known(Red) UnsafeEnum.from[Color.Type]("Red") // Known(Red) UnsafeEnum.from[Color.Type]("Purple") // Unknown(Purple) diff --git a/site/src/main/paradox/guava.md b/site/src/main/paradox/guava.md new file mode 100644 index 000000000..44b0f16e8 --- /dev/null +++ b/site/src/main/paradox/guava.md @@ -0,0 +1,29 @@ +# Guava + +Type class derivation for Guava can be performed both automatically and semi-automatically. + +Automatic derivation are provided as implicits through `import magnolify.guava.auto._`. + +```scala mdoc:compile-only +case class Inner(int: Int, str: String) +case class Outer(inner: Inner) + +// Guava Funnel +import magnolify.guava.auto._ // includes implicit instances for Funnel[Int], etc. +import com.google.common.hash._ + +val fnl: Funnel[Outer] = implicitly[Funnel[Outer]] +val bf: BloomFilter[Outer] = BloomFilter.create[Outer](fnl, 1000) +``` + +Semi-automatic derivation needs to be called explicitly. + +```scala mdoc:compile-only +import magnolify.guava.semiauto._ +import com.google.common.hash._ + +case class Inner(int: Int, str: String) +case class Outer(inner: Inner) + +val fnl: Funnel[Outer] = FunnelDerivation[Outer] +``` diff --git a/site/src/main/paradox/images/favicon.ico b/site/src/main/paradox/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..bed17b7c44144482c44bcdea41397633ddc0cd20 GIT binary patch literal 5430 zcmc(juWuAV6vy`n=i4+*z}<-wkQE4A_YXjzkmMA&ra>U#6oQ0`3It*$B#09T1Y!lE z0&x<7IDx3Bs6ZeP$M>_$yUYzUyL)?gp~;t-_v`z<^Jcnf$g*B`E!)}2^fSnwT+gyw z>;`}B_wv7%@3u@*aQCWzv+PLz@Mk`s_k8T_-8c|l2~*)xxNv)X2Y#-sGaM{2($YGPdEE;t-#EU(UYh6dGg-a5_3F;U(-!E9=5R_6O4-+kd&c<%5}>mA{- z@I$a%R-3c$`!4?{p%CczRWN;M_$*ky-I3XE6niU_!b~_9sCg!!75Md+vVqkX+M|Ey z;jZ>*M-w`J2|opDE(A1KJ+|$`^_yZZg`2`7mq#r#AoruY58}Cpd)n{2*uM5p1=CEe ze}AQHeCVYII2L$w=K}ia1=v^bSu1h#w_U6E<8<{^-}(J|9O&>hhFXY`vv~W|`cRB{ zFau`GoPf=_Iy6VEDf z@$Cxm(kq;M$|jeZ-Bo&pxsGF%zM-B9-l4G0nNu3!JghrA z8n@KjG@&6m2gR6^-0Nn!bPpRodm+9d=hIrJVKD>c zp(mUXHg^IhLMg;@@E5{JsNNQzc2nBIxc>9(ULlk^|qDXoX_mo zI(3}njRa~8*kghDq372+KVb#I&AAk}MD6N}X_s}rGmf9_72`lseo zy-&D=2LiLQd7-)Re7W{Z-!*I(Pw}IlSSo%RzOOd6_|Ss36fb=ZGwn;yHovWKOa#si zZwxjY@=`tNGZl>2)>1xT_NLdFxJNd(NH2PGwpd?c)W>3VKF)b6MSL^)xfN~8Q&L2NNI1?zZTrOX{+YXfey8D3GiZ7XG43WGoU6R$P2L4uOYbc_cg$7|Z*rYkP#b+~ nv3@Zhx;yBo`cfO??CKLA-xp?5I?xB0O)Ou9-}|qC|FZo9V-&Xb literal 0 HcmV?d00001 diff --git a/site/src/main/paradox/images/logo.png b/site/src/main/paradox/images/logo.png new file mode 100755 index 0000000000000000000000000000000000000000..4a83bfd6add45ede796ed3f8673238f2cde35761 GIT binary patch literal 16260 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 A4gdfE literal 0 HcmV?d00001 diff --git a/site/src/main/paradox/index.md b/site/src/main/paradox/index.md new file mode 100644 index 000000000..537a022fe --- /dev/null +++ b/site/src/main/paradox/index.md @@ -0,0 +1,52 @@ +# Magnolify + +A collection of [Magnolia](https://github.com/propensive/magnolia) add-ons for common type class derivation, data type conversion, etc.; a simpler and faster successor to [shapeless-datatype](https://github.com/nevillelyh/shapeless-datatype). + +## Getting help + +- [GitHub discussions](https://github.com/spotify/magnolify/discussions) + +# Modules + +This library includes the following modules. + +- @ref:[`magnolify-avro`](avro.md) - conversion between Scala types and [Apache Avro](https://github.com/apache/avro) `GenericRecord` +- @ref:[`magnolify-bigquery`](bigquery.md) - conversion between Scala types and [Google Cloud BigQuery](https://cloud.google.com/bigquery/) `TableRow` +- @ref:[`magnolify-bigtable`](bigtable.md) - conversion between Scala types and [Google Cloud Bigtable](https://cloud.google.com/bigtable) to `Mutation`, from `Row` +- @ref:[`magnolify-cats`](cats.md) - type class derivation for [Cats](https://github.com/typelevel/cats), specifically + - [`Eq[T]`](https://typelevel.org/cats/api/cats/kernel/Eq.html) + - [`Hash[T]`](https://typelevel.org/cats/api/cats/kernel/Hash.html) + - [`Semigroup[T]`](https://typelevel.org/cats/api/cats/kernel/Semigroup.html), [`CommutativeSemigroup[T]`](https://typelevel.org/cats/api/cats/kernel/CommutativeSemigroup.html), [`Band[T]`](https://typelevel.org/cats/api/cats/kernel/Band.html) + - [`Monoid[T]`](https://typelevel.org/cats/api/cats/kernel/Monoid.html), [`CommutativeMonoid[T]`](https://typelevel.org/cats/api/cats/kernel/CommutativeMonoid.html) + - [`Group[T]`](https://typelevel.org/cats/api/cats/kernel/Group.html), [`CommutativeGroup[T]`](https://typelevel.org/cats/api/cats/kernel/CommutativeGroup.html) +- @ref:[`magnolify-datastore`](datastore.md) - conversion between Scala types and [Google Cloud Datastore](https://cloud.google.com/datastore/) `Entity` +- @ref:[`magnolify-guava`](guava.md) - type class derivation for [Guava](https://guava.dev) + - [`Funnel[T]`](https://guava.dev/releases/snapshot-jre/api/docs/com/google/common/hash/Funnel.html) +- @ref:[`magnolify-neo4j`](neo4j.md) - conversion between Scala types and [Value](https://neo4j.com/docs/driver-manual/1.7/cypher-values/) +- @ref:[`magnolify-parquet`](parquet.md) - support for [Parquet](http://parquet.apache.org/) columnar storage format. +- @ref:[`magnolify-protobuf`](protobuf.md) - conversion between Scala types and [Google Protocol Buffer](https://developers.google.com/protocol-buffers/docs/overview) `Message` +- @ref:[`magnolify-refined`](refined.md) - support for simple refinement types from [Refined](https://github.com/fthomas/refined). +- @ref:[`magnolify-scalacheck`](scalacheck.md) - type class derivation for [ScalaCheck](https://github.com/typelevel/scalacheck) + - [`Arbitrary[T]`](https://github.com/typelevel/scalacheck/blob/master/doc/UserGuide.md#universally-quantified-properties) + - [`Cogen[T]`](https://github.com/typelevel/scalacheck/blob/master/src/main/scala/org/scalacheck/Cogen.scala) +- @ref:[`magnolify-tensorflow`](tensorflow.md) - conversion between Scala types and [TensorFlow](https://www.tensorflow.org/) `Example` + +Complete type mapping @ref:[here](mapping.md). + +@@@ index +- @ref:[Avro](avro.md) +- @ref:[BigQuery](bigquery.md) +- @ref:[Bigtable](bigtable.md) +- @ref:[Cats](cats.md) +- @ref:[Datastore](datastore.md) +- @ref:[Guava](guava.md) +- @ref:[Neo4j](neo4j.md) +- @ref:[Parquet](parquet.md) +- @ref:[Protobuf](protobuf.md) +- @ref:[Refined](refined.md) +- @ref:[ScalaCheck](scalacheck.md) +- @ref:[TensorFlow](tensorflow.md) +- @ref:[EnumType](enums.md) +- @ref:[Mapping](mapping.md) +- @ref:[Scaladoc](scaladoc.md) +@@@ \ No newline at end of file diff --git a/docs/mapping.md b/site/src/main/paradox/mapping.md similarity index 54% rename from docs/mapping.md rename to site/src/main/paradox/mapping.md index f1d383b73..91fb93ba0 100644 --- a/docs/mapping.md +++ b/site/src/main/paradox/mapping.md @@ -1,9 +1,8 @@ -Type Mapping -============ +# Type Mapping | Scala | Avro | BigQuery | Bigtable7 | Datastore | Parquet | Protobuf | TensorFlow | |-----------------------------------|------------------------------|------------------------|---------------------------------|-----------------------|-----------------------------------|-------------------------|---------------------| -| `Unit` | `NULL` | - | - | `Null` | - | - | - | +| `Unit` | `NULL` | x | x | `Null` | x | x | x | | `Boolean` | `BOOLEAN` | `BOOL` | `Byte` | `Boolean` | `BOOLEAN` | `Boolean` | `INT64`3 | | `Char` | `INT`3 | `INT64`3 | `Char` | `Integer`3 | `INT32`3 | `Int`3 | `INT64`3 | | `Byte` | `INT`3 | `INT64`3 | `Byte` | `Integer`3 | `INT32`9 | `Int`3 | `INT64`3 | @@ -12,41 +11,51 @@ Type Mapping | `Long` | `LONG` | `INT64` | `Long` | `Integer` | `INT64`9 | `Long` | `INT64` | | `Float` | `FLOAT` | `FLOAT64`3 | `Float` | `Double`3 | `FLOAT` | `Float` | `FLOAT` | | `Double` | `DOUBLE` | `FLOAT64` | `Double` | `Double` | `DOUBLE` | `Double` | `FLOAT`3 | -| `CharSequence` | `STRING` | - | - | - | - | - | - | +| `CharSequence` | `STRING` | x | x | x | x | x | x | | `String` | `STRING` | `STRING` | `String` | `String` | `BINARY` | `String` | `BYTES`3 | | `Array[Byte]` | `BYTES` | `BYTES` | `ByteString` | `Blob` | `BINARY` | `ByteString` | `BYTES` | -| `ByteString` | - | - | `ByteString` | `Blob` | - | `ByteString` | `BYTES` | -| `ByteBuffer` | `BYTES` | - | - | | - | - | - | +| `ByteString` | x | x | `ByteString` | `Blob` | x | `ByteString` | `BYTES` | +| `ByteBuffer` | `BYTES` | x | x | | x | x | x | | Enum1 | `ENUM` | `STRING`3 | `String` | `String`3 | `BINARY`/`ENUM`9 | Enum | `BYTES`3 | -| `BigInt` | - | - | `BigInt` | - | - | - | - | -| `BigDecimal` | `BYTES`4 | `NUMERIC`6 | `Int` scale + unscaled `BigInt` | - | `LOGICAL[DECIMAL]`9,14 | - | - | +| `BigInt` | x | x | `BigInt` | x | x | x | x | +| `BigDecimal` | `BYTES`4 | `NUMERIC`6 | `Int` scale + unscaled `BigInt` | x | `LOGICAL[DECIMAL]`9,14 | x | x | | `Option[T]` | `UNION[NULL, T]`5 | `NULLABLE` | Empty as `None` | Absent as `None` | `OPTIONAL` | `optional`10 | Size <= 1 | -| `Iterable[T]`2 | `ARRAY` | `REPEATED` | - | `Array` | `REPEATED`13 | `repeated` | Size >= 0 | +| `Iterable[T]`2 | `ARRAY` | `REPEATED` | x | `Array` | `REPEATED`13 | `repeated` | Size >= 0 | | Nested | `RECORD` | `STRUCT` | Flat8 | `Entity` | Group | `Message` | Flat8 | -| `Map[CharSequence, T]` | `MAP[STRING, T]` | - | - | - | - | - | | -| `Map[String, T]` | `MAP[STRING, T]` | - | - | - | - | - | - | -| `java.time.Instant` | `LONG`11 | `TIMESTAMP` | - | `Timestamp` | `LOGICAL[TIMESTAMP]`9 | - | - | -| `java.time.LocalDateTime` | `LONG`11 | `DATETIME` | - | - | `LOGICAL[TIMESTAMP]`9 | - | - | -| `java.time.OffsetTime` | - | - | - | - | `LOGICAL[TIME]`9 | - | - | -| `java.time.LocalTime` | `LONG`11 | `TIME` | - | - | `LOGICAL[TIME]`9 | - | - | -| `java.time.LocalDate` | `INT`11 | `DATE` | - | - | `LOGICAL[DATE]`9 | - | - | -| `org.joda.time.LocalDate` | `INT`11 | - | - | - | - | - | - | -| `org.joda.time.DateTime` | `INT`11 | - | - | - | - | - | - | -| `org.joda.time.LocalTime` | `INT`11 | - | - | - | - | - | - | - | `java.util.UUID` | `STRING`4 | - | ByteString (16 bytes) | - | `FIXED[16]` | - | - | - | `(Long, Long, Long)`12 | `FIXED[12]` | - | - | - | - | - | - | +| `Map[CharSequence, T]` | `MAP[STRING, T]` | x | x | x | x | x | | +| `Map[String, T]` | `MAP[STRING, T]` | x | x | x | x | x | x | +| `java.time.Instant` | `LONG`11 | `TIMESTAMP` | x | `Timestamp` | `LOGICAL[TIMESTAMP]`9 | x | x | +| `java.time.LocalDateTime` | `LONG`11 | `DATETIME` | x | x | `LOGICAL[TIMESTAMP]`9 | x | x | +| `java.time.OffsetTime` | x | x | x | x | `LOGICAL[TIME]`9 | x | x | +| `java.time.LocalTime` | `LONG`11 | `TIME` | x | x | `LOGICAL[TIME]`9 | x | x | +| `java.time.LocalDate` | `INT`11 | `DATE` | x | x | `LOGICAL[DATE]`9 | x | x | +| `org.joda.time.LocalDate` | `INT`11 | x | x | x | x | x | x | +| `org.joda.time.DateTime` | `INT`11 | x | x | x | x | x | x | +| `org.joda.time.LocalTime` | `INT`11 | x | x | x | x | x | x | +| `java.util.UUID` | `STRING`4 | x | ByteString (16 bytes) | x | `FIXED[16]` | x | x | +| `(Long, Long, Long)`12 | `FIXED[12]` | x | x | x | x | x | x | -1. Those wrapped in`UnsafeEnum` are encoded as strings, see [enums.md](https://github.com/spotify/magnolify/blob/master/docs/enums.md) for more +1. Those wrapped in`UnsafeEnum` are encoded as strings, + see [enums.md](https://github.com/spotify/magnolify/blob/master/docs/enums.md) for more 2. Any subtype of `Iterable[T]` 3. Unsafe conversions, `import magnolify.$MODULE.unsafe._` 4. Avro logical types ([doc](https://avro.apache.org/docs/current/spec.html#Logical+Types)) 5. `UNION` of `[NULL, T]` and defaults to `NULL` ([doc](https://avro.apache.org/docs/current/spec.html#Unions)) -6. Fixed precision of 38 and scale of 9 ([doc](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#numeric-type)) +6. Fixed precision of 38 and scale of + 9 ([doc](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#numeric-type)) 7. All Scala types are encoded as big endian `ByteString` for Bigtable 8. Nested fields are encoded flat with field names joined with `.`, e.g. `level1.level2.level3` -9. More information on Parquet logical type schemas can be found [here](https://github.com/apache/parquet-format/blob/master/LogicalTypes.md). Time types are available at multiple precisions; import `magnolify.parquet.logical.micros._`, `magnolify.avro.logical.millis._`, or `magnolify.avro.logical.nanos._` accordingly. +9. More information on Parquet logical type schemas can be + found [here](https://github.com/apache/parquet-format/blob/master/LogicalTypes.md). Time types are available at + multiple precisions; import `magnolify.parquet.logical.micros._`, `magnolify.avro.logical.millis._`, + or `magnolify.avro.logical.nanos._` accordingly. 10. See [protobuf.md](https://github.com/spotify/magnolify/blob/master/docs/protobuf.md) for more -11. Logical types available at micro- or milli-second precision; import `magnolify.avro.logical.micros._` or `magnolify.avro.logical.millis._` accordingly. BigQuery-compatible conversions are available in `magnolify.avro.logical.bigquery._`. -12. Special tuple used to represent Duration in the [Avro spec](https://avro.apache.org/docs/1.11.0/spec.html#Duration). This has not been made implicit in Magnolify; import `AvroType.afDuration` implicitly to enable -13. If `magnolify.parquet.ParquetArray.AvroCompat._` is imported, array fields use the nested, Avro-compatible schema format: `required group $FIELDNAME (LIST) { repeated $FIELDTYPE array ($FIELDSCHEMA); }`. -14. Parquet's Decimal logical format supports multiple representations, and are not implicitly scoped by default. Import one of: `magnolify.parquet.ParquetField.{decimal32, decimal64, decimalFixed, decimalBinary}`. +11. Logical types available at micro- or milli-second precision; import `magnolify.avro.logical.micros._` + or `magnolify.avro.logical.millis._` accordingly. BigQuery-compatible conversions are available + in `magnolify.avro.logical.bigquery._`. +12. Special tuple used to represent Duration in the [Avro spec](https://avro.apache.org/docs/1.11.0/spec.html#Duration). + This has not been made implicit in Magnolify; import `AvroType.afDuration` implicitly to enable +13. If `magnolify.parquet.ParquetArray.AvroCompat._` is imported, array fields use the nested, Avro-compatible schema + format: `required group $FIELDNAME (LIST) { repeated $FIELDTYPE array ($FIELDSCHEMA); }`. +14. Parquet's Decimal logical format supports multiple representations, and are not implicitly scoped by default. Import + one of: `magnolify.parquet.ParquetField.{decimal32, decimal64, decimalFixed, decimalBinary}`. diff --git a/site/src/main/paradox/neo4j.md b/site/src/main/paradox/neo4j.md new file mode 100644 index 000000000..6f8f7bf2f --- /dev/null +++ b/site/src/main/paradox/neo4j.md @@ -0,0 +1,3 @@ +# Neo4j + +TODO diff --git a/docs/parquet.md b/site/src/main/paradox/parquet.md similarity index 73% rename from docs/parquet.md rename to site/src/main/paradox/parquet.md index 5a79b4c40..ccb4c0277 100644 --- a/docs/parquet.md +++ b/site/src/main/paradox/parquet.md @@ -1,9 +1,8 @@ -ParquetType -=========== +# Parquet `ParquetType[T]` provides read and write support between Scala type `T` and the Parquet columnar storage format. Custom support for type `T` can be added with an implicit instance of `ParquetField[T]`. -```scala +```scala mdoc:compile-only import java.net.URI case class Inner(long: Long, str: String, uri: URI) case class Outer(inner: Inner) @@ -12,23 +11,24 @@ val record = Outer(Inner(1L, "hello", URI.create("https://www.spotify.com"))) import magnolify.parquet._ // Encode custom type URI as String -implicit val uriField = ParquetField.from[String](b => URI.create(b))(_.toString) +implicit val uriField: ParquetField[URI] = ParquetField.from[String](b => URI.create(b))(_.toString) val parquetType = ParquetType[Outer] // Parquet schema -parquetType.schema +val schema = parquetType.schema ``` Use `ParquetType#readBuilder` and `ParquetType#writeBuilder` to create new file reader and writer instances. See [HadoopSuite.scala](https://github.com/spotify/magnolify/tree/master/parquet/src/test/scala/magnolify/parquet/test/HadoopSuite.scala) for examples with Hadoop IO. -Enum-like types map to strings. See [enums.md](https://github.com/spotify/magnolify/tree/master/docs/enums.md) for more details. Additional `ParquetField[T]` instances for `Char` and `UnsafeEnum[T]` are available from `import magnolify.parquet.unsafe._`. This conversions is unsafe due to potential overflow. +Enum-like types map to strings. See @ref:[EnumType](enums.md) for more details. Additional `ParquetField[T]` instances for `Char` and `UnsafeEnum[T]` are available from `import magnolify.parquet.unsafe._`. This conversions is unsafe due to potential overflow. To use a different field case format in target records, add an optional `CaseMapper` argument to `ParquetType`. The following example maps `firstName` & `lastName` to `first_name` & `last_name`. -```scala +```scala mdoc:compile-only import magnolify.shared.CaseMapper import com.google.common.base.CaseFormat +import magnolify.parquet._ case class LowerCamel(firstName: String, lastName: String) @@ -38,11 +38,13 @@ val parquetType = ParquetType[LowerCamel](CaseMapper(toSnakeCase)) Parquet `decimal` logical type maps to `BigDecimal` and supports the following encodings: -```scala -implicit val pfDecimal32 = ParquetField.decimal32(9, 0) -implicit val pfDecimal64 = ParquetField.decimal64(18, 0) -implicit val pfDecimalFixed = ParquetField.decimalFixed(8, 18, 0) -implicit val pfDecimalBinary = ParquetField.decimalBinary(20, 0) +```scala mdoc:compile-only +import magnolify.parquet._ + +val pfDecimal32 = ParquetField.decimal32(9, 0) +val pfDecimal64 = ParquetField.decimal64(18, 0) +val pfDecimalFixed = ParquetField.decimalFixed(8, 18, 0) +val pfDecimalBinary = ParquetField.decimalBinary(20, 0) ``` Among the date/time types, `DATE` maps to `java.time.LocalDate`. The other types, `TIME` and `TIMESTAMP`, map to `OffsetTime`/`LocalTime` and `Instant`/`LocalDateTime` with `isAdjustedToUTC` set to `true`/`false`. They can be in nano, micro, or milliseconds precision with `import magnolify.parquet.logical.{nanos,micros,millis}._`. @@ -51,11 +53,12 @@ Note that Parquet's official Avro support maps `REPEATED` fields to an `array` f The top level class and all fields (including nested class fields) can be annotated with `@doc` annotation. Note that nested classes annotations are ignored. -```scala +```scala mdoc:compile-only +import magnolify.shared._ + @doc("This is ignored") case class NestedClass(@doc("nested field annotation") i: Int) @doc("Top level annotation") -case class TopLevelType(@doc("field annotation") pd: NestedClass, @doc("field annotation 2") i: -Integers) +case class TopLevelType(@doc("field annotation") pd: NestedClass) ``` \ No newline at end of file diff --git a/docs/protobuf.md b/site/src/main/paradox/protobuf.md similarity index 73% rename from docs/protobuf.md rename to site/src/main/paradox/protobuf.md index 9e0010c7b..b5b977385 100644 --- a/docs/protobuf.md +++ b/site/src/main/paradox/protobuf.md @@ -1,14 +1,16 @@ -ProtobufType -============ +# Protobuf `ProtobufType[T, MsgT]` provides conversion between Scala type `T` and Protobuf `MsgT <: Message`. Custom support for type `T` can be added with an implicit instance of `ProtobufField[T]`. -```scala +```scala mdoc:compile-only import java.net.URI case class Inner(long: Long, str: String, uri: URI) case class Outer(inner: Inner) val record = Outer(Inner(1L, "hello", URI.create("https://www.spotify.com"))) +// Protobuf record +abstract class MyProto extends com.google.protobuf.Message + import magnolify.protobuf._ // Encode custom type URI as String @@ -20,9 +22,9 @@ val proto: MyProto = protobufType.to(record) val copy: Outer = protobufType.from(proto) ``` -Enum like types map to Protobuf enums. See [enums.md](https://github.com/spotify/magnolify/tree/master/docs/enums.md) for more details. An implicit instance from Java or Scala type to Protobuf enum must be provided. +Enum like types map to Protobuf enums. See @ref:[EnumType](enums.md) for more details. An implicit instance from Java or Scala type to Protobuf enum must be provided. -```scala +```scala mdoc:compile-only // Scala enum object Color extends Enumeration { type Type = Value @@ -35,8 +37,9 @@ object Color extends Enumeration { // GREEN = 1; // BLUE = 2; // } +abstract class ColorProto(name: String, ordinal: Int) extends Enum[ColorProto](name, ordinal) with com.google.protobuf.ProtocolMessageEnum -import magnolify.shared._ +import magnolify.protobuf._ implicit val efEnum = ProtobufField.enum[Color.Type, ColorProto] ``` @@ -44,12 +47,16 @@ Additional `ProtobufField[T]` instances for `Byte`, `Char`, `Short`, and `Unsafe To use a different field case format in target records, add an optional `CaseMapper` argument to `ProtobufType`. The following example maps `firstName` & `lastName` to `first_name` & `last_name`. -```scala +```scala mdoc:compile-only import magnolify.shared.CaseMapper import com.google.common.base.CaseFormat +import magnolify.protobuf._ case class LowerCamel(firstName: String, lastName: String) +// Protobuf record +abstract class LowerHyphenProto extends com.google.protobuf.Message + val toSnakeCase = CaseFormat.LOWER_CAMEL.converterTo(CaseFormat.LOWER_UNDERSCORE).convert _ val protobufType = ProtobufType[LowerCamel, LowerHyphenProto](CaseMapper(toSnakeCase)) protobufType.to(LowerCamel("John", "Doe")) diff --git a/docs/refined.md b/site/src/main/paradox/refined.md similarity index 66% rename from docs/refined.md rename to site/src/main/paradox/refined.md index a85b3e15b..fbda7043d 100644 --- a/docs/refined.md +++ b/site/src/main/paradox/refined.md @@ -1,25 +1,19 @@ -Refined -======= +# Refined [Refined](https://github.com/fthomas/refined) is a Scala library for refining types with type-level predicates which constrain the set of values described by the refined type. -```scala +```scala mdoc:compile-only import eu.timepit.refined._ import eu.timepit.refined.api._ import eu.timepit.refined.auto._ -import eu.timepit.refined.boolean._ import eu.timepit.refined.numeric._ import eu.timepit.refined.string._ case class Record(pos: Int Refined Positive, url: String Refined Url) -// Refinements are checked at compile time +// Refinements are checked at compile time with literals Record(42, "https://www.spotify.com") -``` - -However refinements only works with literals, so we have to use unsafe runtime check for variables. - -```scala +// otherwise unsafe runtime check have to be used for variables. val x = -1 val url = "foo" // Throws IllegalArgumentException @@ -28,10 +22,17 @@ Record(refineV.unsafeFrom(x), refineV.unsafeFrom(url)) Magnolify works with Refined through some extra implicits. -```scala +```scala mdoc:compile-only +import eu.timepit.refined.api._ +import eu.timepit.refined.auto._ +import eu.timepit.refined.numeric._ +import eu.timepit.refined.string._ + import magnolify.avro._ import magnolify.refined.avro._ +case class Record(pos: Int Refined Positive, url: String Refined Url) + val at = AvroType[Record] at(Record(42, "https://www.spotify.com")) ``` diff --git a/site/src/main/paradox/scalacheck.md b/site/src/main/paradox/scalacheck.md new file mode 100644 index 000000000..2542cf3e2 --- /dev/null +++ b/site/src/main/paradox/scalacheck.md @@ -0,0 +1,30 @@ +# Scalacheck + +Type class derivation for Scalacheck can be performed both automatically and semi-automatically. + +Automatic derivation are provided as implicits through `import magnolify.scalacheck.auto._`. + +```scala mdoc:compile-only +case class Inner(int: Int, str: String) +case class Outer(inner: Inner) + +// ScalaCheck Arbitrary +import magnolify.scalacheck.auto._ +import org.scalacheck._ // implicit instances for Arbitrary[Int], etc. + +val arb: Arbitrary[Outer] = implicitly[Arbitrary[Outer]] +arb.arbitrary.sample // = Some(Outer(Inter(12345, abcde))) +``` + +Semi-automatic derivation needs to be called explicitly. + +```scala mdoc:compile-only +import magnolify.scalacheck.semiauto._ +import org.scalacheck._ + +case class Inner(int: Int, str: String) +case class Outer(inner: Inner) + +val arb: Arbitrary[Outer] = ArbitraryDerivation[Outer] +val cogen: Cogen[Outer] = CogenDerivation[Outer] +``` diff --git a/site/src/main/paradox/scaladoc.md b/site/src/main/paradox/scaladoc.md new file mode 100644 index 000000000..a40279e94 --- /dev/null +++ b/site/src/main/paradox/scaladoc.md @@ -0,0 +1,5 @@ +--- +layout: scaladoc +--- + +# Scaladoc diff --git a/docs/tensorflow.md b/site/src/main/paradox/tensorflow.md similarity index 92% rename from docs/tensorflow.md rename to site/src/main/paradox/tensorflow.md index ca4bb3259..a23e6ac9d 100644 --- a/docs/tensorflow.md +++ b/site/src/main/paradox/tensorflow.md @@ -1,5 +1,4 @@ -ExampleType -=========== +# Tensorflow `ExampleType[T]` provides conversion between Scala type `T` and TensorFlow `Example`. Custom support for type `T` can be added with an implicit instance of `ExampleField[T]`. @@ -25,7 +24,7 @@ val copy = exampleType.from(exampleBuilder.build) `ExampleType` encodes each field in a `Feature` of the same name. It encodes nested fields by joining field names as `field_a.field_b.field_c`. Optional and repeated types are not supported in a nested field. -Additional `ExampleField[T]` instances for `Byte`, `Char`, `Short`, `Int`, `Double`, `Boolean`, `String`, and enum-like types are available from `import magnolify.tensorflow.unsafe._`. These conversions are unsafe due to potential overflow and encoding errors. Enum-like types map to strings. See [enums.md](https://github.com/spotify/magnolify/tree/master/docs/enums.md) for more details. +Additional `ExampleField[T]` instances for `Byte`, `Char`, `Short`, `Int`, `Double`, `Boolean`, `String`, and enum-like types are available from `import magnolify.tensorflow.unsafe._`. These conversions are unsafe due to potential overflow and encoding errors. Enum-like types map to strings. See @ref:[EnumType](enums.md) for more details. To use a different field case format in target records, add an optional `CaseMapper` argument to `ExampleType`. The following example maps `firstName` & `lastName` to `first_name` & `last_name`.