Skip to content

fetch: combine unpack error validation in a single function #19618

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 11, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 46 additions & 49 deletions src/Package/Fetch.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1802,45 +1802,25 @@ const UnpackResult = struct {
}

fn validate(self: *UnpackResult, f: *Fetch, filter: Filter) !void {
self.filterErrors(filter);
if (self.hasErrors()) {
const eb = &f.error_bundle;
try self.bundleErrors(eb, try f.srcLoc(f.location_tok));
return error.FetchFailed;
}
}
if (self.errors_count == 0) return;

// Filter errors by manifest inclusion rules.
fn filterErrors(self: *UnpackResult, filter: Filter) void {
var i = self.errors_count;
while (i > 0) {
i -= 1;
if (self.errors[i].excluded(filter)) {
self.errors_count -= 1;
const tmp = self.errors[i];
self.errors[i] = self.errors[self.errors_count];
self.errors[self.errors_count] = tmp;
}
var unfiltered_errors: u32 = 0;
for (self.errors) |item| {
if (item.excluded(filter)) continue;
unfiltered_errors += 1;
}
}

// Emmit errors to an `ErrorBundle`.
fn bundleErrors(
self: *UnpackResult,
eb: *ErrorBundle.Wip,
src_loc: ErrorBundle.SourceLocationIndex,
) !void {
if (self.errors_count == 0 and self.root_error_message.len == 0)
return;
if (unfiltered_errors == 0) return;

const notes_len: u32 = @intCast(self.errors_count);
// Emmit errors to an `ErrorBundle`.
const eb = &f.error_bundle;
try eb.addRootErrorMessage(.{
.msg = try eb.addString(self.root_error_message),
.src_loc = src_loc,
.notes_len = notes_len,
.src_loc = try f.srcLoc(f.location_tok),
.notes_len = unfiltered_errors,
});
const notes_start = try eb.reserveNotes(notes_len);
for (self.errors, notes_start..) |item, note_i| {
var note_i: u32 = try eb.reserveNotes(unfiltered_errors);
for (self.errors) |item| {
if (item.excluded(filter)) continue;
switch (item) {
.unable_to_create_sym_link => |info| {
eb.extra.items[note_i] = @intFromEnum(try eb.addErrorMessage(.{
Expand All @@ -1864,37 +1844,54 @@ const UnpackResult = struct {
}));
},
}
note_i += 1;
}

return error.FetchFailed;
}

test filterErrors {
var arena_instance = std.heap.ArenaAllocator.init(std.testing.allocator);
test validate {
const gpa = std.testing.allocator;
var arena_instance = std.heap.ArenaAllocator.init(gpa);
defer arena_instance.deinit();
const arena = arena_instance.allocator();

// init
// fill UnpackResult with errors
var res: UnpackResult = .{};
try res.allocErrors(arena, 4, "error");
try res.allocErrors(arena, 4, "unable to unpack");
try std.testing.expectEqual(0, res.errors_count);

// create errors
res.unableToCreateFile("dir1/file1", error.File1);
res.unableToCreateSymLink("dir2/file2", "", error.File2);
res.unableToCreateSymLink("dir2/file2", "filename", error.SymlinkError);
res.unableToCreateFile("dir1/file3", error.File3);
res.unsupportedFileType("dir2/file4", 'x');
try std.testing.expectEqual(4, res.errors_count);

// filter errors
// create filter, includes dir2, excludes dir1
var filter: Filter = .{};
try filter.include_paths.put(arena, "dir2", {});
res.filterErrors(filter);

try std.testing.expectEqual(2, res.errors_count);
try std.testing.expect(res.errors[0] == Error.unsupported_file_type);
try std.testing.expect(res.errors[1] == Error.unable_to_create_sym_link);
// filtered: moved to the list end
try std.testing.expect(res.errors[2] == Error.unable_to_create_file);
try std.testing.expect(res.errors[3] == Error.unable_to_create_file);

// init Fetch
var fetch: Fetch = undefined;
fetch.parent_manifest_ast = null;
fetch.location_tok = 0;
try fetch.error_bundle.init(gpa);
defer fetch.error_bundle.deinit();

// validate errors with filter
try std.testing.expectError(error.FetchFailed, res.validate(&fetch, filter));

// output errors to string
var errors = try fetch.error_bundle.toOwnedBundle("");
defer errors.deinit(gpa);
var out = std.ArrayList(u8).init(gpa);
defer out.deinit();
try errors.renderToWriter(.{ .ttyconf = .no_color }, out.writer());
try std.testing.expectEqualStrings(
\\error: unable to unpack
\\ note: unable to create symlink from 'dir2/file2' to 'filename': SymlinkError
\\ note: file 'dir2/file4' has unsupported type 'x'
\\
, out.items);
}
};

Expand Down