165 lines
5.1 KiB
Zig
165 lines
5.1 KiB
Zig
const std = @import("std");
|
|
const leveldb = @import("leveldb");
|
|
const system = @import("system.zig");
|
|
|
|
const String = []const u8;
|
|
|
|
const CORE_VERSION = "12.331";
|
|
const SYSTEM_NAME = system.SYSTEM_NAME;
|
|
const SYSTEM_VERSION = system.SYSTEM_VERSION;
|
|
|
|
// Entry must be a tagged union with one tag being
|
|
// Folder: struct {
|
|
// name: String,
|
|
// entries: []const Entry,
|
|
// },
|
|
// and the other tags being the respective Foundry type name
|
|
pub fn Compendium(base_type: BaseType, Entry: type) type {
|
|
if (std.meta.activeTag(@typeInfo(Entry)) != .@"union") @compileError("Entry must be a tagged union.");
|
|
if (@typeInfo(Entry).@"union".tag_type == null) @compileError("Entry must be a tagged union.");
|
|
|
|
return struct {
|
|
entries: []const Entry = &.{},
|
|
|
|
pub fn serialize(self: @This(), path: [:0]const u8) !void {
|
|
try leveldb.destroy(.{ .path = path });
|
|
|
|
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
|
defer arena.deinit();
|
|
|
|
var diagnostic: leveldb.Diagnostic = null;
|
|
const db = leveldb.open(.{ .path = path, .diagnostic = &diagnostic, .options = .{ .create_if_missing = true, .compression = .Snappy } }) catch |err| {
|
|
std.log.err("leveldb.open failed: {s}", .{ diagnostic.? });
|
|
return err;
|
|
};
|
|
defer db.close();
|
|
|
|
for (self.entries) |entry| {
|
|
defer _ = arena.reset(.retain_capacity);
|
|
try serialize_entry(arena.allocator(), db, entry, null);
|
|
}
|
|
}
|
|
|
|
fn serialize_entry(allocator: std.mem.Allocator, db: leveldb, entry: Entry, folder: ?String) !void{
|
|
switch (entry) {
|
|
.Folder => |_folder| {
|
|
const foundry_folder: Folder(base_type) = .{
|
|
.name = _folder.name,
|
|
._id = &random_id(),
|
|
.folder = folder,
|
|
};
|
|
|
|
const key = try std.fmt.allocPrintZ(allocator, "!folders!{s}", .{ foundry_folder._id.? });
|
|
const value = try std.json.stringifyAlloc(allocator, foundry_folder, .{});
|
|
try db.put(.{ .key = key, .value = value });
|
|
|
|
for (_folder.entries) |folder_entry| {
|
|
try serialize_entry(allocator, db, folder_entry, foundry_folder._id);
|
|
}
|
|
},
|
|
inline else => |item| {
|
|
std.debug.assert(item.folder == null);
|
|
|
|
var foundry_item = item;
|
|
foundry_item.folder = folder;
|
|
if (foundry_item._id == null)
|
|
foundry_item._id = &random_id();
|
|
|
|
const key = try std.fmt.allocPrintZ(allocator, "!{s}!{s}", .{ base_type.to_compendium_type(), foundry_item._id.? });
|
|
const value = try std.json.stringifyAlloc(allocator, foundry_item, .{});
|
|
try db.put(.{ .key = key, .value = value });
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
const BaseType = enum {
|
|
Item,
|
|
Actor,
|
|
|
|
fn to_compendium_type(self: @This()) String {
|
|
return switch (self) {
|
|
.Item => "items",
|
|
.Actor => "actors",
|
|
};
|
|
}
|
|
};
|
|
|
|
fn Folder(base_type: BaseType) type {
|
|
return struct {
|
|
_id: ?String = null,
|
|
name: String,
|
|
@"type": BaseType = base_type,
|
|
|
|
description: String = "",
|
|
|
|
folder: ?String = null,
|
|
|
|
sorting: enum { a, m } = .a,
|
|
sort: u64 = 0,
|
|
|
|
color: ?String = null,
|
|
|
|
flags: struct {} = .{},
|
|
_stats: DocumentStats = .{},
|
|
};
|
|
}
|
|
|
|
pub fn Item(comptime typename: String, comptime T: type) type {
|
|
return struct {
|
|
const Type: BaseType = .Item;
|
|
|
|
_id: ?String = null,
|
|
name: String,
|
|
@"type": String = typename,
|
|
img: String = "icons/svg/item-bag.svg",
|
|
|
|
system: T,
|
|
|
|
effects: []const u8 = &.{}, // TODO
|
|
folder: ?String = null,
|
|
sort: u64 = 0,
|
|
|
|
ownership: struct {
|
|
default: u8 = 0,
|
|
} = .{},
|
|
|
|
flags: struct {} = .{},
|
|
_stats: DocumentStats = .{},
|
|
};
|
|
}
|
|
|
|
pub const DocumentStats = struct {
|
|
coreVersion: String = CORE_VERSION,
|
|
systemId: String = SYSTEM_NAME,
|
|
systemVersion: String = SYSTEM_VERSION,
|
|
|
|
createdTime: ?u64 = null,
|
|
modifiedTime: ?u64 = null,
|
|
lastModifiedBy: ?String = null,
|
|
|
|
compendiumSource: ?String = null,
|
|
duplicateSource: ?String = null,
|
|
};
|
|
|
|
inline fn random_char(alphabet: []const u8) u8 {
|
|
return alphabet[std.crypto.random.uintLessThan(u8, alphabet.len)];
|
|
}
|
|
|
|
var id_set = std.BufSet.init(std.heap.c_allocator);
|
|
|
|
fn random_id() [16]u8 {
|
|
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
|
|
var result: [16]u8 = undefined;
|
|
inline for (&result) |*c| c.* = random_char(alphabet);
|
|
|
|
if (id_set.contains(&result)) {
|
|
return random_id();
|
|
}
|
|
|
|
id_set.insert(&result) catch unreachable;
|
|
return result;
|
|
}
|