diff --git a/README.md b/README.md
index 8f0e3ac..fa219e1 100644
--- a/README.md
+++ b/README.md
@@ -118,7 +118,7 @@ coroutine pool.
The full A–B protocol is the state machine visualised:
-
+
This pattern mirrors the **Erlang client-server model**: the server (A) is passive,
the client (B) sends messages and waits for replies.
diff --git a/build.zig b/build.zig
index f62887c..e67f065 100644
--- a/build.zig
+++ b/build.zig
@@ -1,4 +1,5 @@
const std = @import("std");
+const addGraphFile = @import("polyrole").addGraphFile;
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
@@ -9,18 +10,19 @@ pub fn build(b: *std.Build) void {
"backend",
"Override the default event loop backend (io_uring, epoll, kqueue, iocp, poll)",
);
+
//polyrole
const polyrole = b.dependency("polyrole", .{
.target = target,
.optimize = optimize,
- });
+ }).module("root");
//zio
const zio = b.dependency("zio", .{
.target = target,
.optimize = optimize,
.backend = backend,
- });
+ }).module("zio");
const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source");
const tracy_callstack = b.option(bool, "tracy-callstack", "Include callstack information with Tracy data. Does nothing if -Dtracy is not provided") orelse (tracy != null);
@@ -41,19 +43,21 @@ pub fn build(b: *std.Build) void {
options.addOption([]const u8, "time", &iso_buf);
options.addOption([]const u8, "source_version", git_head);
+ const root_module = b.createModule(.{
+ .root_source_file = b.path("src/main.zig"),
+ .target = target,
+ .optimize = optimize,
+ .imports = &.{
+ .{ .name = "polyrole", .module = polyrole },
+ .{ .name = "zio", .module = zio },
+ },
+ .strip = if (tracy != null) false else if (optimize == .Debug) false else true,
+ });
+
const exe = b.addExecutable(.{
.name = "z3",
.use_llvm = true, //arch sframe bug: https://codeberg.org/ziglang/zig/issues/30959
- .root_module = b.createModule(.{
- .root_source_file = b.path("src/main.zig"),
- .target = target,
- .optimize = optimize,
- .imports = &.{
- .{ .name = "polyrole", .module = polyrole.module("root") },
- .{ .name = "zio", .module = zio.module("zio") },
- },
- .strip = if (tracy != null) false else if (optimize == .Debug) false else true,
- }),
+ .root_module = root_module,
});
exe.root_module.addOptions("build_options", options);
@@ -89,6 +93,35 @@ pub fn build(b: *std.Build) void {
if (b.args) |args| {
run_cmd.addArgs(args);
}
+ //generte graph
+ const run_gen_graph = b.step("gen-graph", "Generate graph.dot and graph.svg");
+ {
+ const graph_exe = b.addExecutable(.{
+ .name = "generate_graph",
+ .root_module = b.createModule(.{ .root_source_file = b.path("tools/graph.zig"), .target = target, .optimize = optimize, .imports = &.{
+ .{ .name = "polyrole", .module = polyrole },
+ .{ .name = "root_module", .module = root_module },
+ } }),
+ .use_llvm = true,
+ });
+
+ const run = b.addRunArtifact(graph_exe);
+ const graph_file = run.captureStdOut(.{});
+
+ const dot_cmd = b.addSystemCommand(&.{"dot"});
+
+ dot_cmd.addArg("-Tsvg");
+
+ dot_cmd.addFileArg(graph_file);
+
+ const graph_svg = dot_cmd.captureStdOut(.{});
+
+ const install_graph_dot = b.addInstallFileWithDir(graph_file, .{ .custom = "../graph" }, b.fmt("{s}.dot", .{"fsm"}));
+ const install_graph_svg = b.addInstallFileWithDir(graph_svg, .{ .custom = "../graph" }, b.fmt("{s}.svg", .{"fsm"}));
+
+ run_gen_graph.dependOn(&install_graph_svg.step);
+ run_gen_graph.dependOn(&install_graph_dot.step);
+ }
const exe_tests = b.addTest(.{
.root_module = exe.root_module,
diff --git a/graph/graph.dot b/graph/fsm.dot
similarity index 63%
rename from graph/graph.dot
rename to graph/fsm.dot
index 4def53b..8f825e3 100644
--- a/graph/graph.dot
+++ b/graph/fsm.dot
@@ -7,15 +7,15 @@ digraph fsm_state_graph {
}
subgraph cluster_fsm_1 {
label = "s3: { .client, .server }";
- 0[shape=rect, label=" c [0] Start .client -> { .server }", color = "red"];
- 1[shape=rect, label=" S [1] ServerLookupCredential .server -> { .client }", color = "green"];
- 2[shape=rect, label=" c [2] SigV4 .client -> { .server }", color = "blue"];
- 3[shape=rect, label=" c [3] Route .client -> { .server }", color = "brown"];
- 4[shape=rect, label=" c [4] Send .client -> { .server }", color = "navy"];
- 5[shape=rect, label=" S [5] Success .server -> { .client }", color = "teal"];
- 7[shape=rect, label=" S [7] Error .server -> { .client }", color = "magenta"];
- 8[shape=rect, label=" c [8] Send .client -> { .server }", color = "darkred"];
- 9[shape=rect, label=" S [9] WaitServer .server -> { .client }", color = "darkgreen"];
+ 0[shape=rect, label="🅱️[0] Start .client -> { .server }", color = "red"];
+ 1[shape=rect, label="🅰️[1] ServerLookupCredential .server -> { .client }", color = "green"];
+ 2[shape=rect, label="🅱️[2] SigV4 .client -> { .server }", color = "blue"];
+ 3[shape=rect, label="🅱️[3] Route .client -> { .server }", color = "brown"];
+ 4[shape=rect, label="🅱️[4] Send .client -> { .server }", color = "navy"];
+ 5[shape=rect, label="🅰️[5] Success .server -> { .client }", color = "teal"];
+ 7[shape=rect, label="🅰️[7] Error .server -> { .client }", color = "magenta"];
+ 8[shape=rect, label="🅱️[8] Send .client -> { .server }", color = "darkred"];
+ 9[shape=rect, label="🅰️[9] WaitServer .server -> { .client }", color = "darkgreen"];
}
0 -> 1 [label = "req_credential_and_id", color = "red", fontcolor = "red"];
0 -> 8 [label = "failed", color = "red", fontcolor = "red"];
diff --git a/graph/fsm.svg b/graph/fsm.svg
new file mode 100644
index 0000000..85abacf
--- /dev/null
+++ b/graph/fsm.svg
@@ -0,0 +1,200 @@
+
+
+
+
+
diff --git a/graph/graph.svg b/graph/graph.svg
deleted file mode 100644
index 84b5f4c..0000000
--- a/graph/graph.svg
+++ /dev/null
@@ -1,200 +0,0 @@
-
-
-
-
-
diff --git a/src/main.zig b/src/main.zig
index 47446b4..f989333 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -9,8 +9,8 @@ const polyrole = @import("polyrole");
const s3 = @import("s3.zig");
const zs3 = @import("zs3.zig");
const run = @import("run.zig");
-const Runner = s3.Runner;
-const EnterState = s3.Start;
+pub const Runner = s3.Runner;
+pub const EnterFsmState = s3.Start;
const build_options = @import("build_options");
pub fn main(init: std.process.Init) !void {
@@ -107,14 +107,6 @@ pub fn main(init: std.process.Init) !void {
std.log.warn("Using built-in default credentials (admin:minioadmin:minioadmin) — DO NOT USE IN PRODUCTION", .{});
}
- // var dot_file = try Io.Dir.cwd().createFile(io, "graph.dot", .{});
- // var dot_file_wirter = dot_file.writer(io, &.{});
- // var graph = try polyrole.Graph.initWithFsm(gpa, EnterState);
- // var emoj_map: std.StringHashMap([]const u8) = .init(gpa);
- // try emoj_map.put("client", " c ");
- // try emoj_map.put("server", " S ");
- // try graph.generateDot(&emoj_map, &dot_file_wirter.interface);
-
Io.Dir.cwd().createDir(io, data_dir, .default_dir) catch |err| switch (err) {
error.PathAlreadyExists => {},
else => return err,
@@ -225,7 +217,7 @@ fn accept_loop(
try ctx.init(data_dir, tmp_dir, io, stream, gpa);
const client_future = try io.concurrent(run.client, .{
- Runner.idFromState(EnterState),
+ Runner.idFromState(EnterFsmState),
msg_channel,
ctx,
clean_channel,
diff --git a/tools/graph.zig b/tools/graph.zig
new file mode 100644
index 0000000..71a6641
--- /dev/null
+++ b/tools/graph.zig
@@ -0,0 +1,25 @@
+const std = @import("std");
+const polyrole = @import("polyrole");
+const root_module = @import("root_module");
+const EnterFsmState = root_module.EnterFsmState;
+const Runner = root_module.Runner;
+
+pub fn main(init: std.process.Init) !void {
+ {
+ const io = init.io;
+ var gpa_instance = std.heap.DebugAllocator(.{}){};
+ const gpa = gpa_instance.allocator();
+ var graph = try polyrole.Graph.initWithFsm(gpa, EnterFsmState);
+ defer graph.deinit();
+ var stdout_buffer: [1024]u8 = undefined;
+ var stdout_writer = std.Io.File.stdout().writer(io, &stdout_buffer);
+ const writer = &stdout_writer.interface;
+ defer writer.flush() catch @panic("Failed to flush");
+
+ var emoj_map: std.StringHashMap([]const u8) = .init(gpa);
+ try emoj_map.put("client", "🅱️");
+ try emoj_map.put("server", "🅰️");
+
+ try graph.generateDot(&emoj_map, writer);
+ }
+}