mirror of
https://codeberg.org/fairyglade/ly.git
synced 2026-08-09 15:19:13 +02:00
refactor(DurFile): use typestate pattern for valid DurFormat
Refactors DurFile to use the typestate pattern with DurFormat. Parses file into DurFormatRaw, then validates into a DurFormat. Reduces unnecessary optional unwrapping. Narrows some numeric types, mostly i64 -> u32
This commit is contained in:
parent
59c07aa3ba
commit
a2ca556fca
1 changed files with 80 additions and 34 deletions
|
|
@ -62,7 +62,7 @@ const Frame = struct {
|
|||
};
|
||||
|
||||
// https://github.com/cmang/durdraw/blob/0.29.0/durformat.md
|
||||
const DurFormat = struct {
|
||||
const DurFormatRaw = struct {
|
||||
allocator: Allocator,
|
||||
formatVersion: ?i64 = null,
|
||||
colorFormat: ?[]const u8 = null,
|
||||
|
|
@ -72,7 +72,9 @@ const DurFormat = struct {
|
|||
lines: ?i64 = null,
|
||||
frames: std.ArrayList(Frame) = undefined,
|
||||
|
||||
pub fn valid(self: *DurFormat) bool {
|
||||
// Validate data and return a valid DurFormat
|
||||
// Consumes `self`, making it unusable after
|
||||
pub fn validate(self: *DurFormatRaw) !DurFormat {
|
||||
if (self.formatVersion != null and
|
||||
self.colorFormat != null and
|
||||
self.encoding != null and
|
||||
|
|
@ -83,27 +85,42 @@ const DurFormat = struct {
|
|||
{
|
||||
// v8 may have breaking changes like changing the colormap xy direction
|
||||
// (https://github.com/cmang/durdraw/issues/24)
|
||||
if (self.formatVersion.? != 7) return false;
|
||||
const formatVersion = self.formatVersion orelse return error.NotValidFile;
|
||||
if (formatVersion != 7) return error.NotValidFile;
|
||||
|
||||
const colorFormatStr = self.colorFormat orelse return error.NotValidFile;
|
||||
// Code currently only supports 16 and 256 color format only
|
||||
if (!(eql(u8, "16", self.colorFormat.?) or eql(u8, "256", self.colorFormat.?)))
|
||||
return false;
|
||||
const colorFormat: DurColorFormat =
|
||||
if (eql(u8, colorFormatStr, "16")) .sixteen else if (eql(u8, colorFormatStr, "256")) .two_fifty_six else return error.NotValidFile;
|
||||
|
||||
// Code currently supports only utf-8 encoding
|
||||
if (!eql(u8, self.encoding.?, "utf-8")) return false;
|
||||
const encoding: DurEncoding = if (eql(u8, self.encoding.?, "utf-8")) .utf_8 else return error.NotValidFile;
|
||||
|
||||
// Sanity check on file
|
||||
if (self.columns.? <= 0) return false;
|
||||
if (self.lines.? <= 0) return false;
|
||||
if (self.framerate.? < 0) return false;
|
||||
const columns = std.math.cast(u32, self.columns.?) orelse return error.NotValidFile;
|
||||
const lines = std.math.cast(u32, self.lines.?) orelse return error.NotValidFile;
|
||||
if (self.framerate.? <= 0) return error.NotValidFile;
|
||||
|
||||
return true;
|
||||
// transfer ownership
|
||||
const frames = self.frames;
|
||||
self.frames = .{};
|
||||
|
||||
return .{
|
||||
.allocator = self.allocator,
|
||||
.formatVersion = formatVersion,
|
||||
.colorFormat = colorFormat,
|
||||
.encoding = encoding,
|
||||
.framerate = self.framerate.?,
|
||||
.columns = columns,
|
||||
.lines = lines,
|
||||
.frames = frames,
|
||||
};
|
||||
}
|
||||
|
||||
return false;
|
||||
return error.NotValidFile;
|
||||
}
|
||||
|
||||
fn parse_dur_from_json(self: *DurFormat, allocator: Allocator, dur_json_root: Json.Value) !void {
|
||||
fn parse_dur_from_json(self: *DurFormatRaw, allocator: Allocator, dur_json_root: Json.Value) !void {
|
||||
var dur_movie = if (dur_json_root.object.get("DurMovie")) |dm| dm.object else return error.NotValidFile;
|
||||
|
||||
// Depending on the version, a dur file can have different json object names (ie: columns vs sizeX)
|
||||
|
|
@ -150,25 +167,21 @@ const DurFormat = struct {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn create_from_file(self: *DurFormat, allocator: Allocator, io: std.Io, file_path: []const u8) !void {
|
||||
const file_decompressed = try read_decompress_file(allocator, io, file_path);
|
||||
pub fn create_from_file(self: *DurFormatRaw, allocator: Allocator, file_path: []const u8) !void {
|
||||
const file_decompressed = try read_decompress_file(allocator, file_path);
|
||||
defer allocator.free(file_decompressed);
|
||||
|
||||
const parsed = try Json.parseFromSlice(Json.Value, allocator, file_decompressed, .{});
|
||||
defer parsed.deinit();
|
||||
|
||||
try parse_dur_from_json(self, allocator, parsed.value);
|
||||
|
||||
if (!self.valid()) {
|
||||
return error.NotValidFile;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(allocator: Allocator) DurFormat {
|
||||
pub fn init(allocator: Allocator) DurFormatRaw {
|
||||
return .{ .allocator = allocator };
|
||||
}
|
||||
|
||||
pub fn deinit(self: *DurFormat) void {
|
||||
pub fn deinit(self: *DurFormatRaw) void {
|
||||
if (self.colorFormat) |str| self.allocator.free(str);
|
||||
if (self.encoding) |str| self.allocator.free(str);
|
||||
|
||||
|
|
@ -179,6 +192,31 @@ const DurFormat = struct {
|
|||
}
|
||||
};
|
||||
|
||||
const DurColorFormat = enum {
|
||||
sixteen,
|
||||
two_fifty_six,
|
||||
};
|
||||
|
||||
const DurEncoding = enum { utf_8 };
|
||||
|
||||
const DurFormat = struct {
|
||||
allocator: Allocator,
|
||||
formatVersion: i64,
|
||||
colorFormat: DurColorFormat,
|
||||
encoding: DurEncoding,
|
||||
framerate: f64,
|
||||
columns: u32,
|
||||
lines: u32,
|
||||
frames: std.ArrayList(Frame),
|
||||
|
||||
pub fn deinit(self: *DurFormat) void {
|
||||
for (self.frames.items) |frame| {
|
||||
frame.deinit(self.allocator);
|
||||
}
|
||||
self.frames.deinit(self.allocator);
|
||||
}
|
||||
};
|
||||
|
||||
const tb_color_16 = [16]u32{
|
||||
Color.ECOL_BLACK,
|
||||
Color.ECOL_RED,
|
||||
|
|
@ -324,16 +362,16 @@ offset_alignment: DurOffsetAlignment,
|
|||
offset: IVec2,
|
||||
|
||||
// if the user has an even number of columns or rows, we will default to the left or higher position (e.g. 4 columns center = .x..)
|
||||
fn center(v: u32) i64 {
|
||||
return @intCast((v / 2) + (v % 2));
|
||||
fn center(v: i64) i64 {
|
||||
return @intCast(@divTrunc(v, 2) + @mod(v, 2));
|
||||
}
|
||||
|
||||
fn calc_start_position(terminal_buffer: *TerminalBuffer, dur_movie: *DurFormat, offset_alignment: DurOffsetAlignment, offset: IVec2) IVec2 {
|
||||
const buf_width: u32 = @intCast(terminal_buffer.width);
|
||||
const buf_height: u32 = @intCast(terminal_buffer.height);
|
||||
|
||||
var movie_width: u32 = @intCast(dur_movie.columns.?);
|
||||
var movie_height: u32 = @intCast(dur_movie.lines.?);
|
||||
var movie_width: u32 = dur_movie.columns;
|
||||
var movie_height: u32 = dur_movie.lines;
|
||||
|
||||
if (movie_width > buf_width) movie_width = buf_width;
|
||||
if (movie_height > buf_height) movie_height = buf_height;
|
||||
|
|
@ -357,8 +395,8 @@ fn calc_frame_size(terminal_buffer: *TerminalBuffer, dur_movie: *DurFormat) UVec
|
|||
const buf_width: u32 = @intCast(terminal_buffer.width);
|
||||
const buf_height: u32 = @intCast(terminal_buffer.height);
|
||||
|
||||
const movie_width: u32 = @intCast(dur_movie.columns.?);
|
||||
const movie_height: u32 = @intCast(dur_movie.lines.?);
|
||||
const movie_width: u32 = dur_movie.columns;
|
||||
const movie_height: u32 = dur_movie.lines;
|
||||
|
||||
// Draw only the needed amount if movie smaller than screen. If movie is bigger, we will just draw entire screen
|
||||
const frame_width = if (movie_width < buf_width) movie_width else buf_width;
|
||||
|
|
@ -381,9 +419,9 @@ pub fn init(
|
|||
timeout_sec: u12,
|
||||
frame_delay: u16,
|
||||
) !DurFile {
|
||||
var dur_movie: DurFormat = .init(allocator);
|
||||
var dur_movie_raw: DurFormatRaw = .init(allocator);
|
||||
|
||||
dur_movie.create_from_file(allocator, io, file_path) catch |err| switch (err) {
|
||||
dur_movie_raw.create_from_file(allocator, file_path) catch |err| switch (err) {
|
||||
error.FileNotFound => {
|
||||
try log_file.err(io, "tui", "dur_file was not found at: {s}", .{file_path});
|
||||
return err;
|
||||
|
|
@ -395,11 +433,19 @@ pub fn init(
|
|||
else => return err,
|
||||
};
|
||||
|
||||
var dur_movie = dur_movie_raw.validate() catch |err| switch (err) {
|
||||
error.NotValidFile => {
|
||||
try log_file.err("tui", "dur_file loaded was invalid or not a dur file!", .{});
|
||||
return err;
|
||||
},
|
||||
};
|
||||
dur_movie_raw.deinit();
|
||||
|
||||
// 4 bit mode with 256 color is unsupported
|
||||
if (!full_color and eql(u8, dur_movie.colorFormat.?, "256")) {
|
||||
try log_file.err(io, "tui", "dur_file can not be 256 color encoded when not using full_color option!", .{});
|
||||
if (!full_color and dur_movie.colorFormat == .two_fifty_six) {
|
||||
try log_file.err("tui", "dur_file can not be 256 color encoded when not using full_color option!", .{});
|
||||
dur_movie.deinit();
|
||||
return error.InvalidColorFormat;
|
||||
return error.NotFullColor;
|
||||
}
|
||||
|
||||
const offset: IVec2 = .{ x_offset, y_offset };
|
||||
|
|
@ -408,7 +454,7 @@ pub fn init(
|
|||
const frame_size = calc_frame_size(terminal_buffer, &dur_movie);
|
||||
|
||||
// Convert dur fps to frames per ms
|
||||
const frame_time: u32 = @trunc(1000 / dur_movie.framerate.?);
|
||||
const frame_time: u32 = @trunc(1000 / dur_movie.framerate);
|
||||
|
||||
return .{
|
||||
.instance = null,
|
||||
|
|
@ -426,7 +472,7 @@ pub fn init(
|
|||
.frame_delay = frame_delay,
|
||||
.dur_movie = dur_movie,
|
||||
.frame_time = frame_time,
|
||||
.is_color_format_16 = eql(u8, dur_movie.colorFormat.?, "16"),
|
||||
.is_color_format_16 = dur_movie.colorFormat == .sixteen,
|
||||
.offset_alignment = offset_alignment,
|
||||
.offset = offset,
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue