diff --git a/build.zig b/build.zig index 9f0876c..d759871 100644 --- a/build.zig +++ b/build.zig @@ -1,34 +1,15 @@ const std = @import("std"); pub fn build(b: *std.build.Builder) 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(.{}); - - // Standard release options allow the person running `zig build` to select - // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. const mode = b.standardReleaseOptions(); - const exe = b.addExecutable("Ziggy", "src/main.zig"); - exe.setTarget(target); - exe.setBuildMode(mode); - exe.install(); + const lib = b.addStaticLibrary("zspinner", "src/lib.zig"); + lib.setBuildMode(mode); + lib.install(); - const run_cmd = exe.run(); - run_cmd.step.dependOn(b.getInstallStep()); - if (b.args) |args| { - run_cmd.addArgs(args); - } + const main_tests = b.addTest("src/lib.zig"); + main_tests.setBuildMode(mode); - const run_step = b.step("run", "Run the app"); - run_step.dependOn(&run_cmd.step); - - const exe_tests = b.addTest("src/main.zig"); - exe_tests.setTarget(target); - exe_tests.setBuildMode(mode); - - const test_step = b.step("test", "Run unit tests"); - test_step.dependOn(&exe_tests.step); + const test_step = b.step("test", "Run library tests"); + test_step.dependOn(&main_tests.step); } diff --git a/src/lib.zig b/src/lib.zig new file mode 100644 index 0000000..506f367 --- /dev/null +++ b/src/lib.zig @@ -0,0 +1,74 @@ +const std = @import("std"); +const Mutex = std.Thread.Mutex; +const time = std.time; + +const default_frame_rate = 150 * time.ns_per_ms; +const default_charset = [_][]u8{ "|", "/", "-", "\\" }; + +const Spinner = struct { + // When locked, the spinner will stop spinning. + stop_lock: Mutex, + // Used to check if stopped. + stopped_lock: Mutex, + // The number of nanoseconds to wait between frames. + framerate: u64, + charset: [][]const u8, + message: []const u8, + + export fn new(framerate: ?u64, charset: ?[][]const u8, message: ?[]const u8) Spinner { + return Spinner{ + .thread_lock = Mutex{}, + .ns_per_frame = framerate orelse default_frame_rate, + .charset = charset orelse default_charset, + .message = message orelse "", + }; + } + + export fn start(sp: *Spinner) void { + sp.stop_lock.unlock(); + sp.stopped_lock.unlock(); + async sp.writer(); + } + + export fn stop(sp: *Spinner) void { + sp.stop_lock.lock(); + sp.stopped_lock.lock(); + } + + fn animateSpinnerOnce(sp: *Spinner) void { + var stdOut = std.io.getStdOut(); + + for (sp.charset) |frame| { + stdOut.write(frame); + stdOut.write(" "); + stdOut.write(sp.message); + + // Jumps to the start of the line. + stdOut.write("\r"); + stdOut.flush(); + + time.sleep(sp.framerate); + } + } + + fn writer(sp: *Spinner) void { + while (true) { + // Stops if it has been locked by .stop(). + if (sp.stop_lock.tryLock()) { + break; + } + sp.stop_lock.unlock(); + + sp.animateSpinnerOnce(); + } + sp.stopped_lock.lock(); + } +}; + + +test "asd" { + var sp = Spinner.new(null, null, "Loading..."); + sp.start(); + time.sleep(5 * time.ns_per_s); + sp.stop(); +} diff --git a/src/main.zig b/src/main.zig deleted file mode 100644 index c8a3f67..0000000 --- a/src/main.zig +++ /dev/null @@ -1,24 +0,0 @@ -const std = @import("std"); - -pub fn main() !void { - // Prints to stderr (it's a shortcut based on `std.io.getStdErr()`) - std.debug.print("All your {s} are belong to us.\n", .{"codebase"}); - - // stdout is for the actual output of your application, for example if you - // are implementing gzip, then only the compressed bytes should be sent to - // stdout, not any debugging messages. - const stdout_file = std.io.getStdOut().writer(); - var bw = std.io.bufferedWriter(stdout_file); - const stdout = bw.writer(); - - try stdout.print("Run `zig build test` to run the tests.\n", .{}); - - try bw.flush(); // don't forget to flush! -} - -test "simple test" { - var list = std.ArrayList(i32).init(std.testing.allocator); - defer list.deinit(); // try commenting this out and see if zig detects the memory leak! - try list.append(42); - try std.testing.expectEqual(@as(i32, 42), list.pop()); -}