Skip to content

Commit

Permalink
Reader implementation for ymlz (#10)
Browse files Browse the repository at this point in the history
* simpler solution to parsing

* Save new value for suspension

* fix reader

* remove seeked variable
  • Loading branch information
pwbh authored Aug 30, 2024
1 parent a543875 commit 47bcbf4
Showing 1 changed file with 40 additions and 29 deletions.
69 changes: 40 additions & 29 deletions src/root.zig
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const std = @import("std");

const Allocator = std.mem.Allocator;
const AnyReader = std.io.AnyReader;

const expect = std.testing.expect;

Expand Down Expand Up @@ -28,18 +29,18 @@ const Expression = struct {
pub fn Ymlz(comptime Destination: type) type {
return struct {
allocator: Allocator,
file: ?std.fs.File,
seeked: usize,
reader: ?AnyReader,
allocations: std.ArrayList([]const u8),
suspensed: ?[]const u8,

const Self = @This();

pub fn init(allocator: Allocator) !Self {
return .{
.allocator = allocator,
.file = null,
.seeked = 0,
.reader = null,
.allocations = std.ArrayList([]const u8).init(allocator),
.suspensed = null,
};
}

Expand All @@ -53,12 +54,25 @@ pub fn Ymlz(comptime Destination: type) type {
self.deinitRecursively(st);
}

pub fn load(self: *Self, yml_path: []const u8) !Destination {
pub fn loadFile(self: *Self, yml_path: []const u8) !Destination {
const file = try std.fs.openFileAbsolute(yml_path, .{ .mode = .read_only });
defer file.close();
const file_reader = file.reader();
const any_reader: std.io.AnyReader = .{ .context = &file_reader.context, .readFn = fileRead };
return self.loadReader(any_reader);
}

fn fileRead(context: *const anyopaque, buf: []u8) anyerror!usize {
const file: *std.fs.File = @constCast(@alignCast(@ptrCast(context)));
return std.fs.File.read(file.*, buf);
}

pub fn loadReader(self: *Self, reader: AnyReader) !Destination {
if (@typeInfo(Destination) != .Struct) {
@panic("ymlz only able to load yml files into structs");
}

self.file = try std.fs.openFileAbsolute(yml_path, .{ .mode = .read_only });
self.reader = reader;

return parse(self, Destination, 0);
}
Expand Down Expand Up @@ -107,7 +121,14 @@ pub fn Ymlz(comptime Destination: type) type {
inline for (destination_reflaction.Struct.fields) |field| {
const typeInfo = @typeInfo(field.type);

const raw_line = try self.readLine() orelse break;
var raw_line: []const u8 = undefined;

if (self.suspensed) |s| {
raw_line = s;
self.suspensed = null;
} else {
raw_line = try self.readLine() orelse break;
}

if (raw_line.len == 0) break;

Expand Down Expand Up @@ -172,17 +193,15 @@ pub fn Ymlz(comptime Destination: type) type {
}

fn readLine(self: *Self) !?[]const u8 {
const file = self.file orelse return error.NoFileFound;
const raw_line = try file.reader().readUntilDelimiterOrEofAlloc(
const reader = self.reader orelse return error.NoFileFound;
const raw_line = try reader.readUntilDelimiterOrEofAlloc(
self.allocator,
'\n',
MAX_READ_SIZE,
);

if (raw_line) |line| {
try self.allocations.append(line);
self.seeked += line.len + 1;
try file.seekTo(self.seeked);

if (line[0] == '#') {
// Skipping comments
Expand All @@ -207,14 +226,6 @@ pub fn Ymlz(comptime Destination: type) type {
return false;
}

fn revert(self: *Self, len: usize) !void {
const file = self.file orelse return error.NoFileFound;
// We stumbled on new field, so we rewind this advancement and return our parsed type.
// - 2 -> For some reason we need to go back twice + the length of the sentence for the '\n'
self.seeked -= len + 1;
try file.seekTo(self.seeked);
}

fn parseStringArrayExpression(self: *Self, comptime T: type, depth: usize) ![]T {
var list = std.ArrayList(T).init(self.allocator);
defer list.deinit();
Expand All @@ -223,7 +234,7 @@ pub fn Ymlz(comptime Destination: type) type {
const raw_value_line = try self.readLine() orelse break;

if (self.isNewExpression(raw_value_line, depth)) {
try self.revert(raw_value_line.len);
self.suspensed = raw_value_line;
break;
}

Expand All @@ -243,7 +254,7 @@ pub fn Ymlz(comptime Destination: type) type {
const raw_value_line = try self.readLine() orelse break;

if (self.isNewExpression(raw_value_line, depth)) {
try self.revert(raw_value_line.len);
self.suspensed = raw_value_line;
break;
}

Expand Down Expand Up @@ -278,7 +289,7 @@ pub fn Ymlz(comptime Destination: type) type {
const raw_value_line = try self.readLine() orelse break;

if (self.isNewExpression(raw_value_line, depth)) {
try self.revert(raw_value_line.len);
self.suspensed = raw_value_line;
if (preserve_new_line)
_ = list.pop();
break;
Expand Down Expand Up @@ -401,7 +412,7 @@ test "should be able to parse simple types" {
defer std.testing.allocator.free(yml_file_location);

var ymlz = try Ymlz(Subject).init(std.testing.allocator);
const result = try ymlz.load(yml_file_location);
const result = try ymlz.loadFile(yml_file_location);
defer ymlz.deinit(result);

try expect(result.first == 500);
Expand All @@ -426,7 +437,7 @@ test "should be able to parse array types" {
defer std.testing.allocator.free(yml_file_location);

var ymlz = try Ymlz(Subject).init(std.testing.allocator);
const result = try ymlz.load(yml_file_location);
const result = try ymlz.loadFile(yml_file_location);
defer ymlz.deinit(result);

try expect(result.foods.len == 4);
Expand Down Expand Up @@ -461,7 +472,7 @@ test "should be able to parse deeps/recursive structs" {
defer std.testing.allocator.free(yml_file_location);

var ymlz = try Ymlz(Subject).init(std.testing.allocator);
const result = try ymlz.load(yml_file_location);
const result = try ymlz.loadFile(yml_file_location);
defer ymlz.deinit(result);

try expect(result.inner.sd == 12);
Expand Down Expand Up @@ -490,7 +501,7 @@ test "should be able to parse booleans in all its forms" {
defer std.testing.allocator.free(yml_file_location);

var ymlz = try Ymlz(Subject).init(std.testing.allocator);
const result = try ymlz.load(yml_file_location);
const result = try ymlz.loadFile(yml_file_location);
defer ymlz.deinit(result);

try expect(result.first == true);
Expand All @@ -516,7 +527,7 @@ test "should be able to parse multiline" {
defer std.testing.allocator.free(yml_file_location);

var ymlz = try Ymlz(Subject).init(std.testing.allocator);
const result = try ymlz.load(yml_file_location);
const result = try ymlz.loadFile(yml_file_location);
defer ymlz.deinit(result);

try expect(std.mem.containsAtLeast(u8, result.multiline, 1, "asdoksad\n"));
Expand All @@ -541,7 +552,7 @@ test "should be able to ignore single quotes and double quotes" {
defer std.testing.allocator.free(yml_file_location);

var ymlz = try Ymlz(Experiment).init(std.testing.allocator);
const result = try ymlz.load(yml_file_location);
const result = try ymlz.loadFile(yml_file_location);
defer ymlz.deinit(result);

try expect(std.mem.containsAtLeast(u8, result.one, 1, "testing without quotes"));
Expand Down Expand Up @@ -578,7 +589,7 @@ test "should be able to parse arrays of T" {
defer std.testing.allocator.free(yml_file_location);

var ymlz = try Ymlz(Experiment).init(std.testing.allocator);
const result = try ymlz.load(yml_file_location);
const result = try ymlz.loadFile(yml_file_location);
defer ymlz.deinit(result);

try expect(std.mem.eql(u8, result.name, "Martin D'vloper"));
Expand Down

0 comments on commit 47bcbf4

Please sign in to comment.