2 releases

0.1.1 Sep 8, 2020
0.1.0 Sep 8, 2020

#2686 in Parser implementations

MIT license

61KB
2K SLoC

Transporter

A Code Generator to for safe, cross-language API types

Setup

  • make sure ~/.cargo/bin is in your PATH (run export PATH="$PATH:$HOME/.cargo/bin)
  • cargo install transporter

How it works

Transporter is a CLI tool that generates data structures that can easily be serialized and deserialized (mainly to/from JSON) for transmission over http(s) across different languages.

Why not GraphQL

Because you want to avoid rethinking your API entirely from the ground up. In contrast to GraphQL, Transporter is not a query language. All it does is add transport type safety.

Why not Protocol Buffers

Because Protocol Buffers are transmitted in a super space-efficient binary format. Transporter-generated types are transmitted as JSON which you can just read and debug like any other JSON-based API.

Why not OpenAPI

Because it does not support wilcard paths (variable number of path parameters).

Why not JSON Schema

Because there is a huge mess of different versions of the spec, tooling is largely outdated and there's no elegant way to map tagged unions.

Features

Transporter reads a toml file as input in which types are defined in terms of

Basic Types

  • string
  • int
  • float
  • bool

since transporter types are generally non-nullable, nullable types must be defined as

  • Optional (denoted as (string))

and for otherwise structured data there is also

Collection Types

  • List ([string]), serialized as JSON arrays
  • Dict ({string}), serialized as JSON objects

User defined types are either

Aliases

[ alias.Addressbook ]
type = "[Contact]"

In this example the alias "Addressbook" is given to a list of Contacts.

Structures

[ struct.PhoneNumber ]
type = "PhoneNumberType"
number = "string"

[ struct.Contact ]
name = "string"
phone = "[PhoneNumber]"

In this example two structs are defined

  • PhoneNumber, which has two fields, type and number
  • Contact, which also has two fields, name and phone

Structs are serialized as JSON object ({"name":"John Doe","phone":[...]})

Tagged Unions

[ union.PhoneNumberType ]
Mobile = {}
Home = {}
Custom = { name = "string" }

In this example a union type is defined. Unions in Transporter are generally tagged unions. An instance of PhoneNumberType::Mobile would be serialized as just "Mobile". An instance of PhoneNumberType::Custom with name set to "Work" would be serialized as {"Custom":{"name":"Work"}}.

Language support

Currently, transporter supports rust und php. In near future, support for Elm and Typescript / Javascript is planned.

Rust

In rust the above examples would roughly (omitting mainly some serde macros) translate to

type Addressbook = Vec<Contact>;

struct PhoneNumber {
    type_: PhoneNumberType,
    number: String,
}

struct Contact {
    name: String,
    phone: Vec<PhoneNumber>,
}

enum PhoneNumberType {
    Mobile,
    Home,
    Custom {
        name: String,
    },
}

PHP

In php the above examples would translate to classes such as

// Addressbook.php
class Addressbook {
    private $value;

    public function __construct(array $value) {...}
    public function get() : array {...}
    public function set(array $value) {...}
    public function deserialize($input) : Self {...}
}

// PhoneNumber.php
class PhoneNumber {
    private $type;
    private $number;

    public function __construct(array $fields) {...}
    public function getType() : PhoneNumberType {...}
    public function setType(PhoneNumberType $value) {...}
    public function getNumber() : string {...}
    public function setNumber(string $value) {...}
    ...
}

// Contact.php
class Contact {...}

// PhoneNumberType.php
abstract class PhoneNumberType {...}

// PhoneNumberType/Mobile.php
class Mobile extends PhoneNumberType {...}

// PhoneNumberType/Home.php
class Home extends PhoneNumberType {...}

// PhoneNumberType/Custom.php
class Custom extends PhoneNumberType {...}

Dependencies

~2.5–3.5MB
~58K SLoC