{
	"$schema": "https://json-schema.org/draft/2020-12/schema",
	"$id": "https://schema.thi.ng/wasm-api/20221111.json",
	"title": "WASM API code generator type specs",
	"description": "Polyglot type definition for hybrid WASM/TypeScript applications",
	"type": "array",
	"items": {
		"type": "object",
		"oneOf": [
			{ "$ref": "#/$defs/enum" },
			{ "$ref": "#/$defs/ext" },
			{ "$ref": "#/$defs/funcptr" },
			{ "$ref": "#/$defs/struct" },
			{ "$ref": "#/$defs/union" }
		]
	},
	"$defs": {
		"enum": {
			"type": "object",
			"properties": {
				"body": { "$ref": "#/$defs/body" },
				"doc": { "$ref": "#/$defs/doc" },
				"name": { "$ref": "#/$defs/name" },
				"skip": { "$ref": "#/$defs/skip" },
				"type": {
					"const": "enum",
					"description": "This spec describes an `enum`-style type."
				},
				"tag": {
					"$ref": "#/$defs/int",
					"description": "Optional integer type to use. Defaults to `i32` Note: If C ABI compatibility is required, then `i32` MUST be used."
				},
				"values": {
					"type": "array",
					"items": {
						"oneOf": [
							{ "type": "string" },
							{
								"type": "object",
								"properties": {
									"name": { "type": "string" },
									"value": {
										"type": "integer",
										"description": "Integer value associated with this name"
									},
									"doc": { "$ref": "#/$defs/doc" }
								},
								"required": ["name"]
							}
						],
						"description": "Names should be given in lowercase. Some code generators can be configured to auto-capitalize these names."
					},
					"title": "Possible enum names",
					"description": "Nonconsecutive or specific values are described via objects of `name`/`value` pairs."
				}
			},
			"required": ["name", "type", "values"]
		},
		"ext": {
			"type": "object",
			"properties": {
				"doc": { "$ref": "#/$defs/doc" },
				"name": { "$ref": "#/$defs/name" },
				"type": {
					"const": "ext",
					"description": "This spec describes an external type."
				},
				"align": {
					"type": "integer",
					"description": "Type alignment (in bytes)"
				},
				"size": {
					"type": "integer",
					"description": "Type size (in bytes)"
				}
			},
			"required": ["name", "type", "align", "size"]
		},
		"funcptr": {
			"type": "object",
			"properties": {
				"body": { "$ref": "#/$defs/body" },
				"doc": { "$ref": "#/$defs/doc" },
				"name": { "$ref": "#/$defs/name" },
				"skip": { "$ref": "#/$defs/skip" },
				"type": {
					"const": "funcptr",
					"description": "This spec describes a function pointer type."
				},
				"rtype": {
					"anyOf": [
						{ "const": "void" },
						{ "$ref": "#/$defs/prim" },
						{ "type": "string" }
					],
					"title": "Function return type",
					"$ref": "#/$defs/docArgs"
				},
				"args": {
					"type": "array",
					"items": { "$ref": "#/$defs/field" },
					"title": "Function arguments",
					"$ref": "#/$defs/docArgs"
				}
			},
			"required": ["name", "type", "rtype", "args"]
		},
		"struct": {
			"type": "object",
			"properties": {
				"body": { "$ref": "#/$defs/body" },
				"doc": { "$ref": "#/$defs/doc" },
				"fields": { "$ref": "#/$defs/fields" },
				"name": { "$ref": "#/$defs/name" },
				"skip": { "$ref": "#/$defs/skip" },
				"type": {
					"const": "struct",
					"description": "This spec describes a struct-style type."
				}
			}
		},
		"union": {
			"type": "object",
			"properties": {
				"body": { "$ref": "#/$defs/body" },
				"doc": { "$ref": "#/$defs/doc" },
				"fields": { "$ref": "#/$defs/fields" },
				"name": { "$ref": "#/$defs/name" },
				"skip": { "$ref": "#/$defs/skip" },
				"type": {
					"const": "union",
					"description": "This spec describes a union type."
				}
			}
		},
		"int": {
			"enum": [
				"i8",
				"u8",
				"i16",
				"u16",
				"i32",
				"u32",
				"i64",
				"u64",
				"isize",
				"usize"
			]
		},
		"prim": {
			"oneOf": [
				{ "$ref": "#/$defs/int" },
				{
					"enum": ["f32", "f64"]
				}
			]
		},
		"stringOrArray": {
			"oneOf": [
				{ "type": "string" },
				{
					"type": "array",
					"items": { "type": "string" }
				}
			],
			"description": "Each line will be automatically word-wrapped and prefixed with the appropriate comment syntax of the target language."
		},
		"name": {
			"type": "string",
			"title": "Type or field name",
			"description": "This name will be used as-is by all code generators and therefore should ensure to not violate naming rules of any of the intended target languages."
		},
		"doc": {
			"$ref": "#/$defs/stringOrArray",
			"title": "Docstring for this type or field."
		},
		"docArgs": {
			"description": "With a few exceptions & language specific limitations, the range of possibilities is similar to field types."
		},
		"field": {
			"type": "object",
			"description": "Type spec for a single field within a struct or union. If `pad` is given, all other keys are ignored and the field is only considered for padding/alignment/sizing purposes. Names for padding fields will be autogenerated.",
			"properties": {
				"name": { "$ref": "#/$defs/name" },
				"doc": { "$ref": "#/$defs/doc" },
				"type": { "$ref": "#/$defs/fieldType" },
				"tag": { "$ref": "#/$defs/fieldTag" },
				"const": {
					"type": "boolean",
					"description": "Marks type as constant / readonly data. Note: For pointers this refers to the target data, not to the pointer itself."
				},
				"optional": {
					"type": "boolean",
					"description": "Marks type as optional. Language specific and currently only supported for pointer types."
				},
				"sentinel": {
					"type": "integer",
					"description": "Optional value for sentinel-terminated arrays/pointers"
				},
				"pad": {
					"type": "integer",
					"minimum": 1,
					"description": "Number of padding bytes. If given, all other keys are ignored."
				},
				"default": {
					"description": "Optional default value (or verbatim source code). If given as object, language specific defaults can be provided.",
					"oneOf": [
						{ "type": "number" },
						{ "type": "string" },
						{
							"type": "object",
							"additionalProperties": {
								"oneOf": [
									{ "type": "number" },
									{ "type": "string" }
								]
							}
						}
					]
				},
				"skip": {
					"type": "boolean",
					"default": false,
					"description": "If true, code generation of this field will be skipped for WASM host environment languages (e.g. TypeScript)"
				},
				"getter": {
					"type": "boolean",
					"default": true,
					"description": "If false, omits getter for languages which would usually define one (e.g. TypeScript)"
				},
				"setter": {
					"type": "boolean",
					"default": true,
					"description": "If false, omits setter for languages which would usually define one (e.g. TypeScript)"
				}
			},
			"required": ["name", "type"],
			"if": {
				"properties": {
					"tag": {
						"oneOf": [{ "const": "array" }, { "const": "vec" }]
					}
				},
				"required": ["tag"]
			},
			"then": {
				"properties": {
					"len": {
						"type": "integer",
						"minimum": 1,
						"default": 1,
						"description": "Array or vector length"
					}
				},
				"required": ["len"]
			}
		},
		"fieldType": {
			"anyOf": [
				{ "$ref": "#/$defs/prim" },
				{
					"enum": ["opaque", "string"]
				},
				{
					"type": "string"
				}
			],
			"title": "Field type",
			"description": "Either a WASM primitive type, a preset name (i.e. `opaque` or `string`) or the name of another type in this document.\n\n- `isize` / `usize` types will be sized depending on chosen WASM target (wasm32/wasm64).\n- `opaque` types are always considered pointers only.\n- `string` types are language specific and the result type can be configured via codegen options (i.e. slice vs. raw pointer)."
		},
		"fieldTag": {
			"enum": ["array", "ptr", "single", "slice", "vec"],
			"default": "single",
			"title": "Optional array type modifier",
			"description": "If `array` or `vec` is selected, the `len` MUST be given too. Note: Not all tags are natively supported by all languages and might result in generation of \"polyfills\". E.g. vectors are similar to arrays, but have stricter alignment & potentially different codegeneration (language specific)."
		},
		"fields": {
			"type": "array",
			"items": { "$ref": "#/$defs/field" },
			"description": "Array of fields specs for a struct or union type"
		},
		"body": {
			"type": "object",
			"title": "Optional custom per-language user code injections",
			"description": "Keys of this object are target language IDs and values are source code (will be automatically indented). Use object values (with `decl` and `impl` keys) for languages requiring multiple injection points for declaration vs. implementation (e.g. TypeScript).",
			"additionalProperties": {
				"oneOf": [
					{ "$ref": "#/$defs/stringOrArray" },
					{
						"type": "object",
						"properties": {
							"decl": {
								"$ref": "#/$defs/stringOrArray",
								"title": "User code for additional declarations"
							},
							"impl": {
								"$ref": "#/$defs/stringOrArray",
								"title": "User code for additional implementations"
							}
						},
						"description": "The `decl` and `impl` keys can be used for languages requiring multiple injection points for declarations (e.g. interface additions) vs. implementation methods (e.g. within a struct)"
					}
				]
			}
		},
		"skip": {
			"type": "array",
			"items": { "type": "string" },
			"description": "List of language IDs for which code generation of this type should be skipped."
		}
	}
}
