Makko, the people-oriented static site generator made for blogging.
forge.starlightnet.work/Team/Makko
ssg
static-site-generator
makko
starlight-network
1const std = @import("std");
2
3const builtin = @import("builtin");
4
5const helper = @import("helper.zig");
6const Data = @import("Data.zig");
7const Parsed = @import("Parsed.zig");
8const Log = @import("Log.zig");
9
10pub const Status = enum(c_int) {
11 unchanged = 0,
12 created,
13 deleted,
14 modified,
15};
16
17pub const Change = struct {
18 status: Status, // never .unchanged
19 id: Data.Id,
20
21 source: ?[]const u8 = null,
22 output: ?[]const u8 = null,
23
24 title: ?[]const u8 = null,
25 description: ?[]const u8 = null,
26 author: ?[]const u8 = null,
27
28 timestamp: i64,
29
30 pub fn copy(self: Change, allocator: std.mem.Allocator) !Change {
31 return Change{
32 .status = self.status,
33 .id = self.id,
34
35 .source = if (self.source) |src|
36 try allocator.dupe(u8, src)
37 else
38 null,
39
40 .output = if (self.output) |output|
41 try allocator.dupe(u8, output)
42 else
43 null,
44
45 .title = if (self.title) |title|
46 try allocator.dupe(u8, title)
47 else
48 null,
49
50 .description = if (self.description) |description|
51 try allocator.dupe(u8, description)
52 else
53 null,
54
55 .author = if (self.author) |author|
56 try allocator.dupe(u8, author)
57 else
58 null,
59
60 .timestamp = self.timestamp,
61 };
62 }
63
64 pub fn deinit(self: Change, allocator: std.mem.Allocator) void {
65 if (self.source) |src| allocator.free(src);
66 if (self.output) |output| allocator.free(output);
67 if (self.title) |title| allocator.free(title);
68 if (self.description) |description| allocator.free(description);
69 if (self.author) |author| allocator.free(author);
70 }
71};
72
73on_change: ?[]const u8 = null,
74on_create: ?[]const u8 = null,
75on_delete: ?[]const u8 = null,
76on_modify: ?[]const u8 = null,
77
78const Callbacks = @This();
79
80fn callSystemProgram(
81 program: []const u8,
82 stdin_input: []const u8,
83 cwd: std.fs.Dir,
84 allocator: std.mem.Allocator,
85) !void {
86 var args = std.ArrayList([]const u8).init(allocator);
87 defer args.deinit();
88
89 if (builtin.os.tag == .windows) {
90 try args.append("cmd.exe");
91 try args.append("/C");
92 } else {
93 try args.append("/bin/sh");
94 try args.append("-c");
95 }
96 try args.append(program);
97
98 var child = std.process.Child.init(args.items, allocator);
99
100 child.cwd_dir = cwd;
101
102 child.stdin_behavior = .Pipe;
103 child.stdout_behavior = .Ignore;
104 child.stderr_behavior = .Inherit;
105
106 var env_map = std.process.EnvMap.init(allocator);
107 defer env_map.deinit();
108
109 try env_map.put("FROM_MAKKO", "YES");
110
111 child.env_map = &env_map;
112
113 try child.spawn();
114
115 if (child.stdin) |stdin| {
116 _ = try stdin.writeAll(stdin_input);
117 stdin.close();
118 child.stdin = null;
119 }
120
121 _ = try child.wait();
122}
123
124fn lessThan(context: void, a: Change, b: Change) bool {
125 _ = context;
126 return @intFromEnum(a.status) < @intFromEnum(b.status);
127}
128
129pub fn run(
130 callbacks: Callbacks,
131 changes: []Change,
132 cwd: std.fs.Dir,
133 log: Log,
134 allocator: std.mem.Allocator,
135) !void {
136 if (changes.len == 0)
137 return;
138
139 //std.sort.block(Change, changes, {}, lessThan);
140
141 var created_changes = std.ArrayList(Change).init(allocator);
142 var deleted_changes = std.ArrayList(Change).init(allocator);
143 var modified_changes = std.ArrayList(Change).init(allocator);
144
145 defer {
146 created_changes.deinit();
147 deleted_changes.deinit();
148 modified_changes.deinit();
149 }
150
151 for (changes) |change| {
152 switch (change.status) {
153 .created => try created_changes.append(change),
154 .deleted => try deleted_changes.append(change),
155 .modified => try modified_changes.append(change),
156
157 .unchanged => {},
158 }
159 }
160
161 if ((created_changes.items.len > 0 and callbacks.on_create != null) or
162 (deleted_changes.items.len > 0 and callbacks.on_delete != null) or
163 (modified_changes.items.len > 0 and callbacks.on_modify != null) or
164 callbacks.on_change != null)
165 log.header("Callbacks");
166
167 if (callbacks.on_change) |on_change| {
168 const json = try std.json.stringifyAlloc(
169 allocator,
170 changes,
171 .{},
172 );
173 defer allocator.free(json);
174
175 log.raw("- on_change: {s}\n", .{on_change});
176 try callSystemProgram(on_change, json, cwd, allocator);
177 }
178
179 if (callbacks.on_create) |on_create| {
180 if (created_changes.items.len > 0) {
181 const json = try std.json.stringifyAlloc(
182 allocator,
183 created_changes.items,
184 .{},
185 );
186 defer allocator.free(json);
187
188 log.raw("- on_create: {s}\n", .{on_create});
189 try callSystemProgram(on_create, json, cwd, allocator);
190 }
191 }
192
193 if (callbacks.on_delete) |on_delete| {
194 if (deleted_changes.items.len > 0) {
195 const json = try std.json.stringifyAlloc(
196 allocator,
197 deleted_changes.items,
198 .{},
199 );
200 defer allocator.free(json);
201
202 log.raw("- on_delete: {s}\n", .{on_delete});
203 try callSystemProgram(on_delete, json, cwd, allocator);
204 }
205 }
206
207 if (callbacks.on_modify) |on_modify| {
208 if (modified_changes.items.len > 0) {
209 const json = try std.json.stringifyAlloc(
210 allocator,
211 modified_changes.items,
212 .{},
213 );
214 defer allocator.free(json);
215
216 log.raw("- on_modify: {s}\n", .{on_modify});
217 try callSystemProgram(on_modify, json, cwd, allocator);
218 }
219 }
220}