make it compile
This commit is contained in:
parent
a584d3ed46
commit
02c17c4d71
3 changed files with 147 additions and 103 deletions
131
build.zig
131
build.zig
|
@ -1,81 +1,126 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
|
// Although this function looks imperative, note that its job is to
|
||||||
|
// declaratively construct a build graph that will be executed by an external
|
||||||
|
// runner.
|
||||||
pub fn build(b: *std.Build) void {
|
pub fn build(b: *std.Build) void {
|
||||||
|
// Standard target options allows the person running `zig build` to choose
|
||||||
|
// what target to build for. Here we do not override the defaults, which
|
||||||
|
// means any target is allowed, and the default is native. Other options
|
||||||
|
// for restricting supported target set are available.
|
||||||
const target = b.standardTargetOptions(.{});
|
const target = b.standardTargetOptions(.{});
|
||||||
|
|
||||||
|
// Standard optimization options allow the person running `zig build` to select
|
||||||
|
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
|
||||||
|
// set a preferred release mode, allowing the user to decide how to optimize.
|
||||||
const optimize = b.standardOptimizeOption(.{});
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
// Add mvzr dependency
|
const mvzr_dep = b.dependency("mvzr", .{});
|
||||||
const mvzr_dep = b.dependency("mvzr", .{
|
// This creates a "module", which represents a collection of source files alongside
|
||||||
|
// some compilation options, such as optimization mode and linked system libraries.
|
||||||
|
// Every executable or library we compile will be based on one or more modules.
|
||||||
|
const lib_mod = b.createModule(.{
|
||||||
|
// `root_source_file` is the Zig "entry point" of the module. If a module
|
||||||
|
// only contains e.g. external object files, you can make this `null`.
|
||||||
|
// In this case the main source file is merely a path, however, in more
|
||||||
|
// complicated build scripts, this could be a generated file.
|
||||||
|
.root_source_file = b.path("src/root.zig"),
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create the library module
|
lib_mod.addImport("mvzr", mvzr_dep.module("mvzr"));
|
||||||
const lib_mod = b.addModule("syncthing_events_lib", .{
|
|
||||||
.source_file = .{ .path = "src/root.zig" },
|
// We will also create a module for our other entry point, 'main.zig'.
|
||||||
.dependencies = &.{
|
const exe_mod = b.createModule(.{
|
||||||
.{ .name = "mvzr", .module = mvzr_dep.module("mvzr") },
|
// `root_source_file` is the Zig "entry point" of the module. If a module
|
||||||
},
|
// only contains e.g. external object files, you can make this `null`.
|
||||||
|
// In this case the main source file is merely a path, however, in more
|
||||||
|
// complicated build scripts, this could be a generated file.
|
||||||
|
.root_source_file = b.path("src/main.zig"),
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create the executable module
|
// Modules can depend on one another using the `std.Build.Module.addImport` function.
|
||||||
const exe_mod = b.addModule("syncthing_events_exe", .{
|
// This is what allows Zig source code to use `@import("foo")` where 'foo' is not a
|
||||||
.source_file = .{ .path = "src/main.zig" },
|
// file path. In this case, we set up `exe_mod` to import `lib_mod`.
|
||||||
.dependencies = &.{
|
exe_mod.addImport("syncthing_events_lib", lib_mod);
|
||||||
.{ .name = "syncthing_events_lib", .module = lib_mod },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create the library
|
// Now, we will create a static library based on the module we created above.
|
||||||
const lib = b.addStaticLibrary(.{
|
// This creates a `std.Build.Step.Compile`, which is the build step responsible
|
||||||
|
// for actually invoking the compiler.
|
||||||
|
const lib = b.addLibrary(.{
|
||||||
|
.linkage = .static,
|
||||||
.name = "syncthing_events",
|
.name = "syncthing_events",
|
||||||
.root_source_file = .{ .path = "src/root.zig" },
|
.root_module = lib_mod,
|
||||||
.target = target,
|
|
||||||
.optimize = optimize,
|
|
||||||
});
|
});
|
||||||
lib.addModule("mvzr", mvzr_dep.module("mvzr"));
|
|
||||||
|
// This declares intent for the library to be installed into the standard
|
||||||
|
// location when the user invokes the "install" step (the default step when
|
||||||
|
// running `zig build`).
|
||||||
b.installArtifact(lib);
|
b.installArtifact(lib);
|
||||||
|
|
||||||
// Create the executable
|
// This creates another `std.Build.Step.Compile`, but this one builds an executable
|
||||||
|
// rather than a static library.
|
||||||
const exe = b.addExecutable(.{
|
const exe = b.addExecutable(.{
|
||||||
.name = "syncthing_events",
|
.name = "syncthing_events",
|
||||||
.root_source_file = .{ .path = "src/main.zig" },
|
.root_module = exe_mod,
|
||||||
.target = target,
|
|
||||||
.optimize = optimize,
|
|
||||||
});
|
});
|
||||||
exe.addModule("syncthing_events_lib", lib_mod);
|
|
||||||
b.installArtifact(exe);
|
|
||||||
|
|
||||||
// Create run command
|
// This declares intent for the executable to be installed into the
|
||||||
|
// standard location when the user invokes the "install" step (the default
|
||||||
|
// step when running `zig build`).
|
||||||
|
const no_bin = b.option(bool, "no-bin", "skip emitting binary") orelse false;
|
||||||
|
const no_llvm = b.option(bool, "no-llvm", "skip use of llvm") orelse false;
|
||||||
|
lib.use_llvm = !no_llvm;
|
||||||
|
exe.use_llvm = !no_llvm;
|
||||||
|
if (no_bin) {
|
||||||
|
b.getInstallStep().dependOn(&exe.step);
|
||||||
|
} else {
|
||||||
|
b.installArtifact(exe);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This *creates* a Run step in the build graph, to be executed when another
|
||||||
|
// step is evaluated that depends on it. The next line below will establish
|
||||||
|
// such a dependency.
|
||||||
const run_cmd = b.addRunArtifact(exe);
|
const run_cmd = b.addRunArtifact(exe);
|
||||||
|
|
||||||
|
// By making the run step depend on the install step, it will be run from the
|
||||||
|
// installation directory rather than directly from within the cache directory.
|
||||||
|
// This is not necessary, however, if the application depends on other installed
|
||||||
|
// files, this ensures they will be present and in the expected location.
|
||||||
|
run_cmd.step.dependOn(b.getInstallStep());
|
||||||
|
|
||||||
|
// This allows the user to pass arguments to the application in the build
|
||||||
|
// command itself, like this: `zig build run -- arg1 arg2 etc`
|
||||||
if (b.args) |args| {
|
if (b.args) |args| {
|
||||||
run_cmd.addArgs(args);
|
run_cmd.addArgs(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This creates a build step. It will be visible in the `zig build --help` menu,
|
||||||
|
// and can be selected like this: `zig build run`
|
||||||
|
// This will evaluate the `run` step rather than the default, which is "install".
|
||||||
const run_step = b.step("run", "Run the app");
|
const run_step = b.step("run", "Run the app");
|
||||||
run_step.dependOn(&run_cmd.step);
|
run_step.dependOn(&run_cmd.step);
|
||||||
|
|
||||||
// Create test step
|
// Creates a step for unit testing. This only builds the test executable
|
||||||
|
// but does not run it.
|
||||||
const lib_unit_tests = b.addTest(.{
|
const lib_unit_tests = b.addTest(.{
|
||||||
.root_source_file = .{ .path = "src/root.zig" },
|
.root_module = lib_mod,
|
||||||
.target = target,
|
|
||||||
.optimize = optimize,
|
|
||||||
});
|
});
|
||||||
lib_unit_tests.addModule("mvzr", mvzr_dep.module("mvzr"));
|
|
||||||
|
|
||||||
const exe_unit_tests = b.addTest(.{
|
|
||||||
.root_source_file = .{ .path = "src/main.zig" },
|
|
||||||
.target = target,
|
|
||||||
.optimize = optimize,
|
|
||||||
});
|
|
||||||
exe_unit_tests.addModule("syncthing_events_lib", lib_mod);
|
|
||||||
|
|
||||||
const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
|
const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
|
||||||
const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
|
|
||||||
|
|
||||||
|
const exe_unit_tests = b.addTest(.{
|
||||||
|
.root_module = exe_mod,
|
||||||
|
});
|
||||||
|
|
||||||
|
const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
|
||||||
|
// Similar to creating the run step earlier, this exposes a `test` step to
|
||||||
|
// the `zig build --help` menu, providing a way for the user to request
|
||||||
|
// running the unit tests.
|
||||||
const test_step = b.step("test", "Run unit tests");
|
const test_step = b.step("test", "Run unit tests");
|
||||||
test_step.dependOn(&run_lib_unit_tests.step);
|
test_step.dependOn(&run_lib_unit_tests.step);
|
||||||
test_step.dependOn(&run_exe_unit_tests.step);
|
test_step.dependOn(&run_exe_unit_tests.step);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
20
src/main.zig
20
src/main.zig
|
@ -9,21 +9,21 @@ const Args = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
var gpa = std.heap.GeneralPurposeAllocator(.{}).init;
|
||||||
defer _ = gpa.deinit();
|
defer _ = gpa.deinit();
|
||||||
const allocator = gpa.allocator();
|
const allocator = gpa.allocator();
|
||||||
|
|
||||||
const args = try parseArgs(allocator);
|
const args = try parseArgs(allocator);
|
||||||
|
|
||||||
var config = try loadConfig(allocator, args.config_path);
|
var parsed_config = try loadConfig(allocator, args.config_path);
|
||||||
defer config.deinit(allocator);
|
defer parsed_config.deinit();
|
||||||
|
var config = parsed_config.value;
|
||||||
|
|
||||||
if (args.syncthing_url) |url| {
|
if (args.syncthing_url) |url| {
|
||||||
config.syncthing_url = url;
|
config.syncthing_url = url;
|
||||||
}
|
}
|
||||||
|
|
||||||
var poller = try EventPoller.init(allocator, config);
|
var poller = try EventPoller.init(allocator, config);
|
||||||
defer poller.deinit();
|
|
||||||
|
|
||||||
const stdout = std.io.getStdOut().writer();
|
const stdout = std.io.getStdOut().writer();
|
||||||
try stdout.print("Monitoring Syncthing events at {s}\n", .{config.syncthing_url});
|
try stdout.print("Monitoring Syncthing events at {s}\n", .{config.syncthing_url});
|
||||||
|
@ -83,7 +83,7 @@ fn parseArgs(allocator: std.mem.Allocator) !Args {
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn loadConfig(allocator: Allocator, path: []const u8) !Config {
|
fn loadConfig(allocator: std.mem.Allocator, path: []const u8) !std.json.Parsed(Config) {
|
||||||
const file = try std.fs.cwd().openFile(path, .{});
|
const file = try std.fs.cwd().openFile(path, .{});
|
||||||
defer file.close();
|
defer file.close();
|
||||||
|
|
||||||
|
@ -93,13 +93,7 @@ fn loadConfig(allocator: Allocator, path: []const u8) !Config {
|
||||||
|
|
||||||
const ext = std.fs.path.extension(path);
|
const ext = std.fs.path.extension(path);
|
||||||
if (std.mem.eql(u8, ext, ".json")) {
|
if (std.mem.eql(u8, ext, ".json")) {
|
||||||
var parser = std.json.Parser.init(allocator, false);
|
return try std.json.parseFromSlice(Config, allocator, content, .{});
|
||||||
defer parser.deinit();
|
|
||||||
|
|
||||||
var tree = try parser.parse(content);
|
|
||||||
defer tree.deinit();
|
|
||||||
|
|
||||||
return try std.json.parse(Config, &tree, .{ .allocator = allocator });
|
|
||||||
} else if (std.mem.eql(u8, ext, ".zon")) {
|
} else if (std.mem.eql(u8, ext, ".zon")) {
|
||||||
// TODO: Implement ZON parsing
|
// TODO: Implement ZON parsing
|
||||||
return error.UnsupportedConfigFormat;
|
return error.UnsupportedConfigFormat;
|
||||||
|
@ -164,4 +158,4 @@ test "config loading" {
|
||||||
try testing.expectEqual(@as(u32, 3), config.max_retries);
|
try testing.expectEqual(@as(u32, 3), config.max_retries);
|
||||||
try testing.expectEqual(@as(u32, 2000), config.retry_delay_ms);
|
try testing.expectEqual(@as(u32, 2000), config.retry_delay_ms);
|
||||||
try testing.expectEqual(@as(usize, 1), config.watchers.len);
|
try testing.expectEqual(@as(usize, 1), config.watchers.len);
|
||||||
}
|
}
|
||||||
|
|
99
src/root.zig
99
src/root.zig
|
@ -1,16 +1,14 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const json = std.json;
|
|
||||||
const Allocator = std.mem.Allocator;
|
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
const mvzr = @import("mvzr");
|
const mvzr = @import("mvzr");
|
||||||
|
|
||||||
pub const Config = struct {
|
pub const Config = struct {
|
||||||
syncthing_url: []const u8 = "http://localhost:8384",
|
syncthing_url: []const u8 = "http://localhost:8384",
|
||||||
max_retries: u32 = 5,
|
max_retries: usize = std.math.maxInt(usize),
|
||||||
retry_delay_ms: u32 = 1000,
|
retry_delay_ms: u32 = 1000,
|
||||||
watchers: []Watcher,
|
watchers: []Watcher,
|
||||||
|
|
||||||
pub fn deinit(self: *Config, allocator: Allocator) void {
|
pub fn deinit(self: *Config, allocator: std.mem.Allocator) void {
|
||||||
for (self.watchers) |*watcher| {
|
for (self.watchers) |*watcher| {
|
||||||
watcher.deinit(allocator);
|
watcher.deinit(allocator);
|
||||||
}
|
}
|
||||||
|
@ -22,9 +20,9 @@ pub const Watcher = struct {
|
||||||
folder: []const u8,
|
folder: []const u8,
|
||||||
path_pattern: []const u8,
|
path_pattern: []const u8,
|
||||||
command: []const u8,
|
command: []const u8,
|
||||||
compiled_pattern: ?mvzr.Pattern = null,
|
compiled_pattern: ?mvzr.Regex = null,
|
||||||
|
|
||||||
pub fn init(allocator: Allocator, folder: []const u8, path_pattern: []const u8, command: []const u8) !Watcher {
|
pub fn init(allocator: std.mem.Allocator, folder: []const u8, path_pattern: []const u8, command: []const u8) !Watcher {
|
||||||
var watcher = Watcher{
|
var watcher = Watcher{
|
||||||
.folder = try allocator.dupe(u8, folder),
|
.folder = try allocator.dupe(u8, folder),
|
||||||
.path_pattern = try allocator.dupe(u8, path_pattern),
|
.path_pattern = try allocator.dupe(u8, path_pattern),
|
||||||
|
@ -35,7 +33,7 @@ pub const Watcher = struct {
|
||||||
return watcher;
|
return watcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Watcher, allocator: Allocator) void {
|
pub fn deinit(self: *Watcher, allocator: std.mem.Allocator) void {
|
||||||
if (self.compiled_pattern) |*pattern| {
|
if (self.compiled_pattern) |*pattern| {
|
||||||
pattern.deinit();
|
pattern.deinit();
|
||||||
}
|
}
|
||||||
|
@ -49,7 +47,7 @@ pub const Watcher = struct {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (self.compiled_pattern) |pattern| {
|
if (self.compiled_pattern) |pattern| {
|
||||||
return pattern.match(path);
|
return pattern.match(path) != null;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -60,8 +58,8 @@ pub const SyncthingEvent = struct {
|
||||||
type: []const u8,
|
type: []const u8,
|
||||||
folder: []const u8,
|
folder: []const u8,
|
||||||
path: []const u8,
|
path: []const u8,
|
||||||
|
|
||||||
pub fn fromJson(allocator: Allocator, value: json.Value) !SyncthingEvent {
|
pub fn fromJson(allocator: std.mem.Allocator, value: std.json.Value) !SyncthingEvent {
|
||||||
return SyncthingEvent{
|
return SyncthingEvent{
|
||||||
.id = value.object.get("id").?.integer,
|
.id = value.object.get("id").?.integer,
|
||||||
.type = try allocator.dupe(u8, value.object.get("type").?.string),
|
.type = try allocator.dupe(u8, value.object.get("type").?.string),
|
||||||
|
@ -70,7 +68,7 @@ pub const SyncthingEvent = struct {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *SyncthingEvent, allocator: Allocator) void {
|
pub fn deinit(self: *SyncthingEvent, allocator: std.mem.Allocator) void {
|
||||||
allocator.free(self.type);
|
allocator.free(self.type);
|
||||||
allocator.free(self.folder);
|
allocator.free(self.folder);
|
||||||
allocator.free(self.path);
|
allocator.free(self.path);
|
||||||
|
@ -78,36 +76,44 @@ pub const SyncthingEvent = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const EventPoller = struct {
|
pub const EventPoller = struct {
|
||||||
allocator: Allocator,
|
allocator: std.mem.Allocator,
|
||||||
config: Config,
|
config: Config,
|
||||||
last_id: i64,
|
last_id: ?i64,
|
||||||
client: std.http.Client,
|
|
||||||
|
|
||||||
pub fn init(allocator: Allocator, config: Config) !EventPoller {
|
pub fn init(allocator: std.mem.Allocator, config: Config) !EventPoller {
|
||||||
return EventPoller{
|
return .{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.config = config,
|
.config = config,
|
||||||
.last_id = 0,
|
.last_id = null,
|
||||||
.client = std.http.Client.init(allocator),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *EventPoller) void {
|
|
||||||
self.client.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn poll(self: *EventPoller) ![]SyncthingEvent {
|
pub fn poll(self: *EventPoller) ![]SyncthingEvent {
|
||||||
var retry_count: u32 = 0;
|
var client = std.http.Client{ .allocator = self.allocator };
|
||||||
|
var arena = std.heap.ArenaAllocator.init(self.allocator);
|
||||||
|
defer arena.deinit();
|
||||||
|
const aa = arena.allocator();
|
||||||
|
try client.initDefaultProxies(aa);
|
||||||
|
|
||||||
|
var retry_count: usize = self.config.max_retries;
|
||||||
while (retry_count < self.config.max_retries) : (retry_count += 1) {
|
while (retry_count < self.config.max_retries) : (retry_count += 1) {
|
||||||
var url_buf: [256]u8 = undefined;
|
var url_buf: [1024]u8 = undefined;
|
||||||
const url = try std.fmt.bufPrint(&url_buf, "{s}/rest/events?events=ItemFinished&since={d}", .{
|
var since_buf: [100]u8 = undefined;
|
||||||
self.config.syncthing_url, self.last_id,
|
const since = if (self.last_id) |id|
|
||||||
|
try std.fmt.bufPrint(&since_buf, "&since={d}", .{id})
|
||||||
|
else
|
||||||
|
"";
|
||||||
|
const url = try std.fmt.bufPrint(&url_buf, "{s}/rest/events?events=ItemFinished{s}", .{
|
||||||
|
self.config.syncthing_url, since,
|
||||||
});
|
});
|
||||||
|
|
||||||
var events = std.ArrayList(SyncthingEvent).init(self.allocator);
|
var al = std.ArrayList(u8).init(self.allocator);
|
||||||
errdefer events.deinit();
|
defer al.deinit();
|
||||||
|
|
||||||
var response = self.client.get(url) catch |err| {
|
const response = client.fetch(.{
|
||||||
|
.location = .{ .url = url },
|
||||||
|
.response_storage = .{ .dynamic = &al },
|
||||||
|
}) catch |err| {
|
||||||
std.log.err("HTTP request failed: {s}", .{@errorName(err)});
|
std.log.err("HTTP request failed: {s}", .{@errorName(err)});
|
||||||
if (retry_count + 1 < self.config.max_retries) {
|
if (retry_count + 1 < self.config.max_retries) {
|
||||||
std.time.sleep(self.config.retry_delay_ms * std.time.ns_per_ms);
|
std.time.sleep(self.config.retry_delay_ms * std.time.ns_per_ms);
|
||||||
|
@ -115,10 +121,9 @@ pub const EventPoller = struct {
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
};
|
};
|
||||||
defer response.deinit();
|
|
||||||
|
|
||||||
if (response.status_code != 200) {
|
if (response.status != .ok) {
|
||||||
std.log.err("HTTP status code: {d}", .{response.status_code});
|
std.log.err("HTTP status code: {}", .{response.status});
|
||||||
if (retry_count + 1 < self.config.max_retries) {
|
if (retry_count + 1 < self.config.max_retries) {
|
||||||
std.time.sleep(self.config.retry_delay_ms * std.time.ns_per_ms);
|
std.time.sleep(self.config.retry_delay_ms * std.time.ns_per_ms);
|
||||||
continue;
|
continue;
|
||||||
|
@ -126,39 +131,39 @@ pub const EventPoller = struct {
|
||||||
return error.HttpError;
|
return error.HttpError;
|
||||||
}
|
}
|
||||||
|
|
||||||
var parser = json.Parser.init(self.allocator, false);
|
var events = std.ArrayList(SyncthingEvent).init(self.allocator);
|
||||||
defer parser.deinit();
|
errdefer events.deinit();
|
||||||
|
|
||||||
var tree = try parser.parse(response.body);
|
const parsed = try std.json.parseFromSliceLeaky(std.json.Value, aa, al.items, .{});
|
||||||
defer tree.deinit();
|
|
||||||
|
|
||||||
const array = tree.root.array;
|
const array = parsed.array;
|
||||||
for (array.items) |item| {
|
for (array.items) |item| {
|
||||||
const event = try SyncthingEvent.fromJson(self.allocator, item);
|
const event = try SyncthingEvent.fromJson(self.allocator, item);
|
||||||
try events.append(event);
|
try events.append(event);
|
||||||
if (event.id > self.last_id) {
|
if (self.last_id == null or event.id > self.last_id.?) {
|
||||||
self.last_id = event.id;
|
self.last_id = event.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return events.toOwnedSlice();
|
return try events.toOwnedSlice();
|
||||||
}
|
}
|
||||||
return error.MaxRetriesExceeded;
|
return error.MaxRetriesExceeded;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn executeCommand(allocator: Allocator, command: []const u8, event: SyncthingEvent) !void {
|
pub fn executeCommand(allocator: std.mem.Allocator, command: []const u8, event: SyncthingEvent) !void {
|
||||||
var expanded_cmd = try expandCommandVariables(allocator, command, event);
|
const expanded_cmd = try expandCommandVariables(allocator, command, event);
|
||||||
defer allocator.free(expanded_cmd);
|
defer allocator.free(expanded_cmd);
|
||||||
|
|
||||||
var process = std.ChildProcess.init(&[_][]const u8{ "sh", "-c", expanded_cmd }, allocator);
|
// TODO: Should this spawn sh like this, or exec directly?
|
||||||
|
var process = std.process.Child.init(&[_][]const u8{ "sh", "-c", expanded_cmd }, allocator);
|
||||||
process.stdout_behavior = .Inherit;
|
process.stdout_behavior = .Inherit;
|
||||||
process.stderr_behavior = .Inherit;
|
process.stderr_behavior = .Inherit;
|
||||||
|
|
||||||
_ = try process.spawnAndWait();
|
_ = try process.spawnAndWait();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expandCommandVariables(allocator: Allocator, command: []const u8, event: SyncthingEvent) ![]const u8 {
|
fn expandCommandVariables(allocator: std.mem.Allocator, command: []const u8, event: SyncthingEvent) ![]const u8 {
|
||||||
var result = std.ArrayList(u8).init(allocator);
|
var result = std.ArrayList(u8).init(allocator);
|
||||||
defer result.deinit();
|
defer result.deinit();
|
||||||
|
|
||||||
|
@ -203,13 +208,13 @@ test "config parsing" {
|
||||||
\\}
|
\\}
|
||||||
;
|
;
|
||||||
|
|
||||||
var parser = json.Parser.init(testing.allocator, false);
|
var parser = std.json.Parser.init(testing.allocator, false);
|
||||||
defer parser.deinit();
|
defer parser.deinit();
|
||||||
|
|
||||||
var tree = try parser.parse(config_json);
|
var tree = try parser.parse(config_json);
|
||||||
defer tree.deinit();
|
defer tree.deinit();
|
||||||
|
|
||||||
const config = try json.parse(Config, &tree, .{ .allocator = testing.allocator });
|
const config = try std.json.parse(Config, &tree, .{ .allocator = testing.allocator });
|
||||||
defer config.deinit(testing.allocator);
|
defer config.deinit(testing.allocator);
|
||||||
|
|
||||||
try testing.expectEqualStrings("http://test:8384", config.syncthing_url);
|
try testing.expectEqualStrings("http://test:8384", config.syncthing_url);
|
||||||
|
@ -231,7 +236,7 @@ test "event parsing" {
|
||||||
\\}
|
\\}
|
||||||
;
|
;
|
||||||
|
|
||||||
var parser = json.Parser.init(testing.allocator, false);
|
var parser = std.json.Parser.init(testing.allocator, false);
|
||||||
defer parser.deinit();
|
defer parser.deinit();
|
||||||
|
|
||||||
var tree = try parser.parse(event_json);
|
var tree = try parser.parse(event_json);
|
||||||
|
@ -277,4 +282,4 @@ test "watcher pattern matching" {
|
||||||
try testing.expect(watcher.matches("photos", "test.jpeg"));
|
try testing.expect(watcher.matches("photos", "test.jpeg"));
|
||||||
try testing.expect(!watcher.matches("photos", "test.png"));
|
try testing.expect(!watcher.matches("photos", "test.png"));
|
||||||
try testing.expect(!watcher.matches("documents", "test.jpg"));
|
try testing.expect(!watcher.matches("documents", "test.jpg"));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue