Skip to content

Instantly share code, notes, and snippets.

@mikedanese
Last active December 25, 2020 04:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mikedanese/f4a4cb57fdd331acf89433575a71187a to your computer and use it in GitHub Desktop.
Save mikedanese/f4a4cb57fdd331acf89433575a71187a to your computer and use it in GitHub Desktop.
types.jsonnet
local types = import 'types.jsonnet';
local mytypes = {
foo: types.define('foo', {
a: types.number,
}),
bar: types.define('bar', {
foo: mytypes.foo,
foos: types.array(mytypes.foo),
foom: types.map(mytypes.foo),
b: types.string,
}),
};
{
barobj: types.declare(mytypes.bar, {
foo: {
a: 1,
},
foos: [
{
a: 1,
},
{
a: 1,
},
],
foom: {
ka: {
a: 1,
},
kb: {
a: 1,
},
},
b: 'b',
}),
}
{
local first(fn, arr) =
std.foldl(
function(agr, elem)
if agr != null then
agr
else
fn(elem),
arr,
null
),
local check(prefix, typ, obj) =
local objtyp = std.type(obj);
local typError(str) =
'type error in %s: type %s: %s' % [prefix, typ.fqname, str];
if objtyp == 'null' then
if typ.optional then
null
else
typError('value is required')
else if typ.primitive then
if typ.fqname == objtyp then
null
else
typError("unexpected type %s" % objtyp)
else if typ.arrayType then
if objtyp != 'array' then
typError('expected array but got %s' % objtyp)
else if std.length(obj) == 0 then
null
else
first(
function(idx)
check(prefix + '[' + idx + ']',
typ.valType,
obj[idx]) tailstrict,
std.range(0, std.length(obj) - 1)
)
else if typ.mapType then
if objtyp != 'object' then
typError('expected object but got %s' % objtyp)
else
first(
function(key)
check(
prefix + '[' + key + ']',
typ.valType,
obj[key]
) tailstrict,
std.objectFields(obj)
)
else
local fieldDiff = std.setDiff(std.objectFields(obj), std.objectFields(typ.schema));
if std.length(fieldDiff) != 0 then
typError('unkown fields %s' % fieldDiff)
else
first(
function(field)
check(prefix + '.' + field,
typ.schema[field],
if std.objectHas(obj, field) then
obj[field]
else
null) tailstrict,
std.objectFields(typ.schema)
),
local base = {
arrayType: false,
mapType: false,
optional: false,
primitive: false,
},
define(name, schema)::
local typ = base {
fqname: name,
schema: schema,
check(obj)::
local res = check('', typ, obj);
if res == null then
true
else
error res,
};
typ,
declare(typ, obj):
assert typ.check(obj);
obj,
boolean:: base {
primitive: true,
fqname: 'boolean',
},
number:: base {
primitive: true,
fqname: 'number',
},
string:: base {
primitive: true,
fqname: 'string',
},
object:: base {
primitive: true,
fqname: 'object',
},
// function
array(typ)::
base {
fqname: typ.fqname + '[]',
arrayType: true,
valType: typ,
},
map(typ)::
base {
fqname: 'map[' + typ.fqname + ']',
mapType: true,
valType: typ,
},
optional(typ)::
typ {
optional: true,
},
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment