The Bond logo: a stylized glue gun

Command line options

IDL syntax

A Bond schema definition file can contain the following elements:

Import statements

In order to use types defined in another schema definition file, the other file needs to be explicitly imported. Schema file can contain zero or more import statements and they must appear at the top of the file:

import "file.bond"

The file being imported can be specified using a partial path which is resolved by Bond compiler relative to the directory containing the schema file being compiled and any import path(s) specified using the –import-dir option(s) passed to gbc.

See examples:

Namespace definition

All schema types are always defined within a namespace. Namespace scope starts with namespace definition statement and ends at the end of the file.

namespace some.unique.name

Enum definition

Bond enums are very similar to C++ enums, both in semantics and syntax used to define them:

enum Protocols
{
    TCP,
    UDP = 10
}

On the wire values of enums types are equivalent to 32-bit signed integers.

Forward declaration

In order to define recursive schemas, such as trees, it may be necessary to declare a struct before it is defined. A forward declaration statement serves this purpose:

struct Node;

Forward declared structs can be used in field declarations as the base type for nullable<T> and bonded<T> or the element type of a container.

struct Node;

struct Node
{
    0: nullable<Node> left;
    1: nullable<Node> right;
}

Struct definition

Struct definition consists of a struct name, an optional base struct, and zero or more fields.

struct Example : Base
{
    0: uint32 fieldName = 10;
}

Field definition consists of an ordinal/id, type, name, and a default value. The fields type’s can be:

An optional default value can be specified for fields of basic types. For integers the default can be specified as either a decimal number or a hexadecimal number prefixed with 0x. The only explicit default value allowed for containers is nothing. Enum fields must have an explicit default value which must be one of the enum named constants or nothing.

Names of structs and enums defined in another namespace must be qualified with the namespace name:

import "bond/core/bond.bond"

namespace example

struct Example
{
    0: bond.GUID id;
    1: bond.BondDataType type = BT_UNAVAILABLE;
}

Generic struct

Generic structs are parameterized with one or more type parameters which can be used within the struct definition in any place where a concrete type could be used (e.g. base struct, field type, container element type, parameter of a generic struct).

struct Example<T1, T2> : T1
{
    0: T1 field;
    1: list<T2> list;
    2: Generic<T2> generic;
}

The usage of a type parameter within a generic struct definition may implicitly constrain what type(s) can be used to instantiate the generic struct:

struct Example<T>
{
    // The default value of 10 implicitly constrains T to numeric types
    0: T x = 10;
}

Using a type parameter in a nullable or as the type of a field with default value of nothing constrains the type parameter to be non-scalar type. If this is undesired then explicit constraint to value type can be specified in the generic schema definition:

struct Example<T : value>
{
    0: nullable<T> x;
    1: T y = nothing;
}

When instantiating a generic struct all type parameters must be concrete types. Bond IDL doesn’t support the equivalent of C++ template template parameters.

See examples:

Type aliases

The syntax to define type aliases is very similar to C++:

using time = int64;
using array<T> = vector<T>;

An alias can be used in any context where the aliased type could be used, including a definition of another alias:

using times = array<time>;

Type aliases can optionally be mapped to custom types in the generated code for both C++ and C#.

See examples:

Struct views

A view definition is syntactic sugar to define a struct that has a subset of the fields of another struct:

struct Example
{
    0: int16 x;
    1: string y;
    2: list<bool> z;
}

struct View view_of Example
{
    x,z;
}

In the above example, the definition of View is equivalent to:

struct View
{
    0: int16 x;
    2: list<bool> z;
}

A view of a generic struct is also a generic struct with the same number of type parameters.

See example: examples/cpp/core/schema_view

Service definition

Bond-over-gRPC is deprecated and will be completely removed from Bond in May 2022. See GitHub issue #1131, Bond-over-gRPC will be deprecated February 2022, for full details. gbc will still be able to parse service definitions and emit them in JSON AST, but C++ and C# codegen will be removed in May 2022.

A service definition consists of a service name and methods:

service Calculator
{
    Result Calculate(Operation);
    void Configure(Settings);
    Stats GetStats();

    Result RunningSum(stream Operation);
    stream Result ComputePrimes(Limit);
    stream Result StartRPNSession(stream Operation);
}

Methods take as input and return as results Bond structs.

There are five different kinds of service methods:

  1. unary: take one parameter and return one result
  2. client streaming: take a sequence of parameters and return one result
  3. server streaming: take one parameter and return a sequence of results
  4. duplex streaming: take a sequence of parameters and return a sequence of results
  5. one-way events: take one parameter and do not return anything

For both parameters and results, void can be used to indicate that there is no meaningful data to be passed/returned. An empty payload is sent for a void parameter or result. A method that omits its parameter has an implicit void parameter. (While both forms are legal, GetStats() is the preferred syntax over GetStats(void).)

Streaming parameters or results are indicate by using the stream keyword before the type name in either the parameter or result position: stream Operation. Note that stream void is not legal. If you need to send/return a stream of empty payloads, use a stream of some empty struct: e.g., stream bond.Void, which uses the pre-defined bond.Void struct in bond.bond.

For duplex streaming methods, the parameters and results do not have to have a 1:1 correspondence. The server is free to return results at any time during the lifetime of the method invocation.

NB: Streaming methods are currently only supported when generating C# and using Bond-over-gRPC.

Methods with a result of nothing are one-way, fire and forget methods: the service doesn’t send any response regardless of whether the service method execution resulted in success or failure. Furthmore, the client will not report transport errors to the application. There is no way to tell whether an event ever left the application process, let alone was received and processed by the serivce. This is different from methods returning void which send back a response with an empty payload and may indicate failure using errors.

service Watchdog
{
    nothing Heartbeat(Identifier);
}

Streaming parameters cannot be used with a nothing result.

Generic service

Generic services are parameterized with one or more type parameters which can be used within the service definition in any place where a concrete type could be used.

service Gateway<Token>
{
    Result Authenticate(Token);
}

The usage of a type parameter within a generic service definition may implicitly constrain what type(s) can be used to instantiate the generic service. For example in the above definition the Token type parameter must be a struct.

Custom attributes

Struct, service, enum as well as struct field and service method definitions can be annotated with custom attributes which are in effect name, string-values pairs:

[Validate("True")]
struct Example
{
    [Max("100")]
    0: uint32 value;
}

[service_id("MyExample")]
service Example
{
    [Tracing("enabled")]
    Result Method(Param);
}

Attributes are available in code generation templates and thus can be used to drive custom code generation. They are also available to applications via compile-time and runtime schema, and as Metadata argument in transforms and protocols.

See example: examples/cpp/core/attributes

Comments

Bond IDL supports C++ style comments:

/*
    Multi-line
    comment
*/
struct Example
{
    // One line comment
}

Schema AST

The compiler exposes a JSON representation of the schema Abstract Syntax Tree. The AST is intended for tools that need to access to the schema information contained in Bond IDL files with the full fidelity. The compiler can also take the JSON representation of the AST as an input, enabling tools which programmatically construct/modify Bond schemas.

Example

Given the following schema definition:

namespace example.some

struct SomeStruct
{
    0: int32 someField = 123;
}

Below is the JSON representation of the schema’s Abstract Syntax Tree generated using gbc schema example.bond command:

{
    "imports": [],
    "namespaces": [
    {
        "name": [
            "example",
            "some"
        ]
    }
    ],
    "declarations": [
    {
        "tag": "Struct",
        "declNamespaces": [
        {
            "name": [
                "example",
                "some"
            ]
        }
        ],
        "declAttributes": [],
        "declParams": [],
        "declName": "SomeStruct",
        "structFields": [
        {
            "fieldOrdinal": 0,
            "fieldType": "int32",
            "fieldName": "someField",
            "fieldDefault": {
            "value": 123,
            "type": "integer"
            }
        }
        ]
    }
    ]
}

Bond

The top level JSON object represents the parsed Bond IDL file and has the following structure:

{
    "imports": [
    ],
    "namespaces": [
    ],
    "declarations": [
    ]
}

where:

Import

Imports are represented by JSON strings. For example the following IDL:

import "bond/core/bond.bond"

is represented in the AST as:

"bond/core/bond.bond"

Namespace

The namespace declaration is represented by a JSON object:

{
    "name": [
    ]
}

where:

For example:

namespace foo.bar

is represented as:

{
    "name": [
    "foo",
    "bar"
    ]
}

Declaration

A declaration is represented by a JSON object with the following common properties:

{
    "tag": "Tag",
    "declNamespaces": [
    ],
    "declName": "Name",
    "declParams": [
    ],
    "declAttributes": [
    ]
}

where:

Struct

A JSON object representing a Struct declaration has the following properties:

{
    "tag": "Struct",
    "declNamespaces": [
    ],
    "declName": "StructName",
    "declParams": [
    ],
    "declAttributes": [
    ],
    "structBase": null,
    "structFields": [
    ]
}

where:

Service

A JSON object representing a Service declaration has the following properties:

{
    "tag": "Service",
    "declNamespaces": [
    ],
    "declName": "ServiceName",
    "declParams": [
    ],
    "declAttributes": [
    ],
    "serviceMethods": [
    ]
}

where:

Methods

A JSON object representing a Function declaration has the following properties:

{
    "tag": "Function",
    "methodName": "methodName",
    "methodAttributes": [
    ],
    "methodResult": {
    },
    "methodInput": {
    }
}

where:

A JSON object representing an Event declaration has the following properties:

{
    "tag": "Event",
    "methodName": "methodName",
    "methodAttributes": [
    ],
    "methodInput": {
    }
}

where:

Enum

A JSON object representing an Enum declaration has the following properties:

{
    "tag": "Enum",
    "declNamespaces": [
    ],
    "declName": "EnumName",
    "declAttributes": [
    ],
    "enumConstants": [
    ]
}

where:

Constant

An enum constant is represented by the following JSON object:

{
    "constantName": "ConstantName",
    "constantValue": null
}

where:

Type alias

A JSON object representing a type alias declaration has the following properties:

{
    "tag": "Alias",
    "declNamespaces": [
    ],
    "declName": "AliasName",
    "declParams": [
    ],
    "aliasType": {
    }
}

where:

Forward declaration

A JSON object representing a forward declaration has the following properties:

{
    "tag": "Forward",
    "declNamespaces": [
    ],
    "declName": "StructName",
    "declParams": [
    ]
}

Qualified name

Qualified names are represented in JSON by an array of strings. For example:

foo.bar

is represented by:

[ "foo",
    "bar"
]

Type parameter

Type parameters are represented by JSON objects with the following properties:

{
    "paramName": "T",
    "paramConstraint": null
}

where:

Attribute

Attributes are represented by JSON objects with the following properties:

{
    "attrName": [
    ],
    "attrValue": "Value"
}

where:

Struct field

A struct field is represented by a JSON object with the following properties:

{
    "fieldModifier": "Optional",
    "fieldDefault": null,
    "fieldType": {
    },
    "fieldName": "name",
    "fieldAttributes": [
    ],
    "fieldOrdinal": 0
}

where:

Field default value

A field default value is represented by a JSON object with the following properties:

{
    "type": "enum",
    "value": "Value2"
}

where:

Type

Basic types

Basic types are represented by JSON strings:

"int8"
"int16"
"int32"
"int64"
"uint8"
"uint16"
"uint32"
"uint64"
"float"
"double"
"bool"
"string"
"wstring"
"blob"

Complex types

Complex types are represented by JSON objects with a type property indicating the complex type. If the type property is one of the following: "vector", "list", "set", "nullable", "maybe", "bonded" then the object has the following structure:

{
    "type": "vector",
    "element": {
    }
}

where element is the type of the element (or the nested type).

Other complex types are:

Service method

A JSON object representing a Method has the following properties:

{
  "tag": "Tag",
  "methodName": "MethodName",
  "methodAttributes": [
  ],
  "methodResult": {
  },
  "methodInput": {
  }
}

where

Message

A JSON object representing a Message has the following properties:

{
  "messagePayload": {
  },
  "messageService": {
  }
}

where

Service type

Service type can be specified using one of the following JSON objects:

{
  "typeParam": {
  }
}

or

{
  "declaration": {
  },
  "arguments": {
  }
}

where

Runtime Schema

Bond defines SchemaDef structure to represent Bond schemas at runtime. SchemaDef is accepted as an argument by various Bond APIs. For example when transcoding binary payload into a text protocol like JSON, the SchemaDef of the payload is used to provide the necessary meta-data such as names of the fields.

Usually SchemaDef is produced at runtime using Bond APIs. However in some scenarios it may be desirable to be able to obtain SchemaDef object(s) directly from a schema definition IDL file. The compiler can generate SchemaDef serialized in Simple JSON protocol which can be deserialized using standard Bond APIs.

Example

Given the following schema definition contained a file example.bond:

namespace example.some

struct SomeStruct
{
    0: int32 someField = 123;
}

The command gbc schema --runtime-schema example.bond would produce a file named example.SomeStruct.json with the following content:

{
    "structs": [
    {
        "metadata": {
        "qualified_name": "example.some.SomeStruct",
        "name": "SomeStruct"
        },
        "fields": [
        {
            "metadata": {
            "default_value": {
                "int_value": 123
            },
            "name": "someField"
            },
            "id": 0,
            "type": {
            "id": 16
            }
        }
        ]
    }
    ]
}

Library

The Bond IDL compiler and codegen functionality is accessible programatically via the bond Haskell package. The library can be used to implement custom codegen tools. See examples/codegen in the repository.