You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

415 lines
14 KiB
Zig

const c = @import("c.zig");
const fs = @import("std").fs;
const std = @import("std");
const debug = @import("std").debug;
pub const OutputConfig = struct {
const Type = enum {
window,
};
type: Type,
width: i32,
height: i32,
// only for 'window'
monitor: []const u8,
transparent: bool,
fullscreen: bool,
};
pub const ParameterConfig = struct {
name: []const u8,
type: []const u8,
};
pub const OSCConfig = union(enum) {
const Protocol = enum {
udp,
tcp,
unix,
};
Manual: struct {
protocol: Protocol = Protocol.udp,
port: u16 = 9000,
},
URL: []const u8,
};
const defaultOutput = OutputConfig{
.type = .window,
.width = 800,
.height = 600,
.monitor = "",
.transparent = false,
.fullscreen = false,
};
pub const Config = struct {
width: i32,
height: i32,
outputs: []const OutputConfig,
fragment: []const u8,
parameters: []const ParameterConfig,
osc: OSCConfig,
pub fn parse(allocator: std.mem.Allocator, 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, .{});
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 = "",
.parameters = ([0]ParameterConfig{})[0..],
.osc = .{ .URL = "osc.udp://:9000" },
};
try expectEvent(&parser, c.YAML_STREAM_START_EVENT);
try expectEvent(&parser, c.YAML_DOCUMENT_START_EVENT);
try expectEvent(&parser, c.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_MAPPING_END_EVENT => break,
c.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, allocator);
} else if (std.mem.eql(u8, key, "outputs")) {
config.outputs = try parseOutputs(&parser, allocator);
} else if (std.mem.eql(u8, key, "parameters")) {
config.parameters = try parseParameters(&parser, allocator);
} else if (std.mem.eql(u8, key, "osc")) {
config.osc = try parseOSC(&parser, allocator);
} else {
debug.print("unknown config key: '{s}'\n", .{key});
try skipAny(&parser);
}
},
else => {
debug.print("unexpected config event: {}\n", .{event.type});
return error.InvalidConfiguration;
},
}
}
try expectEvent(&parser, c.YAML_DOCUMENT_END_EVENT);
try expectEvent(&parser, c.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.print("unexpected event: {}\n", .{event.type});
return error.InvalidConfiguration;
}
}
fn parseStringEvent(event: *c.yaml_event_t, allocator: std.mem.Allocator) ![]const u8 {
if (event.type != c.YAML_SCALAR_EVENT)
return error.InvalidType;
const data = event.data.scalar;
const value: []const u8 = data.value[0..data.length];
return try allocator.dupe(u8, value);
}
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);
return parseStringEvent(&event, allocator);
}
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_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_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_SCALAR_EVENT)
return error.InvalidType;
const data = event.data.scalar;
const value: []const u8 = data.value[0..data.length];
if (data.style != c.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_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_SEQUENCE_END_EVENT => break,
c.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_MAPPING_END_EVENT => break,
c.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, OutputConfig.Type);
} 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, "monitor")) {
output.*.monitor = try parseString(parser, allocator);
} else if (std.mem.eql(u8, key, "fullscreen")) {
output.*.fullscreen = try parseBool(parser);
} else if (std.mem.eql(u8, key, "transparent")) {
output.*.transparent = try parseBool(parser);
} else {
debug.print("unknown config key: '{s}'\n", .{key});
try skipAny(parser);
}
},
else => {
debug.print("unexpected config event: {}\n", .{event.type});
return error.InvalidConfiguration;
},
}
}
output_count += 1;
},
else => {
debug.print("unexpected config 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_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_SEQUENCE_END_EVENT => break,
c.YAML_MAPPING_START_EVENT => {
var param = &parameters[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_MAPPING_END_EVENT => break,
c.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.print("unknown config key: '{s}'\n", .{key});
try skipAny(parser);
}
},
else => {
debug.print("unexpected config event: {}\n", .{event.type});
return error.InvalidConfiguration;
},
}
}
if (!(have_name and have_type)) {
debug.print("name and type are mandatory for parameters.\n", .{});
return error.InvalidConfiguration;
}
param_count += 1;
},
else => {
debug.print("unexpected config event: {}\n", .{seqEvent.type});
return error.InvalidConfiguration;
},
}
}
_ = allocator.realloc(parameters, param_count) catch 0;
return parameters[0..param_count];
}
fn parseOSC(parser: *c.yaml_parser_t, allocator: std.mem.Allocator) !OSCConfig {
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_SCALAR_EVENT) {
const url = try parseStringEvent(&event, allocator);
return OSCConfig{ .URL = url };
} else if (event.type != c.YAML_MAPPING_START_EVENT) {
debug.print("unexpected event: {}\n", .{event.type});
return error.InvalidConfiguration;
}
var config: OSCConfig = .{ .Manual = .{} };
while (true) {
if (c.yaml_parser_parse(parser, &event) != 1)
return error.YAMLParserError;
defer c.yaml_event_delete(&event);
switch (event.type) {
c.YAML_MAPPING_END_EVENT => break,
c.YAML_SCALAR_EVENT => {
const data = event.data.scalar;
const key: []const u8 = data.value[0..data.length];
if (std.mem.eql(u8, key, "protocol")) {
config.Manual.protocol = try parseEnum(parser, OSCConfig.Protocol);
} else if (std.mem.eql(u8, key, "port")) {
config.Manual.port = try parseInt(parser, u16);
} else {
debug.print("unknown key: '{s}'\n", .{key});
try skipAny(parser);
}
},
else => {
debug.print("unexpected event: {}\n", .{event.type});
return error.InvalidConfiguration;
},
}
}
return config;
}
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_SCALAR_EVENT => {
if (depth == 0)
break;
},
c.YAML_SEQUENCE_START_EVENT, c.YAML_MAPPING_START_EVENT => depth += 1,
c.YAML_SEQUENCE_END_EVENT, c.YAML_MAPPING_END_EVENT => {
depth -= 1;
if (depth == 0)
break;
},
else => {
debug.print("unexpected event: {}\n", .{event.type});
return error.InvalidConfiguration;
},
}
}
}