diff --git a/publish-local.sh b/publish-local.sh index 2dc1aac..e1ad150 100755 --- a/publish-local.sh +++ b/publish-local.sh @@ -1,3 +1,6 @@ +#!/bin/bash spacetime logout spacetime login --server-issued-login local -spacetime publish -y --server local --bin-path=zig-out/bin/stdb-zig-helloworld.wasm \ No newline at end of file +spacetime publish -y --server local --bin-path=zig-out/bin/stdb-zig-helloworld.wasm +DB_HASH=$(spacetime list 2>/dev/null | tail -1) +spacetime logs $DB_HASH \ No newline at end of file diff --git a/spacetimedb.sh b/spacetimedb.sh new file mode 100755 index 0000000..38b71dc --- /dev/null +++ b/spacetimedb.sh @@ -0,0 +1,5 @@ +#!/bin/bash +DB_HASH=$(spacetime list 2>/dev/null | tail -1) +func=$1; +shift; +spacetime "$func" $DB_HASH "$@" \ No newline at end of file diff --git a/src/main.zig b/src/main.zig index 47f7e6a..aa58f59 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,151 +1,66 @@ const std = @import("std"); const spacetime = @import("spacetime.zig"); -pub fn print(fmt: []const u8) void { - spacetime.console_log(2, null, 0, null, 0, 0, fmt.ptr, fmt.len); +pub export fn spacetime_includes() void { + _ = &spacetime.__describe_module__; + _ = &spacetime.__call_reducer__; } -const moduleTablesDef = .{ - .Person = spacetime.Table(Person){ .name = "person" }, +pub const moduleTablesDef = .{ + .Person = Person, }; -const moduleReducersDef = .{ +pub const moduleReducersDef = .{ .Init = spacetime.Reducer(Init){ .lifecycle = .Init }, .OnConnect = spacetime.Reducer(OnConnect){ .lifecycle = .OnConnect }, .OnDisconnect = spacetime.Reducer(OnDisconnect){ .lifecycle = .OnDisconnect }, - .add = spacetime.Reducer(add){ .param_names = &[_][]const u8{ "name" }}, + .add = spacetime.Reducer(add){ .param_names = &[_][:0]const u8{ "name", "age", "blah" }}, .say_hello = spacetime.Reducer(say_hello){}, }; -pub fn callReducer(comptime mdef: anytype, id: usize, args: anytype) void { - comptime var i = 0; - inline for(std.meta.fields(@TypeOf(mdef))) |field| { - if( comptime std.mem.endsWith(u8, @typeName(field.type), "spacetime_10.0__reducer_")) { - if(id == i) { - const func = @as(*const field.type, @alignCast(@ptrCast(field.default_value.?))).*.func; - if(std.meta.fields(@TypeOf(args)).len == @typeInfo(@TypeOf(func)).@"fn".params.len) { - return @call(.auto, func, args); - } - - const name: []const u8 = @as(*const field.type, @alignCast(@ptrCast(field.default_value.?))).*.name orelse field.name; - var buf: [128]u8 = undefined; - print(std.fmt.bufPrint(&buf, "invalid number of args passed to {s}, expected {} got {}", .{name, @typeInfo(@TypeOf(func)).@"fn".params.len, std.meta.fields(@TypeOf(args)).len}) catch "!!!Error while printing last error!!!"); - @panic("invalid number of args passed to func"); - } - i += 1; - } - } -} +pub const Person = spacetime.Struct(.{ + .name = "person", + .fields = &[_]spacetime.StructFieldDecl{ + .{ .name = "name", .type = .String, }, + .{ .name = "age", .type = .U32, }, + .{ .name = "blah", .type = .U64, }, + }, +}); -export fn __describe_module__(description: spacetime.BytesSink) void { - const allocator = std.heap.wasm_allocator; - print("Hello from Zig!"); - - var moduleDefBytes = std.ArrayList(u8).init(allocator); - defer moduleDefBytes.deinit(); - - spacetime.serialize_module(&moduleDefBytes, comptime spacetime.compile(moduleTablesDef, moduleReducersDef) catch |err| { - var buf: [1024]u8 = undefined; - const fmterr = std.fmt.bufPrint(&buf, "Error: {}", .{err}) catch { - @compileError("ERROR2: No Space Left! Expand error buffer size!"); - }; - @compileError(fmterr); - }) catch { - print("Allocator Error: Cannot continue!"); - @panic("Allocator Error: Cannot continue!"); - }; - - spacetime.write_to_sink(description, moduleDefBytes.items); -} - -fn readStringArg(allocator: std.mem.Allocator, args: spacetime.BytesSource) ![]const u8 { - var maxbuf: [4]u8 = undefined; - const len_buf = try spacetime.read_bytes_source(args, &maxbuf); - const len: usize = std.mem.bytesToValue(u32, len_buf); - const string_buf = try allocator.alloc(u8, len); - return try spacetime.read_bytes_source(args, string_buf); -} - -export fn __call_reducer__( - id: usize, - sender_0: u64, - sender_1: u64, - sender_2: u64, - sender_3: u64, - conn_id_0: u64, - conn_id_1: u64, - timestamp: u64, - args: spacetime.BytesSource, - err: spacetime.BytesSink, -) i16 { - const allocator = std.heap.wasm_allocator; - _ = err; - //_ = args; - - var ctx: spacetime.ReducerContext = .{ - .indentity = std.mem.bytesAsValue(u256, std.mem.sliceAsBytes(&[_]u64{ sender_0, sender_1, sender_2, sender_3})).*, - .timestamp = timestamp, - .connection_id = std.mem.bytesAsValue(u128, std.mem.sliceAsBytes(&[_]u64{ conn_id_0, conn_id_1})).*, - .db = undefined, - }; - - switch(id) { - 0...2, 4 => { - callReducer(moduleReducersDef, id, .{ &ctx }); - }, - 3 => { - //var maxbuf: [1024]u8 = undefined; - //const buf = spacetime.read_bytes_source(args, &maxbuf) catch unreachable; - //const fmtbuf = std.fmt.allocPrint(allocator, "{any}", .{buf}) catch unreachable; - //defer allocator.free(fmtbuf); - //print(fmtbuf); - //manually parse args - const name: []const u8 = readStringArg(allocator, args) catch unreachable; - callReducer(moduleReducersDef, id, .{ &ctx, name }); - }, - else => unreachable, - } - - - return 0; -} - -pub const Person = struct{ - name: []u8, -}; - -pub fn Init(_ctx: *spacetime.ReducerContext) void { +pub fn Init(ctx: *spacetime.ReducerContext) void { // Called when the module is initially published - _ = _ctx; - print("Hello, Init!"); + _ = ctx; + spacetime.print("[Init]"); } -pub fn OnConnect(_ctx: *spacetime.ReducerContext) void { +pub fn OnConnect(ctx: *spacetime.ReducerContext) void { // Called everytime a new client connects - _ = _ctx; - print("Hello, OnConnect!"); + _ = ctx; + spacetime.print("[OnConnect]"); } -pub fn OnDisconnect(_ctx: *spacetime.ReducerContext) void { +pub fn OnDisconnect(ctx: *spacetime.ReducerContext) void { // Called everytime a client disconnects - _ = _ctx; - print("Hello, OnDisconnect!"); + _ = ctx; + spacetime.print("[OnDisconnect]"); } -pub fn add(ctx: *spacetime.ReducerContext, name: []const u8) void { - //@compileLog(.{@typeInfo(@TypeOf(ctx.db))});// .person().insert(Person { name }); - _ = ctx.db.get(moduleTablesDef.Person); - //ctx.db.person().insert(Person{ .name = name }); +pub fn add(ctx: *spacetime.ReducerContext, name: []const u8, age: u32, blah: u64) void { + const personTable = ctx.*.db.get(moduleTablesDef.Person); + personTable.insert(Person{ .name = name, .age = age, .blah = blah }); + var buf: [128]u8 = undefined; - print(std.fmt.bufPrint(&buf, "Hello, add({s})!", .{ name }) catch "[add] Error: name to long"); + spacetime.print(std.fmt.bufPrint(&buf, "[add] {{{s}, {}}}!", .{ name, age }) catch "[add] Error: name to long"); } -//#[spacetimedb::reducer] -pub fn say_hello(_ctx: *spacetime.ReducerContext) void { - //for person in ctx.db.person().iter() { - // log::info!("Hello, {}!", person.name); - //} - //log::info!("Hello, World!"); - _ = _ctx; - print("Hello, World!"); +pub fn say_hello(ctx: *spacetime.ReducerContext) void { + var personIter = ctx.*.db.get(moduleTablesDef.Person).iter(); + while(personIter.next() catch { + @panic("person Iter errored!"); + }) |person| { + var buffer: [512]u8 = undefined; + const msg = std.fmt.bufPrint(&buffer, "Hello, {s} (age: {})!", .{ person.name, person.age }) catch ""; + spacetime.print(msg); + } + spacetime.print("Hello, World!"); } \ No newline at end of file diff --git a/src/spacetime.zig b/src/spacetime.zig index 4a97c2d..62cf307 100644 --- a/src/spacetime.zig +++ b/src/spacetime.zig @@ -1,37 +1,37 @@ const std = @import("std"); -pub const types = @import("spacetime/types.zig"); +pub const st_types = @import("spacetime/types.zig"); pub const serializer = @import("spacetime/serializer.zig"); -pub const SumTypeVariant = types.SumTypeVariant; -pub const SumType = types.SumType; -pub const ArrayType = types.ArrayType; -pub const AlgebraicType = types.AlgebraicType; -pub const Typespace = types.Typespace; -pub const RawIdentifier = types.RawIdentifier; -pub const AlgebraicTypeRef = types.AlgebraicTypeRef; -pub const RawIndexAlgorithm = types.RawIndexAlgorithm; -pub const RawIndexDefV9 = types.RawIndexDefV9; -pub const RawUniqueConstraintDataV9 = types.RawUniqueConstraintDataV9; -pub const RawConstraintDataV9 = types.RawConstraintDataV9; -pub const RawConstraintDefV9 = types.RawConstraintDefV9; -pub const RawSequenceDefV9 = types.RawSequenceDefV9; -pub const RawScheduleDefV9 = types.RawScheduleDefV9; -pub const TableType = types.TableType; -pub const TableAccess = types.TableAccess; -pub const RawTableDefV9 = types.RawTableDefV9; -pub const ProductTypeElement = types.ProductTypeElement; -pub const ProductType = types.ProductType; -pub const Lifecycle = types.Lifecycle; -pub const ReducerContext = types.ReducerContext; -pub const ReducerFn = types.ReducerFn; -pub const RawReducerDefV9 = types.RawReducerDefV9; -pub const RawScopedTypeNameV9 = types.RawScopedTypeNameV9; -pub const RawTypeDefV9 = types.RawTypeDefV9; -pub const RawMiscModuleExportV9 = types.RawMiscModuleExportV9; -pub const RawSql = types.RawSql; -pub const RawRowLevelSecurityDefV9 = types.RawRowLevelSecurityDefV9; -pub const RawModuleDefV9 = types.RawModuleDefV9; +pub const SumTypeVariant = st_types.SumTypeVariant; +pub const SumType = st_types.SumType; +pub const ArrayType = st_types.ArrayType; +pub const AlgebraicType = st_types.AlgebraicType; +pub const Typespace = st_types.Typespace; +pub const RawIdentifier = st_types.RawIdentifier; +pub const AlgebraicTypeRef = st_types.AlgebraicTypeRef; +pub const RawIndexAlgorithm = st_types.RawIndexAlgorithm; +pub const RawIndexDefV9 = st_types.RawIndexDefV9; +pub const RawUniqueConstraintDataV9 = st_types.RawUniqueConstraintDataV9; +pub const RawConstraintDataV9 = st_types.RawConstraintDataV9; +pub const RawConstraintDefV9 = st_types.RawConstraintDefV9; +pub const RawSequenceDefV9 = st_types.RawSequenceDefV9; +pub const RawScheduleDefV9 = st_types.RawScheduleDefV9; +pub const TableType = st_types.TableType; +pub const TableAccess = st_types.TableAccess; +pub const RawTableDefV9 = st_types.RawTableDefV9; +pub const ProductTypeElement = st_types.ProductTypeElement; +pub const ProductType = st_types.ProductType; +pub const Lifecycle = st_types.Lifecycle; +pub const ReducerContext = st_types.ReducerContext; +pub const ReducerFn = st_types.ReducerFn; +pub const RawReducerDefV9 = st_types.RawReducerDefV9; +pub const RawScopedTypeNameV9 = st_types.RawScopedTypeNameV9; +pub const RawTypeDefV9 = st_types.RawTypeDefV9; +pub const RawMiscModuleExportV9 = st_types.RawMiscModuleExportV9; +pub const RawSql = st_types.RawSql; +pub const RawRowLevelSecurityDefV9 = st_types.RawRowLevelSecurityDefV9; +pub const RawModuleDefV9 = st_types.RawModuleDefV9; pub const serialize_module = serializer.serialize_module; @@ -46,16 +46,31 @@ pub extern "spacetime_10.0" fn console_log( message_len: usize, ) void; +pub fn print(fmt: []const u8) void { + console_log(2, null, 0, null, 0, 0, fmt.ptr, fmt.len); +} + pub const BytesSink = extern struct { inner: u32 }; pub const BytesSource = extern struct { inner: u32 }; pub extern "spacetime_10.0" fn bytes_sink_write(sink: BytesSink, buffer_ptr: [*c]const u8, buffer_len_ptr: *usize) u16; pub extern "spacetime_10.0" fn bytes_source_read(source: BytesSource, buffer_ptr: [*c]u8, buffer_len_ptr: *usize) i16; -const NO_SUCH_BYTES = 8; -const NO_SPACE = 9; +pub const TableId = extern struct { _inner: u32, }; +pub extern "spacetime_10.0" fn table_id_from_name(name: [*c]const u8, name_len: usize, out: *TableId) u16; +pub extern "spacetime_10.0" fn datastore_insert_bsatn(table_id: TableId, row_ptr: [*c]const u8, row_len_ptr: *usize) u16; + +pub const RowIter = extern struct { _inner: u32, pub const INVALID = RowIter{ ._inner = 0}; }; +pub extern "spacetime_10.0" fn row_iter_bsatn_advance(iter: RowIter, buffer_ptr: [*c]u8, buffer_len_ptr: *usize) i16; +pub extern "spacetime_10.0" fn datastore_table_scan_bsatn(table_id: TableId, out: [*c]RowIter) u16; + +pub const EXHAUSTED = -1; +pub const OK = 0; +pub const NO_SUCH_ITER = 6; +pub const NO_SUCH_BYTES = 8; +pub const NO_SPACE = 9; +pub const BUFFER_TOO_SMALL = 11; -/// Read `source` from the host fully into `buf`. pub fn read_bytes_source(source: BytesSource, buf: []u8) ![]u8 { const INVALID: i16 = NO_SUCH_BYTES; @@ -63,7 +78,7 @@ pub fn read_bytes_source(source: BytesSource, buf: []u8) ![]u8 { const ret = bytes_source_read(source, @ptrCast(buf), &buf_len); switch(ret) { -1, 0 => {}, - INVALID => @panic("invalid source passed"), + INVALID => return error.InvalidSource, else => unreachable, } @@ -76,7 +91,6 @@ pub fn write_to_sink(sink: BytesSink, _buf: []const u8) void { const len: *usize = &buf.len; switch(bytes_sink_write(sink, buf.ptr, len)) { 0 => { - // Set `buf` to remainder and bail if it's empty. buf = buf[len.*..]; if(buf.len == 0) { break; @@ -89,40 +103,6 @@ pub fn write_to_sink(sink: BytesSink, _buf: []const u8) void { } } -// pub fn parse_reducers(root: type) []const RawReducerDefV9 { -// const decls = std.meta.declarations(root); -// //@compileLog(.{ decls }); - -// var reducers : []const RawReducerDefV9 = &[_]RawReducerDefV9{}; -// _ = &reducers; - -// inline for(decls) |decl| { - -// const temp = @field(root, decl.name); -// const temp_type = @typeInfo(@TypeOf(temp)); -// if(temp_type != .@"fn") continue; -// if(temp_type.@"fn".params[0].type.? != *ReducerContext) continue; - -// const lifecycle: ?Lifecycle = blk: { -// if(std.mem.eql(u8, decl.name, "Init")) break :blk .Init; -// if(std.mem.eql(u8, decl.name, "OnConnect")) break :blk .OnConnect; -// if(std.mem.eql(u8, decl.name, "OnDisconnect")) break :blk .OnDisconnect; -// break :blk null; -// }; - -// reducers = reducers ++ &[_]RawReducerDefV9{ -// .{ -// .name = decl.name, -// .params = .{ .elements = &[_]ProductTypeElement{} }, -// .lifecycle = lifecycle, -// }, -// }; - -// } - -// return reducers; -// } - pub const Param = struct { name: []const u8, }; @@ -132,29 +112,107 @@ pub fn Reducer(comptime func: anytype) type { name: ?[]const u8 = null, func: @TypeOf(func) = func, lifecycle: ?Lifecycle = null, - param_names: []const []const u8 = &[_][]const u8{}, + param_names: []const [:0]const u8 = &[_][:0]const u8{}, }; - //@compileLog(.{@TypeOf(func)}); - return @"spacetime_10.0__reducer_"; } -pub fn Table(comptime table: anytype) type { - _ = table; +pub const StructFieldDecl = struct { + name: [:0]const u8, + type: AlgebraicType, + isPrimaryKey: bool = false, + autoInc: bool = false, +}; + +pub const StructDecl = struct { + name: []const u8, + fields: []const StructFieldDecl, +}; + +fn spacetimeType2ZigType(t: AlgebraicType) type { + return switch (t) { + .String => []const u8, + .U32 => u32, + .U64 => u64, + else => unreachable, + }; +} + +pub fn Struct(comptime decl: StructDecl) type { const @"spacetime_10.0__table_" = struct { - name: ?[]const u8 = null, + name: ?[]const u8 = decl.name, table_type: TableType = .User, table_access: TableAccess = .Private, }; - return @"spacetime_10.0__table_"; + + var zigStructMembers: []const std.builtin.Type.StructField = &[_]std.builtin.Type.StructField{ + std.builtin.Type.StructField{ + .name = "spacetime_10.0__table_", + .type = @"spacetime_10.0__table_", + .default_value = @as(?*const anyopaque, &@"spacetime_10.0__table_"{}), + .is_comptime = false, + .alignment = 0, + }, + }; + + inline for(decl.fields) |field| { + zigStructMembers = zigStructMembers ++ &[_]std.builtin.Type.StructField{ + std.builtin.Type.StructField{ + .name = field.name, + .type = spacetimeType2ZigType(field.type), + .default_value = null, + .is_comptime = false, + .alignment = 0, + }, + }; + } + + return @Type(.{ + .@"struct" = std.builtin.Type.Struct{ + .decls = &[_]std.builtin.Type.Declaration{}, + .fields = zigStructMembers, + .is_tuple = false, + .layout = .@"auto", + }, + }); } -fn zigTypeToSpacetimeType(comptime param: ?type) !AlgebraicType { +pub fn readArg(allocator: std.mem.Allocator, args: BytesSource, comptime t: AlgebraicType) !spacetimeType2ZigType(t) { + switch(t) { + .String => { + var maxbuf: [4]u8 = undefined; + const len_buf = try read_bytes_source(args, &maxbuf); + const len: usize = std.mem.bytesToValue(u32, len_buf); + const string_buf = try allocator.alloc(u8, len); + return try read_bytes_source(args, string_buf); + }, + .U32 => { + var maxbuf: [4]u8 = undefined; + const len_buf = try read_bytes_source(args, &maxbuf); + const len: u32 = std.mem.bytesToValue(u32, len_buf); + return len; + }, + .U64 => { + var maxbuf: [8]u8 = undefined; + const len_buf = try read_bytes_source(args, &maxbuf); + const len: u64 = std.mem.bytesToValue(u64, len_buf); + return len; + }, + else => @compileError("unsupported type in readArg!"), + } +} + +pub fn zigTypeToSpacetimeType(comptime param: ?type) AlgebraicType { if(param == null) @compileError("Null parameter type passed to zigParamsToSpacetimeParams"); return switch(param.?) { []const u8 => .{ .String = {} }, - else => error.ZigTypeNotSupported, + u32 => .{ .U32 = {}, }, + u64 => .{ .U64 = {}, }, + else => { + @compileLog(param.?); + @compileError("Unmatched type passed to zigTypeToSpacetimeType!"); + }, }; } @@ -162,32 +220,74 @@ pub fn compile(comptime moduleTables : anytype, comptime moduleReducers : anytyp var def : RawModuleDefV9 = undefined; _ = &def; - //def.reducers = def.reducers ++ &[_]RawReducerDefV9{}; var tables: []const RawTableDefV9 = &[_]RawTableDefV9{}; var reducers: []const RawReducerDefV9 = &[_]RawReducerDefV9{}; + var raw_types: []const AlgebraicType = &[_]AlgebraicType{}; + var types: []const RawTypeDefV9 = &[_]RawTypeDefV9{}; + inline for(std.meta.fields(@TypeOf(moduleTables))) |field| { const default_values = @as(*const field.type, @alignCast(@ptrCast(field.default_value.?))).*; - const name: []const u8 = default_values.name orelse field.name; - if( std.mem.endsWith(u8, @typeName(field.type), "spacetime_10.0__table_")) { - const table_type: TableType = default_values.table_type; - const table_access: TableAccess = default_values.table_access; - tables = tables ++ &[_]RawTableDefV9{ + const structInfo = blk: { + for(@typeInfo(default_values).@"struct".fields) |structInfoField| { + if(std.mem.eql(u8, structInfoField.name, "spacetime_10.0__table_")) { + break :blk structInfoField.type{}; + } + } + }; + + var product_type_ref: AlgebraicTypeRef = undefined; + { + var product_elements: []const ProductTypeElement = &[_]ProductTypeElement{}; + + inline for(@typeInfo(default_values).@"struct".fields, 0..) |table_field, i| { + if(i == 0) continue; + product_elements = product_elements ++ &[_]ProductTypeElement{ + .{ + .name = table_field.name, + .algebraic_type = zigTypeToSpacetimeType(table_field.type) + } + }; + } + + raw_types = raw_types ++ &[_]AlgebraicType{ .{ - .name = name, - .product_type_ref = .{ .inner = 0, }, - .primary_key = &[_]u16{}, - .indexes = &[_]RawIndexDefV9{}, - .constraints = &[_]RawConstraintDefV9{}, - .sequences = &[_]RawSequenceDefV9{}, - .schedule = null, - .table_type = table_type, - .table_access = table_access, + .Product = .{ + .elements = product_elements, + } + }, + }; + + types = types ++ &[_]RawTypeDefV9{ + .{ + .name = .{ + .scope = &[_][]u8{}, + .name = field.name + }, + .ty = .{ .inner = raw_types.len-1, }, + .custom_ordering = true, } }; - continue; + + product_type_ref.inner = types.len-1; } - @compileLog(.{ field }); + + const name: []const u8 = structInfo.name.?; + const table_type: TableType = structInfo.table_type; + const table_access: TableAccess = structInfo.table_access; + tables = tables ++ &[_]RawTableDefV9{ + .{ + .name = name, + .product_type_ref = product_type_ref, + .primary_key = &[_]u16{}, + .indexes = &[_]RawIndexDefV9{}, + .constraints = &[_]RawConstraintDefV9{}, + .sequences = &[_]RawSequenceDefV9{}, + .schedule = null, + .table_type = table_type, + .table_access = table_access, + } + }; } inline for(std.meta.fields(@TypeOf(moduleReducers))) |field| { @@ -196,17 +296,14 @@ pub fn compile(comptime moduleTables : anytype, comptime moduleReducers : anytyp if( std.mem.endsWith(u8, @typeName(field.type), "spacetime_10.0__reducer_")) { const lifecycle: ?Lifecycle = default_values.lifecycle; - //parse the arguments var params: []const ProductTypeElement = &[_]ProductTypeElement{}; const param_names = default_values.param_names; - //@compileLog(.{@typeInfo(@TypeOf(default_values.func)).@"fn"}); for(@typeInfo(@TypeOf(default_values.func)).@"fn".params[1..], param_names) |param, param_name| { - //@compileLog(.{ name, param }); params = params ++ &[_]ProductTypeElement{ .{ .name = param_name, - .algebraic_type = try zigTypeToSpacetimeType(param.type), + .algebraic_type = zigTypeToSpacetimeType(param.type), } }; } @@ -225,32 +322,161 @@ pub fn compile(comptime moduleTables : anytype, comptime moduleReducers : anytyp return .{ .typespace = .{ - .types = &[_]AlgebraicType{ - .{ - .Product = .{ - .elements = &[_]ProductTypeElement{ - .{ - .name = "name", - .algebraic_type = .String, - } - } - } - }, - }, + .types = raw_types, }, .tables = tables, .reducers = reducers, - .types = &[_]RawTypeDefV9{ - .{ - .name = .{ - .scope = &[_][]u8{}, - .name = "Person" - }, - .ty = .{ .inner = 0, }, - .custom_ordering = true, - } - }, + .types = types, .misc_exports = &[_]RawMiscModuleExportV9{}, .row_level_security = &[_]RawRowLevelSecurityDefV9{}, }; -} \ No newline at end of file +} + +pub fn callReducer(comptime mdef: anytype, id: usize, args: anytype) void { + comptime var i = 0; + inline for(std.meta.fields(@TypeOf(mdef))) |field| { + if( comptime std.mem.endsWith(u8, @typeName(field.type), "spacetime_10.0__reducer_")) { + if(id == i) { + const func = @as(*const field.type, @alignCast(@ptrCast(field.default_value.?))).*.func; + if(std.meta.fields(@TypeOf(args)).len == @typeInfo(@TypeOf(func)).@"fn".params.len) { + return @call(.auto, func, args); + } + + const name: []const u8 = @as(*const field.type, @alignCast(@ptrCast(field.default_value.?))).*.name orelse field.name; + var buf: [128]u8 = undefined; + print(std.fmt.bufPrint(&buf, "invalid number of args passed to {s}, expected {} got {}", .{name, @typeInfo(@TypeOf(func)).@"fn".params.len, std.meta.fields(@TypeOf(args)).len}) catch "!!!Error while printing last error!!!"); + @panic("invalid number of args passed to func"); + } + i += 1; + } + } +} + +const moduleTablesDef = @import("root").moduleTablesDef; +const moduleReducersDef = @import("root").moduleReducersDef; + +pub export fn __describe_module__(description: BytesSink) void { + const allocator = std.heap.wasm_allocator; + print("Hello from Zig!"); + + var moduleDefBytes = std.ArrayList(u8).init(allocator); + defer moduleDefBytes.deinit(); + + serialize_module(&moduleDefBytes, comptime compile(moduleTablesDef, moduleReducersDef) catch |err| { + var buf: [1024]u8 = undefined; + const fmterr = std.fmt.bufPrint(&buf, "Error: {}", .{err}) catch { + @compileError("ERROR2: No Space Left! Expand error buffer size!"); + }; + @compileError(fmterr); + }) catch { + print("Allocator Error: Cannot continue!"); + @panic("Allocator Error: Cannot continue!"); + }; + + write_to_sink(description, moduleDefBytes.items); +} + +fn itoa(comptime value: anytype) [:0]const u8 { + comptime var s: []const u8 = &[_]u8{}; + comptime var n = value; + if (n == 0) { + s = s ++ .{'0'}; + } else { + comptime while (n != 0) { + s = s ++ .{'0' + (n % 10)}; + n = n / 10; + }; + } + return @ptrCast(s ++ .{0}); +} + +pub export fn __call_reducer__( + id: usize, + sender_0: u64, + sender_1: u64, + sender_2: u64, + sender_3: u64, + conn_id_0: u64, + conn_id_1: u64, + timestamp: u64, + args: BytesSource, + err: BytesSink, +) i16 { + _ = err; + + const allocator = std.heap.wasm_allocator; + + var ctx: ReducerContext = .{ + .indentity = std.mem.bytesAsValue(u256, std.mem.sliceAsBytes(&[_]u64{ sender_0, sender_1, sender_2, sender_3})).*, + .timestamp = timestamp, + .connection_id = std.mem.bytesAsValue(u128, std.mem.sliceAsBytes(&[_]u64{ conn_id_0, conn_id_1})).*, + .db = .{ + .allocator = allocator, + }, + }; + + var i: usize = 0; + inline for(std.meta.fields(@TypeOf(moduleReducersDef))) |field| { + if( comptime std.mem.endsWith(u8, @typeName(field.type), "spacetime_10.0__reducer_")) { + defer i += 1; + if(id == i) { + const func = std.meta.fields(field.type)[std.meta.fieldIndex(field.type, "func").?].type; + const params = @typeInfo(func).@"fn".params; + const param_names = @field(moduleReducersDef, field.name).param_names; + comptime var argCount = 1; + comptime var argList: []const std.builtin.Type.StructField = &[_]std.builtin.Type.StructField{ + std.builtin.Type.StructField{ + .alignment = 0, + .default_value = null, + .is_comptime = false, + .name = "0", + .type = *ReducerContext, + } + }; + + inline for(params[1..], param_names) |param, name| { + _ = name; + argList = argList ++ &[_]std.builtin.Type.StructField{ + std.builtin.Type.StructField{ + .alignment = 0, + .default_value = null, + .is_comptime = false, + .name = comptime itoa(argCount), + .type = param.type.?, + } + }; + argCount += 1; + } + + const argsStruct = @Type(.{ + .@"struct" = std.builtin.Type.Struct{ + .backing_integer = null, + .decls = &[_]std.builtin.Type.Declaration{}, + .fields = argList, + .is_tuple = true, + .layout = .auto, + } + }); + + var constructedArg: argsStruct = undefined; + + @field(constructedArg, "0") = &ctx; + + if(args.inner != 0) { + inline for(params, 0..) |param, name| { + comptime if(name == 0) continue; + @field(constructedArg, itoa(name)) = readArg(allocator, args, zigTypeToSpacetimeType(param.type.?)) catch |err2| { + var buf: [512]u8 = undefined; + print(std.fmt.bufPrint(&buf, "Error: {}", .{err2}) catch "Expand Error Buffer!"); + @panic("blah"); + }; + } + } + + callReducer(moduleReducersDef, i, constructedArg); + } + } + } + + return 0; +} diff --git a/src/spacetime/types.zig b/src/spacetime/types.zig index eaea723..56fed25 100644 --- a/src/spacetime/types.zig +++ b/src/spacetime/types.zig @@ -1,4 +1,7 @@ const std = @import("std"); +const spacetime = @import("../spacetime.zig"); +const console_log = spacetime.console_log; +const TableId = spacetime.TableId; pub const Str = []const u8; @@ -12,7 +15,6 @@ pub const SumType = struct { }; pub const ArrayType = struct { - /// The base type every element of the array has. elem_ty: []const AlgebraicType, }; @@ -126,18 +128,181 @@ pub const Lifecycle = enum { OnDisconnect, }; -pub fn Table2Struct(comptime table_type: type) type { - _ = table_type; - return struct { +pub fn StructSerializer(struct_type: type) fn(std.mem.Allocator, struct_type) std.mem.Allocator.Error![]u8 { + const @"spacetime_10.0__table_" = std.meta.fields(struct_type)[std.meta.fieldIndex(struct_type, "spacetime_10.0__table_").?].type; + + return struct { + pub fn serialize(allocator: std.mem.Allocator, data: struct_type) ![]u8 { + const fields = std.meta.fields(@TypeOf(data)); + var size: usize = 0; + inline for(fields) |field| { + switch(field.type) { + []const u8 => { + const val = @field(data, field.name); + size += 4 + val.len; + }, + u32 => { + size += 4; + }, + u64 => { + size += 8; + }, + @"spacetime_10.0__table_" => {}, + else => { + @compileLog(field.type); + @compileError("Unsupported type in StructSerializer"); + }, + } + } + const mem = try allocator.alloc(u8, size); + var offset_mem = mem; + inline for(fields) |field| { + switch(field.type) { + []const u8 => { + const val = @field(data, field.name); + std.mem.bytesAsValue(u32, offset_mem[0..4]).* = val.len; + std.mem.copyForwards(u8, offset_mem[4..], val); + offset_mem = offset_mem[4 + val.len ..]; + }, + u32 => { + const val = @field(data, field.name); + std.mem.bytesAsValue(u32, offset_mem[0..4]).* = val; + offset_mem = offset_mem[4..]; + }, + u64 => { + const val = @field(data, field.name); + std.mem.bytesAsValue(u64, offset_mem[0..4]).* = val; + offset_mem = offset_mem[8..]; + }, + @"spacetime_10.0__table_" => {}, + else => @compileError("Unsupported type in StructSerializer"), + } + } + return mem; + } + }.serialize; +} + +pub fn StructDeserializer(struct_type: type) fn(allocator: std.mem.Allocator, *[]const u8) std.mem.Allocator.Error!*struct_type { + + const @"spacetime_10.0__table_" = std.meta.fields(struct_type)[std.meta.fieldIndex(struct_type, "spacetime_10.0__table_").?].type; + + return struct { + pub fn deserialize(allocator: std.mem.Allocator, data: *[]const u8) std.mem.Allocator.Error!*struct_type { + const ret = try allocator.create(struct_type); + var offset_mem = data.*; + const fields = std.meta.fields(struct_type); + inline for(fields) |field| { + switch(field.type) { + []const u8 => { + const len = std.mem.bytesAsValue(u32, offset_mem[0..4]).*; + const str = try allocator.dupe(u8, offset_mem[4..(4+len)]); + @field(ret.*, field.name) = str; + offset_mem = offset_mem[4+len ..]; + }, + u32 => { + @field(ret.*, field.name) = std.mem.bytesAsValue(u32, offset_mem[0..4]).*; + offset_mem = offset_mem[4..]; + }, + u64 => { + @field(ret.*, field.name) = std.mem.bytesAsValue(u64, offset_mem[0..4]).*; + offset_mem = offset_mem[8..]; + }, + @"spacetime_10.0__table_" => {}, + else => @compileError("Unsupported type in StructDeserializer"), + } + } + data.* = offset_mem; + return ret; + } + }.deserialize; +} + +pub fn Table2Struct(comptime table_type: type) type { + + const fields = std.meta.fields(table_type); + const field = fields[std.meta.fieldIndex(table_type, "spacetime_10.0__table_").?]; + const struct_type = @as(*const field.type, @alignCast(@ptrCast(field.default_value.?))).*; + const table_name: []const u8 = struct_type.name.?; + + return struct { + allocator: std.mem.Allocator, + + pub const Iter = struct { + allocator: std.mem.Allocator, + handle: spacetime.RowIter, + buffer: [0x20_000]u8 = undefined, + contents: []u8 = undefined, + last_ret: i16 = spacetime.OK, + + pub fn next(self: *@This()) !?*table_type { + var buffer_len: usize = undefined; + while(true) + { + var ret = self.last_ret; + if(self.contents.len == 0) { + if(self.handle._inner == spacetime.RowIter.INVALID._inner) { + return null; + } + buffer_len = self.buffer.len; + ret = spacetime.row_iter_bsatn_advance(self.handle, @constCast(@ptrCast(&self.buffer)), &buffer_len); + self.contents = self.buffer[0..buffer_len]; + + if(ret == spacetime.EXHAUSTED) { + self.handle = spacetime.RowIter.INVALID; + } + } + + switch(ret) { + spacetime.EXHAUSTED, spacetime.OK => { + return StructDeserializer(table_type)(self.allocator, &self.contents); + }, + spacetime.BUFFER_TOO_SMALL => { + return error.BUFFER_TOO_SMALL; + }, + spacetime.NO_SUCH_ITER => { + return error.NO_SUCH_ITER; + }, + else => { + var buffer: [512]u8 = undefined; + const msg = try std.fmt.bufPrint(&buffer, "Iter Err: {}!", .{ ret }); + console_log(2, null, 0, null, 0, 0, msg.ptr, msg.len); + @panic("Fix Me!"); + } + } + } + } + }; + + pub fn insert(self: @This(), data: table_type) void { + var id: TableId = undefined; + _ = spacetime.table_id_from_name(table_name.ptr, table_name.len, &id); + const raw_data = StructSerializer(table_type)(self.allocator, data) catch return; + defer self.allocator.free(raw_data); + var raw_data_len: usize = raw_data.len; + _ = spacetime.datastore_insert_bsatn(id, raw_data.ptr, &raw_data_len); + } + + pub fn iter(self: @This()) Iter { + var id: TableId = undefined; + _ = spacetime.table_id_from_name(table_name.ptr, table_name.len, &id); + var rowIter: spacetime.RowIter = undefined; + _ = spacetime.datastore_table_scan_bsatn(id, &rowIter); + return Iter{ + .allocator = self.allocator, + .handle = rowIter, + }; + } }; } pub const Local = struct { - pub fn get(self: @This(), table: anytype) Table2Struct(@TypeOf(table)) { - _ = self; + allocator: std.mem.Allocator, + + pub fn get(self: @This(), table: anytype) Table2Struct(table) { return .{ - //.name = "blahsss", + .allocator = self.allocator, }; } };