guid/src/common-structures.zig

186 lines
5.1 KiB
Zig

const std = @import("std");
const Allocator = std.mem.Allocator;
const AutoHashMap = std.AutoHashMap;
const ArrayList = std.ArrayList;
/// This file introduces the main structures used by guid once running.
/// Tests show how to use them.
/// Type.create do no allocate memory for the structure, only to its attributes.
/// Type.deinit works in cascade (deinit its children).
const PropertyHashMap = AutoHashMap([] const u8, PropertyValue);
const NodeList = ArrayList(Node);
const DefinitionHashMap = AutoHashMap([] const u8, Node);
pub const Node = struct {
id: ?[] const u8,
type_name: [] const u8,
properties: PropertyHashMap,
children: NodeList,
gpa: *Allocator,
pub fn deinit(self: *Node) void {
self.properties.deinit();
for (self.children.items) |*value| {
value.deinit();
}
self.children.deinit();
}
pub fn create(allocator: *Allocator, type_name: []const u8, id: ?[]const u8) !Node {
return Node{
.id = id,
.type_name = type_name,
.properties = PropertyHashMap.init(allocator),
.children = NodeList.init(allocator),
.gpa = allocator,
};
}
};
const PropertyValueTags = enum {
string,
integer,
float,
reference,
};
pub const PropertyValue = union(PropertyValueTags) {
// nil: null,
string: [] const u8, // String.
integer: u64, // Num (integer).
float: f64, // Num (float).
reference: [] const u8, // Reference to another property (property binding).
};
pub const Tree = struct {
definitions: DefinitionHashMap,
children: NodeList,
gpa: *Allocator,
pub fn deinit(self: *Tree) void {
self.definitions.deinit();
for (self.children.items) |*value| {
value.deinit();
}
self.children.deinit();
}
pub fn create(allocator: *Allocator) !Tree {
return Tree{
.definitions = DefinitionHashMap.init(allocator),
.children = NodeList.init(allocator),
.gpa = allocator,
};
}
};
// TESTS and private util functions.
fn say(tosay: []const u8) void {
std.debug.print("{}", .{tosay});
}
fn print_properties(properties: PropertyHashMap) void {
var it = properties.iterator();
while(it.next()) |kv| {
std.debug.print("\t{} => {}\n", .{kv.key, properties.get(kv.key)});
}
}
fn print_node(node: Node) void {
std.debug.print("Node type {} (id: {})\n", .{node.type_name, node.id});
print_properties(node.properties);
}
pub fn print_tree(tree: Tree) void {
say("\ntree.definitions:\n");
var it = tree.definitions.iterator();
while(it.next()) |kv| {
std.debug.print("{} => ", .{kv.key});
const node = tree.definitions.get(kv.key);
if(node) |n| { print_node(n); }
}
say("tree.children:\n");
for(tree.children.items) |v, k| {
std.debug.print("{} => ", .{k});
print_node(v);
}
}
test "simple test about structures" {
var gpa = std.heap.GeneralPurposeAllocator(.{.safety = true}){};
const allocator = &gpa.allocator;
var value = PropertyValue { .integer = 10 };
var properties = PropertyHashMap.init(allocator);
try properties.put("hello", value);
// Displaying the content.
// say("\n");
// print_properties(properties);
// Freeing the properties.
properties.deinit();
// Testing memory leaks at the end of the test.
const leaks = gpa.deinit();
if (leaks) {
say("\nthere were leaks, oh no\n");
}
else {
say("\nno leaks, yay!\n");
}
std.testing.expect(! leaks);
}
fn init_stuff(allocator: *Allocator) !Tree {
var tree = try Tree.create(allocator);
// Creating a definition and a few children.
try tree.definitions.put("MyObject", try Node.create(allocator, "my-type-name", "my-id"));
var new_node = try Node.create(allocator, "Object", "some-id-for-this-object");
var value = PropertyValue { .integer = 10 };
try new_node.properties.put("integer-val", value);
value = PropertyValue { .string = "some value" };
try new_node.properties.put("string-val", value);
try tree.children.append(new_node);
try tree.children.append(try Node.create(allocator, "Object", "my-id"));
try tree.children.append(try Node.create(allocator, "OtherObject", null));
try tree.children.append(try Node.create(allocator, "Text", "my-id-for-text-object"));
return tree;
}
test "init a Tree structure" {
// Allocator with safety on: checking for memory leaks.
var gpa = std.heap.GeneralPurposeAllocator(.{.safety = true}){};
const allocator = &gpa.allocator;
// Creating a tree.
var tree = try init_stuff(allocator);
// Display the content of the tree.
// print_tree(tree);
// Freeing the tree.
tree.deinit();
// Testing memory leaks at the end of the test.
const leaks = gpa.deinit();
if (leaks) {
say("\nthere were leaks, oh no\n");
}
else {
say("\nno leaks, yay!\n");
}
std.testing.expect(! leaks);
}