Zig Module: Cronz Introduction

Hey, Zig community,

This is Ng, who joined as a member recently. I am a newbie to Zig, and I’ve come up with a little module to support this Zig community. Nothing big, but I wanted to give it back.

Cronz is set to enhance the scheduling of the repeatable tasks using the crontab notation with zero allocations. This module prefers crontab notation to provide a clear and concise understanding of its repetition.

Please give it a try and share the observations to sharpen my Zig knowledge.

Check out examples of the above repo to know more on its usage.

const cronz = try Cronz.create(allocator);

// executes tasks on every 0th, 10th, 20th, 30th and 40th second of each minute
try cronz.AddCronJob("0,10,20,30,40 * * * * *", "task-1", task1);

//execute tasks on every 2 second
try cronz.AddCronJob("*/2 * * * * *", "task-2", task2);

//execute tasks between 0-10 seconds of 15th and 30th minute
try cronz.AddCronJob("[0-10] 15,30 * * * *", "task-3", task3);

Thanks
Ng

9 Likes

Welcome to the Zig community, Ng!

I have some suggestions (which you may ignore as well, and I am myself not infinitely experienced):

  • In your API you write cronz.AddCronJob(). That function does not return a type (in which case it could be written in TitleCase). In Zig it is common to give functions a camelCased name.
  • Your examples contain phrases like const cronz = try Cronz.create(allocator);. In the standard library code another pattern is used more frequently: putting the type right after the variable name and then using the declaration literal system (see Proposal: Decl Literals · Issue #9938 · ziglang/zig · GitHub) to call create. That would be const cronz: Cronz = .create(allocator);.
  • In your examples, you say var msg: []u8 = undefined; and in the (immediately) following line msg = try allocator.alloc(u8, 100);. It would be a good idea to merge those two lines together (so that you have var msg: []u8 = allocator.alloc(u8, 100);. But I guess, you were searching for std.fmt.allocPrint (where you don’t even have to specify a length for the thing you want to have, you give it an allocator and it gives you a slice to the resulting bytes). So that would become (directly): const msg = try std.fmt.allocPrint(allocator, "Task 1 performed", .{});.
  • You are using a global allocator in your examples (global var allocator: std.mem.Allocator = undefined; + allocator = arena_instance.allocator()). The best practice is to pass around the arguments (which is difficult for tasks however). But even if you don’t do that, please go a small safety route and make the global allocator optional with the start value being null (var allocator: ?std.mem.Allocator = null;). Then you can unwrap the allocator in your task functions (if (allocator) |alloc| {doStuff();} else {@panic("no allocator");}). Setting the allocator (allocator = arena_instance.allocator();) can stay the same (that throws no compile error with the type change).

A small suggestion for your library: I don’t know how many people really do know the cron syntax. Maybe it would be better to make all cron stuff functions take in a struct with (maybe optional) values for day, hour etc.?

Happy zigging (I don’t actually know if that’s a term we use here :D)!

PS. Please take all my pieces of code with a grain of salt and indent them properly. I wrote them in a rush and they may contain compile errors.

5 Likes

dropped the try:

const cronz: Cronz = try .create(allocator);
var msg: []u8 = try allocator.alloc(u8, 100);
2 Likes

@samuel-fiedler @Sze Appreciate your time in getting this reviewed and sharing your inputs. Since I am coming from a Go background, I went with PascalCase for Pub fn notation. I will review them and make corrections as necessary.

#2 The declaration literals didn’t come to my mind as the zls fills it when I am writing the code. I may need to stop using short notations and explicitly type everything going forward.