const builtin = @import("builtin"); const std = @import("std"); const xdg = @import("xdg"); const Allocator = std.mem.Allocator; const ArenaAllocator = std.heap.ArenaAllocator; const ArrayList = std.ArrayList; const Config = @This(); const File = std.fs.File; const Target = std.Target; allocator: Allocator, /// Earlier in the list means more preferred. supported_targets: ArrayList(Target), pub fn init(allocator: Allocator) !Config { var self = Config{ .allocator = allocator, .supported_targets = ArrayList(Target).init(allocator), }; errdefer self.deinit(); // Native target is always supported and at highest priority try self.supported_targets.append(builtin.target); const config_dirs = try xdg.getAllConfigDirs(allocator, "zup"); defer { for (config_dirs) |s| allocator.free(s); allocator.free(config_dirs); } for (config_dirs) |config_dir| { const file_name = try std.fs.path.join(allocator, &.{ config_dir, "zup.json" }); defer allocator.free(file_name); var file = std.fs.openFileAbsolute(file_name, .{}) catch |err| { if (err == error.FileNotFound) { continue; } else { return err; } }; defer file.close(); try self.readConfig(file); } return self; } pub fn deinit(self: *Config) void { self.supported_targets.deinit(); self.* = undefined; } fn readConfig(self: *Config, file: File) !void { var arena = ArenaAllocator.init(self.allocator); defer arena.deinit(); const allocator = arena.allocator(); const ConfigJson = struct { supported_targets: [][]u8, }; var reader = std.json.reader(allocator, file.reader()); defer reader.deinit(); const parsed = try std.json.parseFromTokenSourceLeaky( ConfigJson, allocator, &reader, .{ .duplicate_field_behavior = .use_last, .ignore_unknown_fields = true, }, ); try self.supported_targets.ensureUnusedCapacity(parsed.supported_targets.len); for (parsed.supported_targets) |target| { const query = Target.Query.parse(.{ .arch_os_abi = target, }) catch |e| { std.log.warn( "Failed to parse '{s}' as a target string: {}", .{ target, e }, ); continue; }; const resolved = std.zig.system.resolveTargetQuery(query) catch |e| { std.log.warn("Failed to resolve '{s}' as a target: {}", .{ target, e}); continue; }; self.supported_targets.appendAssumeCapacity(resolved); } }