davegurnell / bridges

Generate bindings for Scala types in other programming languages.

GitHub

Bridges

Generate bindings for Scala types in other programming languages.

Copyright 2017-2018 Dave Gurnell. Licensed Apache 2.0.

Build Status Coverage status Maven Central

Getting Started

Grab the code by adding the following to your build.sbt:

libraryDependencies += "com.davegurnell" %% "bridges" % "<<VERSION>>"

Synopsis

Render Typescript/Flow Declarations for Scala ADTs

Create a simple data model:

final case class Color(red: Int, green: Int, blue: Int)

sealed abstract class Shape extends Product with Serializable
final case class Circle(radius: Double, color: Color) extends Shape
final case class Rectangle(width: Double, height: Double, color: Color) extends Shape

Call declaration[Foo] to generate a type-declaration for Foo. Call render[Typescript](...) to convert a list of declarations as Typescript, or render[Flow](...) to render them as Flow types.

import bridges._
import bridges.syntax._

render(List(
  declaration[Color],
  declaration[Circle],
  declaration[Rectangle],
  declaration[Shape]
))
// res1: String =
// export type Color = {
//   red: number,
//   green: number,
//   blue: number
// };
//
// export type Circle = {
//   radius: number,
//   color: Color
// };
//
// export type Rectangle = {
//   width: number,
//   height: number,
//   color: Color
// };
//
// export type Shape =
//   ({ type: "Circle" } & Circle) |
//   ({ type: "Rectangle" } & Rectangle);

Elm extensions

Support for Elm has been added to the project. You can use the render method, as described above, to convert an ADT to an Elm type that compiles in Elm 0.18.x.

Additionally, the following methods are available (with some caveats described below):

  • jsonDecoder : generates a valid Elm Decode.Decoder for your ADT
  • jsonEncoder : generates a valid Elm Encoder for your ADT
  • buildFile : returns a pair (String, String) where the first element is the name of an Elm file and the second element is the content for that file. The file contains the ADT in Elm, the json decoder, and the json encoder. The module can be passed as a parameter, so you can copy the contents to a location of your choice.

Note that buildFile has a special behaviour: if you provide a list of declaration as the input, the output will be the contents of a single file, including the definitions of all the elements provided. This is done so we can avoid circular references in some scenarios.

If you want to use any of the JSON encoders or decoders generated by the project you need to do the following:

  • Your Elm project must include the NoRedInk/elm-decode-pipeline dependency
  • We assume any non-primitive type in your ADT will be generated by Bridges in the same module as the current ADT, to be able to define the right imports for them.
  • If your ADT is/has a CoProduct, the generated json must distinguish between alternatives using a field named type that encodes the name of the coproduct instance as a String. If you use Circe, see this link

Working with Refined types

If you are interested in this library you are most likely using Refined.

We have provided a default encoder for refined types. It will defaults to the basic type associated with the refined type. For example:

  • For Int Refined Greater[W.6.T] we treat the type as an Int
  • For String Refined Size[ClosedOpen[W.1.T, W.100.T]] we treat the type as a String
  • etc

This should cover most (if not all) use cases of refined types when converting to other languages. You can still override the default encoder with your own higher-precedence encoder.

You can see an example of this in tests for class ClassWithRefinedType.

Developing

We use the git flow branching model:

  • most development happens on feature branches called feature/foo;
  • complete features are PR'd onto develop;
  • releases are tagged and merged to master.

Travis is configured to publish as follows:

  • releases from release tags woth the format x.y.z;
  • snapshots from non-PR commits to develop.

The git.baseVersion line in build.sbt should be kept up-to-date with the next non-snapshot release number.

Example: Publishing a Release

This development, versioning, and publishing process is a WIP. TODO: Investigate using git-release to automate some of this:

# Start with a clean Git (everything committed).

# Switch to develop branch:
$ git checkout develop

# Ensure you're up-to-date with Github:
$ git pull
$ git pull --tags

# See what versions we've released before.
# Work out what version you're releasing next:
$ git tag --list

# Start a release:
$ git flow release start x.y.z

# Finish the release, updating master and develop and creating the x.y.z tag:
$ git flow release finish x.y.z

# Modify build.sbt and update git.baseVersion to x.(y+1).z:
$ git checkout develop
$ myeditor build.sbt # etc...

# Push to Github. Travis will publish the release and a new snapshot:
$ git push --all
$ git push --tags