///|
fn Schema::type_description(
self : Schema,
t : IdlType,
) -> Json raise SchemaError {
match t {
Base(name) =>
Json::object({
"typeId": (if name == "byte" { "i8" } else { name }).to_json(),
})
Named(name) => {
let (_, definition) = self.find(self.root, name)
let id = match definition {
Enumeration(_, _, _) => "enum"
Record(_, flavor, _, _) =>
if flavor == "exception" {
"exception"
} else {
"struct"
}
_ => raise InvalidSchema("not a described type")
}
let owner = definition_owner(name)
let class_name = (if owner == self.root {
""
} else {
idl_stem(owner) + "."
}) +
definition_name(definition)
Json::object({ "typeId": id.to_json(), "class": class_name.to_json() })
}
ListOf(elem) | SetOf(elem) => {
let data : Map[String, Json] = {
"typeId": (if t is ListOf(_) { "list" } else { "set" }).to_json(),
}
self.describe_type_at(data, "elemTypeId", "elemType", elem)
Json::object(data)
}
MapOf(key, value) => {
let data : Map[String, Json] = { "typeId": "map" }
self.describe_type_at(data, "keyTypeId", "keyType", key)
self.describe_type_at(data, "valueTypeId", "valueType", value)
Json::object(data)
}
}
}
///|
fn Schema::describe_type_at(
self : Schema,
data : Map[String, Json],
id_key : String,
type_key : String,
t : IdlType,
) -> Unit raise SchemaError {
let description = self.type_description(t)
if description is Object(fields) {
data[id_key] = fields["typeId"]
if fields.length() > 1 {
data[type_key] = description
}
}
}
///|
fn add_annotations(
data : Map[String, Json],
annotations : Map[String, String],
) -> Unit {
if !annotations.is_empty() {
data["annotations"] = annotations.to_json()
}
}
///|
fn Schema::describe_fields(
self : Schema,
owner : String,
fields : Array[IdlField],
) -> Json raise SchemaError {
Json::array(
fields.map(field => {
let data : Map[String, Json] = {
"key": field.id.to_json(),
"name": field.name.to_json(),
"required": field.requiredness.to_json(),
}
let t = self.resolve(owner, field.field_type, 0)
self.describe_type_at(data, "typeId", "type", t)
add_annotations(data, field.annotations)
if field.default_value is Some(value) {
data["default"] = self.const_json(owner, t, value, [], 0)
}
Json::object(data)
}),
)
}
///|
/// Canonical resolved schema description, retaining declarations and annotations.
pub fn Schema::describe(self : Schema) -> Json raise SchemaError {
let module_ = self.modules[self.root]
let enums = []
let structs = []
let aliases = []
let constants = []
let services = []
for definition in module_.definitions {
let data : Map[String, Json] = {
"name": definition_name(definition).to_json(),
}
match definition {
Alias(_, t, annotations) => {
self.describe_type_at(
data,
"typeId",
"type",
self.resolve(self.root, t, 0),
)
add_annotations(data, annotations)
aliases.push(Json::object(data))
}
Enumeration(_, values, annotations) => {
data["members"] = Json::array(
values.map(pair => {
Json::object({ "name": pair.0.to_json(), "value": pair.1.to_json() })
}),
)
add_annotations(data, annotations)
enums.push(Json::object(data))
}
Record(_, flavor, fields, annotations) => {
data["isException"] = (flavor == "exception").to_json()
data["isUnion"] = (flavor == "union").to_json()
data["fields"] = self.describe_fields(self.root, fields)
add_annotations(data, annotations)
structs.push(Json::object(data))
}
Constant(_, t, value) => {
let t = self.resolve(self.root, t, 0)
self.describe_type_at(data, "typeId", "type", t)
data["value"] = self.const_json(self.root, t, value, [], 0)
constants.push(Json::object(data))
}
Service(_, parent, functions, annotations) => {
if parent is Some(name) {
data["extends"] = name.to_json()
}
add_annotations(data, annotations)
data["functions"] = Json::array(
functions.map(function_ => {
let item : Map[String, Json] = {
"name": function_.name.to_json(),
"oneway": function_.oneway.to_json(),
"arguments": self.describe_fields(self.root, function_.arguments),
"exceptions": self.describe_fields(
self.root,
function_.exceptions,
),
}
self.describe_type_at(
item,
"returnTypeId",
"returnType",
self.resolve(self.root, function_.return_type, 0),
)
add_annotations(item, function_.annotations)
Json::object(item)
}),
)
services.push(Json::object(data))
}
}
}
Json::object({
"name": idl_stem(self.root).to_json(),
"namespaces": module_.namespaces.to_json(),
"includes": module_.includes
.map(idl_stem)
.filter(s => self.imports[self.root].contains(s))
.to_json(),
"enums": Json::array(enums),
"typedefs": Json::array(aliases),
"structs": Json::array(structs),
"constants": Json::array(constants),
"services": Json::array(services),
})
}