-
Notifications
You must be signed in to change notification settings - Fork 12
Interface Description
The interface description for services exported from modules or the Foundry host is represented in either JSON or YAML. Interface description consists of schemas for the types of values exchanged through service interfaces and the definition of the service interfaces themselves.
This specification uses JSON schema to describe syntax of the interface description.
Services and types are intended to be consumed across different programming languages, which have different syntatic rules and naming conventions. So in order to generate identifiers conforming to the rules in each programming language, identifiers in the interface description encode the structure of a name, rather than being a mere sequence of alphanumeric characters.
- An identifier is a sequence of words, which are separated with single
-
(hyphen) - A word is a sequence of characters picked among
_
,0
-9
, anda
-z
- If a word is considered an acronym, all occurrences of alphabets in one should be uppercase instead of
a
-z
- The first word in an identifier must not start with numbers
{
"$id": "#ident"
"type": "string",
"pattern": "^([_a-z]+|[_A-Z]+)(-([_a-z0-9]+|[_A-Z0-9]+))*$"
}
Afterwards, <ident>
will be used in place of ([_a-z]+|[_A-Z]+)(-([_a-z0-9]+|[_A-Z0-9]+))*
for clarity and brevity. But note that <ident>
is not a valid regular expression. The actual expression must be substituted in its occurrences.
Since types and services belong to a module, one can be referred to with its name within the same module. But to designate one in another moduel, a qualified name must be used.
For the purpose of referring to a type or service in interface descriptions, a qualified name for it is formed by concatenating module names from outermost to innermost and the name of the type or service all delimited by colons (:
). But note that it doesn't mean that the module names and the type or service name must be delimited so in every language.
{
"$id": "#qname"
"type": "string",
"pattern": "^<ident>(:<ident>)+$"
}
An interface description file is a module with no name. A module forms a namespace where names scoped. The outmost module is the root module and has no name. In a module, property names are names of the types or services defined in their values.
Modules may be nested. To nest a module, add a property the name of which is prefixed with a colon (:
). Then its value forms a nested module. The name of the nested module is the property name without the single colon.
{
"$id": "#module",
"type": "object",
"patternProperties": {
"^<ident>$": {
"oneOf": [
{ "$ref": "#struct" },
{ "$ref": "#enum" },
{ "$ref": "#list" },
{ "$ref": "#array" },
{ "$ref": "#tuple" },
{ "$ref": "#map" },
{ "$ref": "#service" }
]
},
"^:<ident>$": { "$ref": "#module" }
},
"additionalProperties": false
}
The following are names of primitive types supported in schema:
-
bool
: eithertrue
orfalse
-
i8
,i16
,i32
,i64
: signed integer of 8bit, 16bit, 32bit, and 64bit respectively in 2's complement -
u8
,u16
,u32
,u64
: unsigned integer of 8bit, 16bit, 32bit, and 64bit respectively -
string
: a sequence of Unicode characters
The above names may be used where a type is required in addition to the names of user defined types.
A type is designated as follows:
{
"$id": "#type_desc",
"oneOf": [
{
"type": "string",
"enum": ["bool", "i8", "i16", "i32", "i64", "u8", "u16", "u32", "u64", "string"]
},
{ "$ref": "#ident" },
{ "$ref": "#qname" }
]
}
It's either a name of a primitive type defined above or a type name defined elsewhere in the interface description.
All complex types share a common structure defined as follows:
{
"$id": "#type_commons"
"type": "object",
"properties": {
"doc": { "type": "string" }
}
}
- doc (optional): serves as documentation for the type
Struct is an aggregation of fields of different types.
{
"$id": "#struct",
"allOf": [
{
"type": "object",
"properties": {
"type": { "const": "struct" },
"fields": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": { "$ref": "#ident" },
"type": { "$ref": "#type_desc" }
}
}
}
}
},
{ "$ref": "#type_commons" }
]
}
Enum is a tagged union type. Tags are represented as identifiers, for each of which a value of differing type is associated. If type of a variant is given as an array instead of a JSON object, it's identical to usual enum of symbols.
{
"$id": "#struct",
"allOf": [
{
"type": "object",
"properties": {
"type": { "const": "enum" },
"variants": {
"oneOf": [
{
"type": "array",
"items": { "$ref": "#ident" }
},
{
"type": "object",
"propertyNames": { "$ref": "#ident" },
"additionalProperties": { "$ref": "#type_desc" }
}
]
}
}
},
{ "$ref": "#type_commons" }
]
}
A list is a sequence of values of single designated type.
{
"$id": "#list",
"allOf": [
{
"type": "object",
"properties": {
"type": { "const": "list" },
"items": { "$ref": "#type_desc" }
}
},
{ "$ref": "#type_commons" }
]
}
A sequence of a fixed number of values of single type.
{
"$id": "#array",
"allOf": [
{
"type": "object",
"properties": {
"type": { "const": "array" },
"items": { "$ref": "#type_desc" },
"size": {
"type": "integer",
"minimum": 0
}
}
},
{ "$ref": "#type_commons" }
]
}
Tuples are an aggregation of items that may be of different types. For a tuple type, the size of tuples and the type of each item are fixed.
{
"$id": "#tuple",
"allOf": [
{
"type": "object",
"properties": {
"type": { "const": "tuple" },
"items": { "$ref": "#type_desc" }
}
},
{ "$ref": "#type_commons" }
]
}
A set of key and value pairs, where keys are restricted to single type picked among string
and integer types. Values are also of single type.
Note that complex types are not allowed as keys since some languages lack such support.
{
"$id": "#map",
"allOf": [
{
"type": "object",
"properties": {
"type": { "const": "map" },
"keys": {
"type": "string",
"enum": ["i8", "i16", "i32", "i64", "u8", "u16", "u32", "u64", "string"]
},
"values": { "$ref": "#type_desc" }
},
{ "$ref": "#type_commons" }
]
}
The exact encoding of values is not specified in this specification. It's to be decided by the implementations of links among modules. In some cases, such links may not even involve
A service is a set of methods with possible state hidden behind. Description of a service doesn't involve that of any state. So it's basically similar to Java's interface.
{
"allOf": [
{
"properties": {
"extends": {
"oneOf": [
{ "$ref": "#ident" }
{ "$ref": "#qname" }
]
},
"methods": {
"type": "object",
"patternProperties": {
"^<ident>$": {
"type": "object",
"properties": {
"accepts": {
"type": "object",
"patternProperties": {
"^<ident>$": {
"type": "object",
"properties": {
"type": { "$ref": "#type_desc" },
"optional": { "type": "boolean" },
"pos": { "type": "number", "minimum": 0 }
},
"required": ["type"]
}
}
},
"returns": { "$ref": "#type_desc" },
"throws": { "$ref": "#type_desc" }
}
}
}
}
"overloads": {
"type": "object",
"patternProperties": {
"^<ident>$": {
"type": "array",
"items": { "$ref": "#ident" }
}
}
}
}
},
{ "$ref": "#type_commons" }
]
}
- extends: specifies the base service to extend. If specified, the current service becomes a subtype of the specified service type
- methods: a map of methods defined on the service. The property names are unique identifiers.
- overloads: in case language supports name overloading, the methods defined under
methods
property are grouped into overloaded names.
And each method is defined with the following properties:
- accepts: a map of parameters optionally with a position indicated with
pos
property.- type: parameter type
- optional: represents if it can be omitted
- pos: in case the parameter has a fixed position in the list of parameters.
0
based.
- returns: the type of the value to return. If omitted, means no value is returned
- throws: the type of an error value to return or throw