diff --git a/src/codegen.zig b/src/codegen.zig index 0074553..a789a71 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -6,14 +6,15 @@ const mem = std.mem; const Dependency = @import("Dependency.zig"); const Entry = StringHashMap(Dependency).Entry; +const MetaEntry = StringHashMap([]const u8).Entry; -pub fn write(alloc: Allocator, out: anytype, deps: StringHashMap(Dependency)) !void { +pub fn write(alloc: Allocator, out: anytype, meta: StringHashMap([]const u8), deps: StringHashMap(Dependency)) !void { try out.writeAll( \\# generated by zon2nix (https://github.com/nix-community/zon2nix) \\ \\{ linkFarm, fetchzip }: \\ - \\linkFarm "zig-packages" [ + \\(linkFarm "zig-packages" [ \\ ); @@ -40,9 +41,38 @@ pub fn write(alloc: Allocator, out: anytype, deps: StringHashMap(Dependency)) !v , .{ key, dep.url, dep.nix_hash }); } - try out.writeAll("]\n"); + try out.writeAll("])"); + + const meta_len = meta.count(); + if (meta_len > 0) { + try out.writeAll(".overrideAttrs {\n passthru.zonMeta = {\n"); + + var meta_entries = try alloc.alloc(MetaEntry, meta_len); + var meta_iter = meta.iterator(); + for (0..meta_len) |i| { + meta_entries[i] = meta_iter.next().?; + } + mem.sortUnstable(MetaEntry, meta_entries, {}, metaLessThan); + + for (meta_entries) |entry| { + const key = entry.key_ptr.*; + const value = entry.value_ptr.*; + + try out.print( + \\ "{s}" = "{s}"; + \\ + , .{ key, value }); + } + + try out.writeAll(" };\n}"); + } + try out.writeAll("\n"); } fn lessThan(_: void, lhs: Entry, rhs: Entry) bool { return mem.order(u8, lhs.key_ptr.*, rhs.key_ptr.*) == .lt; } + +fn metaLessThan(_: void, lhs: MetaEntry, rhs: MetaEntry) bool { + return mem.order(u8, lhs.key_ptr.*, rhs.key_ptr.*) == .lt; +} diff --git a/src/fetch.zig b/src/fetch.zig index 459b461..f0e83ed 100644 --- a/src/fetch.zig +++ b/src/fetch.zig @@ -79,7 +79,8 @@ pub fn fetch(alloc: Allocator, deps: *StringHashMap(Dependency)) !void { }; defer file.close(); - try parse(alloc, deps, file); + var meta = StringHashMap([]const u8).init(alloc); + try parse(alloc, &meta, deps, file); if (deps.count() > len_before) { done = false; } diff --git a/src/main.zig b/src/main.zig index 07cd9d0..5df6bb6 100644 --- a/src/main.zig +++ b/src/main.zig @@ -28,12 +28,13 @@ pub fn main() !void { defer arena.deinit(); const alloc = arena.allocator(); + var meta = StringHashMap([]const u8).init(alloc); var deps = StringHashMap(Dependency).init(alloc); - try parse(alloc, &deps, file); + try parse(alloc, &meta, &deps, file); try fetch(alloc, &deps); var out = io.bufferedWriter(io.getStdOut().writer()); - try write(alloc, out.writer(), deps); + try write(alloc, out.writer(), meta, deps); try out.flush(); } diff --git a/src/parse.zig b/src/parse.zig index fda3b6e..ae9f51a 100644 --- a/src/parse.zig +++ b/src/parse.zig @@ -9,7 +9,7 @@ const string_literal = std.zig.string_literal; const Dependency = @import("Dependency.zig"); -pub fn parse(alloc: Allocator, deps: *StringHashMap(Dependency), file: File) !void { +pub fn parse(alloc: Allocator, meta: *StringHashMap([]const u8), deps: *StringHashMap(Dependency), file: File) !void { const content = try alloc.allocSentinel(u8, try file.getEndPos(), 0); _ = try file.reader().readAll(content); @@ -21,44 +21,16 @@ pub fn parse(alloc: Allocator, deps: *StringHashMap(Dependency), file: File) !vo }; for (root_init.ast.fields) |field_idx| { - if (!mem.eql(u8, try parseFieldName(alloc, ast, field_idx), "dependencies")) { - continue; - } - - const deps_init = ast.fullStructInit(&buf, field_idx) orelse { - return error.ParseError; - }; - - for (deps_init.ast.fields) |dep_idx| { - var dep: Dependency = .{ - .url = undefined, - .nix_hash = undefined, - .done = false, - }; - var hash: []const u8 = undefined; - var has_url = false; - var has_hash = false; - - const dep_init = ast.fullStructInit(&buf, dep_idx) orelse { - return error.parseError; - }; - - for (dep_init.ast.fields) |dep_field_idx| { - const name = try parseFieldName(alloc, ast, dep_field_idx); - - if (mem.eql(u8, name, "url")) { - dep.url = try parseString(alloc, ast, dep_field_idx); - has_url = true; - } else if (mem.eql(u8, name, "hash")) { - hash = try parseString(alloc, ast, dep_field_idx); - has_hash = true; - } - } - - if (has_url and has_hash) { - _ = try deps.getOrPutValue(hash, dep); - } else { - return error.parseError; + const field_name = try parseFieldName(alloc, ast, field_idx); + if (mem.eql(u8, field_name, "dependencies")) { + try parseDependency(alloc, ast, field_idx, deps); + } else if (mem.eql(u8, field_name, "paths")) { + // TODO: implement parsing of 'paths' array + } else { + if (parseString(alloc, ast, field_idx)) |value| { + _ = try meta.getOrPutValue(field_name, value); + } else |_| { + // Ignore field if metadata value isn't a string. } } } @@ -70,7 +42,53 @@ fn parseFieldName(alloc: Allocator, ast: Ast, idx: Index) ![]const u8 { } fn parseString(alloc: Allocator, ast: Ast, idx: Index) ![]const u8 { - return string_literal.parseAlloc(alloc, ast.tokenSlice(ast.nodes.items(.main_token)[idx])); + const token = ast.tokenSlice(ast.nodes.items(.main_token)[idx]); + return switch (token[0]) { + // Check if the start of the token looks like a string to avoid + // unreachable error when trying to parse a non-string. + '"', '\\' => string_literal.parseAlloc(alloc, token), + else => error.ParseError, + }; +} + +fn parseDependency(alloc: Allocator, ast: Ast, field_idx: Index, deps: *StringHashMap(Dependency)) !void { + var buf: [2]Index = undefined; + const deps_init = ast.fullStructInit(&buf, field_idx) orelse { + return error.ParseError; + }; + + for (deps_init.ast.fields) |dep_idx| { + var dep: Dependency = .{ + .url = undefined, + .nix_hash = undefined, + .done = false, + }; + var hash: []const u8 = undefined; + var has_url = false; + var has_hash = false; + + const dep_init = ast.fullStructInit(&buf, dep_idx) orelse { + return error.parseError; + }; + + for (dep_init.ast.fields) |dep_field_idx| { + const name = try parseFieldName(alloc, ast, dep_field_idx); + + if (mem.eql(u8, name, "url")) { + dep.url = try parseString(alloc, ast, dep_field_idx); + has_url = true; + } else if (mem.eql(u8, name, "hash")) { + hash = try parseString(alloc, ast, dep_field_idx); + has_hash = true; + } + } + + if (has_url and has_hash) { + _ = try deps.getOrPutValue(hash, dep); + } else { + return error.parseError; + } + } } test parse { @@ -82,9 +100,10 @@ test parse { defer arena.deinit(); const alloc = arena.allocator(); + var meta = StringHashMap([]const u8).init(alloc); var deps = StringHashMap(Dependency).init(alloc); const basic = try fs.cwd().openFile("fixtures/basic.zon", .{}); - try parse(alloc, &deps, basic); + try parse(alloc, &meta, &deps, basic); basic.close(); try testing.expectEqual(deps.count(), 3);