WIPWIP
This commit is contained in:
parent
c56e713510
commit
15f471f390
3 changed files with 182 additions and 2 deletions
|
|
@ -349,6 +349,12 @@ pub fn build_exe(
|
|||
});
|
||||
const syntax_mod = syntax_dep.module("syntax");
|
||||
|
||||
const nightwatch_dep = b.dependency("nightwatch", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
const nightwatch_mod = nightwatch_dep.module("nightwatch");
|
||||
|
||||
const help_mod = b.createModule(.{
|
||||
.root_source_file = b.path("help.md"),
|
||||
});
|
||||
|
|
@ -607,8 +613,7 @@ pub fn build_exe(
|
|||
const file_watcher_mod = b.createModule(.{
|
||||
.root_source_file = b.path("src/file_watcher.zig"),
|
||||
.imports = &.{
|
||||
.{ .name = "soft_root", .module = soft_root_mod },
|
||||
.{ .name = "log", .module = log_mod },
|
||||
.{ .name = "nightwatch", .module = nightwatch_mod },
|
||||
.{ .name = "cbor", .module = cbor_mod },
|
||||
.{ .name = "thespian", .module = thespian_mod },
|
||||
},
|
||||
|
|
|
|||
|
|
@ -50,6 +50,10 @@
|
|||
.url = "git+https://github.com/hexops/xcode-frameworks?ref=main#8a1cfb373587ea4c9bb1468b7c986462d8d4e10e",
|
||||
.hash = "N-V-__8AALShqgXkvqYU6f__FrA22SMWmi2TXCJjNTO1m8XJ",
|
||||
},
|
||||
.nightwatch = .{
|
||||
.url = "git+https://git.flow-control.dev/neurocyte/nightwatch?ref=master#a1e5e3e9a5126d1ff7ce4959a823ea12f20ef0ae",
|
||||
.hash = "nightwatch-0.1.0-uXzeH8WuAAC95apH6JQZQDzCGrXK2PRPq0rDPPxwUI3Z",
|
||||
},
|
||||
},
|
||||
.paths = .{
|
||||
"include",
|
||||
|
|
|
|||
171
src/file_watcher.zig
Normal file
171
src/file_watcher.zig
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
const std = @import("std");
|
||||
const tp = @import("thespian");
|
||||
const cbor = @import("cbor");
|
||||
const nightwatch = @import("nightwatch");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
pid: tp.pid_ref,
|
||||
|
||||
const Self = @This();
|
||||
const module_name = @typeName(Self);
|
||||
|
||||
pub const EventType = nightwatch.EventType;
|
||||
|
||||
pub const Error = error{
|
||||
FileWatcherSendFailed,
|
||||
ThespianSpawnFailed,
|
||||
OutOfMemory,
|
||||
};
|
||||
const SpawnError = error{ OutOfMemory, ThespianSpawnFailed };
|
||||
|
||||
pub fn watch(path: []const u8) Error!void {
|
||||
return send(.{ "watch", path });
|
||||
}
|
||||
|
||||
pub fn unwatch(path: []const u8) Error!void {
|
||||
return send(.{ "unwatch", path });
|
||||
}
|
||||
|
||||
pub fn start() SpawnError!void {
|
||||
_ = try get();
|
||||
}
|
||||
|
||||
pub fn shutdown() void {
|
||||
const pid = tp.env.get().proc(module_name);
|
||||
if (pid.expired()) return;
|
||||
pid.send(.{"shutdown"}) catch {};
|
||||
}
|
||||
|
||||
fn get() SpawnError!Self {
|
||||
const pid = tp.env.get().proc(module_name);
|
||||
return if (pid.expired()) create() else .{ .pid = pid };
|
||||
}
|
||||
|
||||
fn send(message: anytype) Error!void {
|
||||
return (try get()).pid.send(message) catch error.FileWatcherSendFailed;
|
||||
}
|
||||
|
||||
fn create() SpawnError!Self {
|
||||
const pid = try Process.create();
|
||||
defer pid.deinit();
|
||||
tp.env.get().proc_set(module_name, pid.ref());
|
||||
return .{ .pid = tp.env.get().proc(module_name) };
|
||||
}
|
||||
|
||||
const Process = struct {
|
||||
allocator: std.mem.Allocator,
|
||||
parent: tp.pid,
|
||||
receiver: Receiver,
|
||||
nw: nightwatch,
|
||||
fd_watcher: if (builtin.os.tag == .linux) tp.file_descriptor else void,
|
||||
handler: nightwatch.Handler,
|
||||
|
||||
const Receiver = tp.Receiver(*@This());
|
||||
|
||||
fn create() SpawnError!tp.pid {
|
||||
const allocator = std.heap.c_allocator;
|
||||
const self = try allocator.create(@This());
|
||||
errdefer allocator.destroy(self);
|
||||
self.* = .{
|
||||
.allocator = allocator,
|
||||
.parent = tp.self_pid().clone(),
|
||||
.receiver = Receiver.init(@This().receive, self),
|
||||
.nw = undefined,
|
||||
.fd_watcher = if (builtin.os.tag == .linux) undefined else {},
|
||||
.handler = init_handler(),
|
||||
};
|
||||
return tp.spawn_link(self.allocator, self, @This().start, module_name);
|
||||
}
|
||||
|
||||
fn deinit(self: *@This()) void {
|
||||
if (builtin.os.tag == .linux) self.fd_watcher.deinit();
|
||||
self.nw.deinit();
|
||||
self.parent.deinit();
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
|
||||
pub fn init_handler() nightwatch.Handler {
|
||||
return .{
|
||||
.vtable = &.{
|
||||
.change = handle_change,
|
||||
.rename = handle_rename,
|
||||
.wait_readable = if (builtin.os.tag == .linux) wait_readable else {},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fn start(self: *@This()) tp.result {
|
||||
errdefer self.deinit();
|
||||
_ = tp.set_trap(true);
|
||||
self.nw = nightwatch.init(self.allocator, &self.handler) catch |e| return tp.exit_error(e, @errorReturnTrace());
|
||||
if (builtin.os.tag == .linux)
|
||||
self.fd_watcher = tp.file_descriptor.init(module_name, self.nw.backend.inotify_fd) catch |e| {
|
||||
std.log.err("file_watcher.start: {}", .{e});
|
||||
return tp.exit_error(e, @errorReturnTrace());
|
||||
};
|
||||
tp.receive(&self.receiver);
|
||||
}
|
||||
|
||||
fn receive(self: *@This(), from: tp.pid_ref, m: tp.message) tp.result {
|
||||
errdefer self.deinit();
|
||||
return self.receive_safe(from, m) catch |e| switch (e) {
|
||||
error.ExitNormal => tp.exit_normal(),
|
||||
else => {
|
||||
const err = tp.exit_error(e, @errorReturnTrace());
|
||||
std.log.err("file_watcher.receive: {}", .{err});
|
||||
return err;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fn receive_safe(self: *@This(), _: tp.pid_ref, m: tp.message) (error{ExitNormal} || cbor.Error)!void {
|
||||
var path: []const u8 = undefined;
|
||||
var tag: []const u8 = undefined;
|
||||
var err_code: i64 = 0;
|
||||
var err_msg: []const u8 = undefined;
|
||||
|
||||
if (try cbor.match(m.buf, .{ "fd", tp.extract(&tag), "read_ready" })) {
|
||||
// re-arm the file_discriptor
|
||||
if (builtin.os.tag == .linux) {
|
||||
self.fd_watcher.wait_read() catch |e| std.log.err("file_watcher wait_read: {}", .{e});
|
||||
self.nw.handle_read_ready() catch |e| std.log.err("file_watcher handle_read_ready: {}", .{e});
|
||||
}
|
||||
} else if (try cbor.match(m.buf, .{ "fd", tp.extract(&tag), "read_error", tp.extract(&err_code), tp.extract(&err_msg) })) {
|
||||
std.log.err("fd read error on {s}: ({d}) {s}", .{ tag, err_code, err_msg });
|
||||
} else if (try cbor.match(m.buf, .{ "watch", tp.extract(&path) })) {
|
||||
self.nw.watch(path) catch |e| std.log.err("file_watcher watch: {s} -> {}", .{ path, e });
|
||||
} else if (try cbor.match(m.buf, .{ "unwatch", tp.extract(&path) })) {
|
||||
self.nw.unwatch(path) catch |e| std.log.err("file_watcher unwatch: {s} -> {}", .{ path, e });
|
||||
} else if (try cbor.match(m.buf, .{"shutdown"})) {
|
||||
return error.ExitNormal;
|
||||
} else if (try cbor.match(m.buf, .{ "exit", tp.more })) {
|
||||
return error.ExitNormal;
|
||||
} else {
|
||||
std.log.err("file_watcher.receive: {}", .{tp.unexpected(m)});
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_change(handler: *nightwatch.Handler, path: []const u8, event_type: EventType) error{HandlerFailed}!void {
|
||||
const self: *@This() = @alignCast(@fieldParentPtr("handler", handler));
|
||||
_ = self;
|
||||
_ = path;
|
||||
_ = event_type;
|
||||
}
|
||||
|
||||
fn handle_rename(handler: *nightwatch.Handler, src_path: []const u8, dst_path: []const u8) error{HandlerFailed}!void {
|
||||
const self: *@This() = @alignCast(@fieldParentPtr("handler", handler));
|
||||
_ = self;
|
||||
_ = src_path;
|
||||
_ = dst_path;
|
||||
}
|
||||
|
||||
fn wait_readable(handler: *nightwatch.Handler) error{HandlerFailed}!nightwatch.ReadableStatus {
|
||||
const self: *@This() = @alignCast(@fieldParentPtr("handler", handler));
|
||||
if (builtin.os.tag == .linux)
|
||||
self.fd_watcher.wait_read() catch |e| {
|
||||
std.log.err("file_watcher.wait_readable: {}", .{e});
|
||||
return error.HandlerFailed;
|
||||
};
|
||||
return .will_notify;
|
||||
}
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue