const std = @import("std"); const builtin = @import("builtin"); const helper = @import("helper.zig"); const Data = @import("Data.zig"); const Parsed = @import("Parsed.zig"); const Log = @import("Log.zig"); pub const Status = enum(c_int) { unchanged = 0, created, deleted, modified, }; pub const Change = struct { status: Status, // never .unchanged id: Data.Id, source: ?[]const u8 = null, output: ?[]const u8 = null, title: ?[]const u8 = null, description: ?[]const u8 = null, author: ?[]const u8 = null, timestamp: i64, pub fn copy(self: Change, allocator: std.mem.Allocator) !Change { return Change{ .status = self.status, .id = self.id, .source = if (self.source) |src| try allocator.dupe(u8, src) else null, .output = if (self.output) |output| try allocator.dupe(u8, output) else null, .title = if (self.title) |title| try allocator.dupe(u8, title) else null, .description = if (self.description) |description| try allocator.dupe(u8, description) else null, .author = if (self.author) |author| try allocator.dupe(u8, author) else null, .timestamp = self.timestamp, }; } pub fn deinit(self: Change, allocator: std.mem.Allocator) void { if (self.source) |src| allocator.free(src); if (self.output) |output| allocator.free(output); if (self.title) |title| allocator.free(title); if (self.description) |description| allocator.free(description); if (self.author) |author| allocator.free(author); } }; on_change: ?[]const u8 = null, on_create: ?[]const u8 = null, on_delete: ?[]const u8 = null, on_modify: ?[]const u8 = null, const Callbacks = @This(); fn callSystemProgram( program: []const u8, stdin_input: []const u8, cwd: std.fs.Dir, allocator: std.mem.Allocator, ) !void { var args = std.ArrayList([]const u8).init(allocator); defer args.deinit(); if (builtin.os.tag == .windows) { try args.append("cmd.exe"); try args.append("/C"); } else { try args.append("/bin/sh"); try args.append("-c"); } try args.append(program); var child = std.process.Child.init(args.items, allocator); child.cwd_dir = cwd; child.stdin_behavior = .Pipe; child.stdout_behavior = .Ignore; child.stderr_behavior = .Inherit; var env_map = std.process.EnvMap.init(allocator); defer env_map.deinit(); try env_map.put("FROM_MAKKO", "YES"); child.env_map = &env_map; try child.spawn(); if (child.stdin) |stdin| { _ = try stdin.writeAll(stdin_input); stdin.close(); child.stdin = null; } _ = try child.wait(); } fn lessThan(context: void, a: Change, b: Change) bool { _ = context; return @intFromEnum(a.status) < @intFromEnum(b.status); } pub fn run( callbacks: Callbacks, changes: []Change, cwd: std.fs.Dir, log: Log, allocator: std.mem.Allocator, ) !void { if (changes.len == 0) return; //std.sort.block(Change, changes, {}, lessThan); var created_changes = std.ArrayList(Change).init(allocator); var deleted_changes = std.ArrayList(Change).init(allocator); var modified_changes = std.ArrayList(Change).init(allocator); defer { created_changes.deinit(); deleted_changes.deinit(); modified_changes.deinit(); } for (changes) |change| { switch (change.status) { .created => try created_changes.append(change), .deleted => try deleted_changes.append(change), .modified => try modified_changes.append(change), .unchanged => {}, } } if ((created_changes.items.len > 0 and callbacks.on_create != null) or (deleted_changes.items.len > 0 and callbacks.on_delete != null) or (modified_changes.items.len > 0 and callbacks.on_modify != null) or callbacks.on_change != null) log.header("Callbacks"); if (callbacks.on_change) |on_change| { const json = try std.json.stringifyAlloc( allocator, changes, .{}, ); defer allocator.free(json); log.raw("- on_change: {s}\n", .{on_change}); try callSystemProgram(on_change, json, cwd, allocator); } if (callbacks.on_create) |on_create| { if (created_changes.items.len > 0) { const json = try std.json.stringifyAlloc( allocator, created_changes.items, .{}, ); defer allocator.free(json); log.raw("- on_create: {s}\n", .{on_create}); try callSystemProgram(on_create, json, cwd, allocator); } } if (callbacks.on_delete) |on_delete| { if (deleted_changes.items.len > 0) { const json = try std.json.stringifyAlloc( allocator, deleted_changes.items, .{}, ); defer allocator.free(json); log.raw("- on_delete: {s}\n", .{on_delete}); try callSystemProgram(on_delete, json, cwd, allocator); } } if (callbacks.on_modify) |on_modify| { if (modified_changes.items.len > 0) { const json = try std.json.stringifyAlloc( allocator, modified_changes.items, .{}, ); defer allocator.free(json); log.raw("- on_modify: {s}\n", .{on_modify}); try callSystemProgram(on_modify, json, cwd, allocator); } } }