diff options
Diffstat (limited to 'src/config.zig')
| -rw-r--r-- | src/config.zig | 339 |
1 files changed, 339 insertions, 0 deletions
diff --git a/src/config.zig b/src/config.zig new file mode 100644 index 0000000..8d296dc --- /dev/null +++ b/src/config.zig @@ -0,0 +1,339 @@ +const c = @import("c.zig"); +const fs = @import("std").fs; +const std = @import("std"); +const debug = @import("std").debug; + +pub const OutputType = enum { + window, +}; + +pub const OutputConfig = struct { + type: OutputType, + width: i32, + height: i32, + fullscreen: bool, // only for `Window` +}; + +pub const ParameterConfig = struct { + name: []const u8, + type: []const u8, +}; + +const defaultOutput = OutputConfig{ + .type = OutputType.window, + .width = 800, + .height = 600, + .fullscreen = false, +}; + +pub const Config = struct { + width: i32, + height: i32, + outputs: []const OutputConfig, + fragment: []const u8, + parameters: []const ParameterConfig, + arena: std.heap.ArenaAllocator, + + pub fn parse(filename: []const u8) !Config { + var parser: c.yaml_parser_t = undefined; + _ = c.yaml_parser_initialize(&parser); + defer c.yaml_parser_delete(&parser); + + const file = try fs.cwd().openFile(filename, .{}); + // c.yaml_parser_set_input_file(&parser, @as([*c]c.FILE, file.handle)); + var buffer: [1024]u8 = undefined; + const len: usize = try file.read(buffer[0..]); + c.yaml_parser_set_input_string(&parser, &buffer[0], len); + + var config: Config = .{ + .width = 1920, + .height = 1080, + .outputs = ([_]OutputConfig{defaultOutput})[0..], + .fragment = "sphere.frag", + .parameters = ([0]ParameterConfig{})[0..], + .arena = std.heap.ArenaAllocator.init(std.heap.page_allocator), + }; + + try expectEvent(&parser, c.yaml_event_type_t.YAML_STREAM_START_EVENT); + try expectEvent(&parser, c.yaml_event_type_t.YAML_DOCUMENT_START_EVENT); + try expectEvent(&parser, c.yaml_event_type_t.YAML_MAPPING_START_EVENT); + + while (true) { + var event: c.yaml_event_t = undefined; + if (c.yaml_parser_parse(&parser, &event) != 1) + return error.YAMLParserError; + defer c.yaml_event_delete(&event); + + switch (event.type) { + c.yaml_event_type_t.YAML_MAPPING_END_EVENT => break, + c.yaml_event_type_t.YAML_SCALAR_EVENT => { + const data = event.data.scalar; + const key: []const u8 = data.value[0..data.length]; + + if (std.mem.eql(u8, key, "width")) { + config.width = try parseInt(&parser, i32); + } else if (std.mem.eql(u8, key, "height")) { + config.height = try parseInt(&parser, i32); + } else if (std.mem.eql(u8, key, "fragment")) { + config.fragment = try parseString(&parser, &config.arena.allocator); + } else if (std.mem.eql(u8, key, "outputs")) { + config.outputs = try parseOutputs(&parser, &config.arena.allocator); + } else if (std.mem.eql(u8, key, "parameters")) { + config.parameters = try parseParameters(&parser, &config.arena.allocator); + } else { + debug.warn("unknown key: '{}'\n", .{key}); + try skipAny(&parser); + } + }, + else => { + debug.warn("unexpected event: {}\n", .{event.type}); + return error.InvalidConfiguration; + }, + } + } + + try expectEvent(&parser, c.yaml_event_type_t.YAML_DOCUMENT_END_EVENT); + try expectEvent(&parser, c.yaml_event_type_t.YAML_STREAM_END_EVENT); + + return config; + } +}; + +fn expectEvent(parser: *c.yaml_parser_t, expectedType: c.yaml_event_type_t) !void { + var event: c.yaml_event_t = undefined; + + if (c.yaml_parser_parse(parser, &event) != 1) + return error.YAMLParserError; + defer c.yaml_event_delete(&event); + + if (event.type != expectedType) { + debug.warn("unexpected event: {}\n", .{event.type}); + return error.InvalidConfiguration; + } +} + +fn parseString(parser: *c.yaml_parser_t, allocator: *std.mem.Allocator) ![]const u8 { + var event: c.yaml_event_t = undefined; + if (c.yaml_parser_parse(parser, &event) != 1) + return error.YAMLParserError; + defer c.yaml_event_delete(&event); + + if (event.type != c.yaml_event_type_t.YAML_SCALAR_EVENT) + return error.InvalidType; + + const data = event.data.scalar; + const value: []const u8 = data.value[0..data.length]; + return try std.mem.dupe(allocator, u8, value); +} + +fn parseInt(parser: *c.yaml_parser_t, comptime T: type) !T { + var event: c.yaml_event_t = undefined; + if (c.yaml_parser_parse(parser, &event) != 1) + return error.YAMLParserError; + defer c.yaml_event_delete(&event); + + if (event.type != c.yaml_event_type_t.YAML_SCALAR_EVENT) + return error.InvalidType; + + const data = event.data.scalar; + const value: []const u8 = data.value[0..data.length]; + return try std.fmt.parseInt(T, value, 10); +} + +fn parseEnum(parser: *c.yaml_parser_t, comptime T: type) !T { + var event: c.yaml_event_t = undefined; + if (c.yaml_parser_parse(parser, &event) != 1) + return error.YAMLParserError; + defer c.yaml_event_delete(&event); + + if (event.type != c.yaml_event_type_t.YAML_SCALAR_EVENT) + return error.InvalidType; + + const data = event.data.scalar; + const value: []const u8 = data.value[0..data.length]; + + inline for (@typeInfo(T).Enum.fields) |field| { + if (std.mem.eql(u8, value, field.name)) + return @field(T, field.name); + } + + return error.InvalidValue; +} + +fn parseBool(parser: *c.yaml_parser_t) !bool { + var event: c.yaml_event_t = undefined; + if (c.yaml_parser_parse(parser, &event) != 1) + return error.YAMLParserError; + defer c.yaml_event_delete(&event); + + if (event.type != c.yaml_event_type_t.YAML_SCALAR_EVENT) + return error.InvalidType; + + const data = event.data.scalar; + const value: []const u8 = data.value[0..data.length]; + + if (data.style != c.yaml_scalar_style_t.YAML_PLAIN_SCALAR_STYLE) + return false; + return std.mem.eql(u8, value, "true") or std.mem.eql(u8, value, "1"); +} + +fn parseOutputs(parser: *c.yaml_parser_t, allocator: *std.mem.Allocator) ![]OutputConfig { + try expectEvent(parser, c.yaml_event_type_t.YAML_SEQUENCE_START_EVENT); + + var outputs = try allocator.alloc(OutputConfig, 1024); + var output_count: usize = 0; + + while (true) { + var seqEvent: c.yaml_event_t = undefined; + if (c.yaml_parser_parse(parser, &seqEvent) != 1) + return error.YAMLParserError; + defer c.yaml_event_delete(&seqEvent); + + switch (seqEvent.type) { + c.yaml_event_type_t.YAML_SEQUENCE_END_EVENT => break, + c.yaml_event_type_t.YAML_MAPPING_START_EVENT => { + var output = &outputs[output_count]; + output.* = defaultOutput; + + while (true) { + var event: c.yaml_event_t = undefined; + if (c.yaml_parser_parse(parser, &event) != 1) + return error.YAMLParserError; + defer c.yaml_event_delete(&event); + + switch (event.type) { + c.yaml_event_type_t.YAML_MAPPING_END_EVENT => break, + c.yaml_event_type_t.YAML_SCALAR_EVENT => { + const data = event.data.scalar; + const key: []const u8 = data.value[0..data.length]; + + if (std.mem.eql(u8, key, "type")) { + output.*.type = try parseEnum(parser, OutputType); + } else if (std.mem.eql(u8, key, "width")) { + output.*.width = try parseInt(parser, i32); + } else if (std.mem.eql(u8, key, "height")) { + output.*.height = try parseInt(parser, i32); + } else if (std.mem.eql(u8, key, "fullscreen")) { + output.*.fullscreen = try parseBool(parser); + } else { + debug.warn("unknown key: '{}'\n", .{key}); + try skipAny(parser); + } + }, + else => { + debug.warn("unexpected event: {}\n", .{event.type}); + return error.InvalidConfiguration; + }, + } + } + + output_count += 1; + }, + else => { + debug.warn("unexpected event: {}\n", .{seqEvent.type}); + return error.InvalidConfiguration; + }, + } + } + + _ = allocator.realloc(outputs, output_count) catch 0; + return outputs[0..output_count]; +} + +fn parseParameters(parser: *c.yaml_parser_t, allocator: *std.mem.Allocator) ![]ParameterConfig { + try expectEvent(parser, c.yaml_event_type_t.YAML_SEQUENCE_START_EVENT); + + var parameters = try allocator.alloc(ParameterConfig, 1024); + var param_count: usize = 0; + + while (true) { + var seqEvent: c.yaml_event_t = undefined; + if (c.yaml_parser_parse(parser, &seqEvent) != 1) + return error.YAMLParserError; + defer c.yaml_event_delete(&seqEvent); + + switch (seqEvent.type) { + c.yaml_event_type_t.YAML_SEQUENCE_END_EVENT => break, + c.yaml_event_type_t.YAML_MAPPING_START_EVENT => { + var param = ¶meters[param_count]; + var have_name = false; + var have_type = false; + + while (true) { + var event: c.yaml_event_t = undefined; + if (c.yaml_parser_parse(parser, &event) != 1) + return error.YAMLParserError; + defer c.yaml_event_delete(&event); + + switch (event.type) { + c.yaml_event_type_t.YAML_MAPPING_END_EVENT => break, + c.yaml_event_type_t.YAML_SCALAR_EVENT => { + const data = event.data.scalar; + const key: []const u8 = data.value[0..data.length]; + + if (std.mem.eql(u8, key, "name")) { + param.*.name = try parseString(parser, allocator); + have_name = true; + } else if (std.mem.eql(u8, key, "type")) { + param.*.type = try parseString(parser, allocator); + have_type = true; + } else { + debug.warn("unknown key: '{}'\n", .{key}); + try skipAny(parser); + } + }, + else => { + debug.warn("unexpected event: {}\n", .{event.type}); + return error.InvalidConfiguration; + }, + } + } + + if (!(have_name and have_type)) { + debug.warn("name and type are mandatory for parameters.\n", .{}); + return error.InvalidConfiguration; + } + + param_count += 1; + }, + else => { + debug.warn("unexpected event: {}\n", .{seqEvent.type}); + return error.InvalidConfiguration; + }, + } + } + + _ = allocator.realloc(parameters, param_count) catch 0; + return parameters[0..param_count]; +} + +fn skipAny(parser: *c.yaml_parser_t) !void { + var event: c.yaml_event_t = undefined; + var depth: u32 = 0; + while (true) { + if (c.yaml_parser_parse(parser, &event) != 1) + return error.YAMLParserError; + defer c.yaml_event_delete(&event); + + switch (event.type) { + c.yaml_event_type_t.YAML_SCALAR_EVENT => { + if (depth == 0) + break; + }, + + c.yaml_event_type_t.YAML_SEQUENCE_START_EVENT, c.yaml_event_type_t.YAML_MAPPING_START_EVENT => depth += 1, + + c.yaml_event_type_t.YAML_SEQUENCE_END_EVENT, c.yaml_event_type_t.YAML_MAPPING_END_EVENT => { + depth -= 1; + + if (depth == 0) + break; + }, + + else => { + debug.warn("unexpected event: {}\n", .{event.type}); + return error.InvalidConfiguration; + }, + } + } +} |
