From d1722e3b6b3a3c054f19edb5ee3acd66ac212d34 Mon Sep 17 00:00:00 2001 From: Roman Pytkov Date: Mon, 2 Mar 2026 17:50:05 +0300 Subject: [PATCH] =?UTF-8?q?=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=D1=8E?= =?UTF-8?q?=D1=89=D0=B5=D0=B5=20=D1=81=D0=BE=D1=85=D1=80=D0=B0=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/models/Document.zig | 17 ++++++++++++++++ src/persistence/document_json.zig | 30 ---------------------------- src/persistence/json_io.zig | 33 +++++++++++++++++++++++++++++++ src/ui/menu_bar.zig | 7 ++++--- 4 files changed, 54 insertions(+), 33 deletions(-) delete mode 100644 src/persistence/document_json.zig create mode 100644 src/persistence/json_io.zig diff --git a/src/models/Document.zig b/src/models/Document.zig index 668fbcd..2c99e25 100644 --- a/src/models/Document.zig +++ b/src/models/Document.zig @@ -23,6 +23,23 @@ pub fn deinit(self: *Document, allocator: std.mem.Allocator) void { self.objects.deinit(allocator); } +pub fn clone(self: *const Document, allocator: std.mem.Allocator) !Document { + var objects_list = std.ArrayList(Object).empty; + errdefer { + for (objects_list.items) |*obj| obj.deinit(allocator); + objects_list.deinit(allocator); + } + var next_id = self.next_object_id; + for (self.objects.items) |obj| { + try objects_list.append(allocator, try obj.clone(allocator, &next_id)); + } + return .{ + .size = self.size, + .objects = objects_list, + .next_object_id = next_id, + }; +} + pub fn addObject(self: *Document, allocator: std.mem.Allocator, template: Object) !void { const obj = try template.clone(allocator, &self.next_object_id); try self.objects.append(allocator, obj); diff --git a/src/persistence/document_json.zig b/src/persistence/document_json.zig deleted file mode 100644 index 28395f4..0000000 --- a/src/persistence/document_json.zig +++ /dev/null @@ -1,30 +0,0 @@ -const std = @import("std"); -const Document = @import("../models/Document.zig"); - -pub fn saveToFile(doc: *const Document, path: []const u8) !void { - var file = try std.fs.cwd().createFile(path, .{ .truncate = true }); - defer file.close(); - - var buffer: [4096]u8 = undefined; - var writer = file.writer(&buffer); - - try std.json.Stringify.value( - doc, - .{ .whitespace = .indent_2 }, - &writer.interface, - ); - - try writer.interface.flush(); -} - -pub fn loadFromFile(allocator: std.mem.Allocator, path: []const u8) !Document { - var file = try std.fs.cwd().openFile(path, .{}); - defer file.close(); - - const data = try file.readToEndAlloc(allocator, std.math.maxInt(usize)); - defer allocator.free(data); - - const parsed = try std.json.parseFromSlice(Document, allocator, data, .{ .parse_numbers = true }); - - return parsed.value; -} diff --git a/src/persistence/json_io.zig b/src/persistence/json_io.zig new file mode 100644 index 0000000..0ef32db --- /dev/null +++ b/src/persistence/json_io.zig @@ -0,0 +1,33 @@ +const std = @import("std"); +const Document = @import("../models/Document.zig"); + +/// Сохраняет значение произвольного типа T в JSON-файл. +pub fn saveToFile(comptime T: type, value: *const T, path: []const u8) !void { + var file = try std.fs.cwd().createFile(path, .{ .truncate = true }); + defer file.close(); + + var buffer: [4096]u8 = undefined; + var writer = file.writer(&buffer); + + try std.json.Stringify.value(value, .{ .whitespace = .indent_2 }, &writer.interface); + + try writer.interface.flush(); +} + +/// Загружает значение типа T из JSON-файла. +/// Для Document после разбора делается клон через allocator, т.к. парсер выделяет память +/// из арены — при закрытии документа её нельзя освобождать нашим аллокатором. +pub fn loadFromFile(comptime T: type, allocator: std.mem.Allocator, path: []const u8) !T { + var file = try std.fs.cwd().openFile(path, .{}); + defer file.close(); + + const data = try file.readToEndAlloc(allocator, std.math.maxInt(usize)); + defer allocator.free(data); + + var parsed = try std.json.parseFromSlice(T, allocator, data, .{ .ignore_unknown_fields = true }); + if (T == Document) { + defer parsed.deinit(); + return try parsed.value.clone(allocator); + } + return parsed.value; +} diff --git a/src/ui/menu_bar.zig b/src/ui/menu_bar.zig index 0bc1ff1..1da7686 100644 --- a/src/ui/menu_bar.zig +++ b/src/ui/menu_bar.zig @@ -1,7 +1,8 @@ const std = @import("std"); const dvui = @import("dvui"); const WindowContext = @import("../WindowContext.zig"); -const document_json = @import("../persistence/document_json.zig"); +const Document = @import("../models/Document.zig"); +const json_io = @import("../persistence/json_io.zig"); pub fn menuBar(ctx: *WindowContext) void { var m = dvui.menu(@src(), .horizontal, .{ .background = true, .expand = .horizontal }); @@ -35,7 +36,7 @@ fn openDocumentDialog(ctx: *WindowContext) void { defer ctx.allocator.free(path_z); const path = path_z[0..path_z.len]; - const doc = document_json.loadFromFile(ctx.allocator, path) catch |err| { + const doc = json_io.loadFromFile(Document, ctx.allocator, path) catch |err| { std.debug.print("Open file error: {}\n", .{err}); return; }; @@ -58,7 +59,7 @@ fn saveAsDialog(ctx: *WindowContext) void { defer ctx.allocator.free(path_z); const path_raw = path_z[0..path_z.len]; - document_json.saveToFile(&open_doc.document, path_raw) catch |err| { + json_io.saveToFile(Document, &open_doc.document, path_raw) catch |err| { std.debug.print("Save file error: {}\n", .{err}); return; };