| ... | ... | @@ -1802,45 +1802,25 @@ const UnpackResult = struct { |
| 1802 | 1802 | } |
| 1803 | 1803 | |
| 1804 | 1804 | fn validate(self: *UnpackResult, f: *Fetch, filter: Filter) !void { |
| 1805 | | self.filterErrors(filter); |
| 1806 | | if (self.hasErrors()) { |
| 1807 | | const eb = &f.error_bundle; |
| 1808 | | try self.bundleErrors(eb, try f.srcLoc(f.location_tok)); |
| 1809 | | return error.FetchFailed; |
| 1810 | | } |
| 1811 | | } |
| 1805 | if (self.errors_count == 0) return; |
| 1812 | 1806 | |
| 1813 | | // Filter errors by manifest inclusion rules. |
| 1814 | | fn filterErrors(self: *UnpackResult, filter: Filter) void { |
| 1815 | | var i = self.errors_count; |
| 1816 | | while (i > 0) { |
| 1817 | | i -= 1; |
| 1818 | | if (self.errors[i].excluded(filter)) { |
| 1819 | | self.errors_count -= 1; |
| 1820 | | const tmp = self.errors[i]; |
| 1821 | | self.errors[i] = self.errors[self.errors_count]; |
| 1822 | | self.errors[self.errors_count] = tmp; |
| 1823 | | } |
| 1807 | var unfiltered_errors: u32 = 0; |
| 1808 | for (self.errors) |item| { |
| 1809 | if (item.excluded(filter)) continue; |
| 1810 | unfiltered_errors += 1; |
| 1824 | 1811 | } |
| 1825 | | } |
| 1826 | | |
| 1827 | | // Emmit errors to an `ErrorBundle`. |
| 1828 | | fn bundleErrors( |
| 1829 | | self: *UnpackResult, |
| 1830 | | eb: *ErrorBundle.Wip, |
| 1831 | | src_loc: ErrorBundle.SourceLocationIndex, |
| 1832 | | ) !void { |
| 1833 | | if (self.errors_count == 0 and self.root_error_message.len == 0) |
| 1834 | | return; |
| 1812 | if (unfiltered_errors == 0) return; |
| 1835 | 1813 | |
| 1836 | | const notes_len: u32 = @intCast(self.errors_count); |
| 1814 | // Emmit errors to an `ErrorBundle`. |
| 1815 | const eb = &f.error_bundle; |
| 1837 | 1816 | try eb.addRootErrorMessage(.{ |
| 1838 | 1817 | .msg = try eb.addString(self.root_error_message), |
| 1839 | | .src_loc = src_loc, |
| 1840 | | .notes_len = notes_len, |
| 1818 | .src_loc = try f.srcLoc(f.location_tok), |
| 1819 | .notes_len = unfiltered_errors, |
| 1841 | 1820 | }); |
| 1842 | | const notes_start = try eb.reserveNotes(notes_len); |
| 1843 | | for (self.errors, notes_start..) |item, note_i| { |
| 1821 | var note_i: u32 = try eb.reserveNotes(unfiltered_errors); |
| 1822 | for (self.errors) |item| { |
| 1823 | if (item.excluded(filter)) continue; |
| 1844 | 1824 | switch (item) { |
| 1845 | 1825 | .unable_to_create_sym_link => |info| { |
| 1846 | 1826 | eb.extra.items[note_i] = @intFromEnum(try eb.addErrorMessage(.{ |
| ... | ... | @@ -1864,37 +1844,54 @@ const UnpackResult = struct { |
| 1864 | 1844 | })); |
| 1865 | 1845 | }, |
| 1866 | 1846 | } |
| 1847 | note_i += 1; |
| 1867 | 1848 | } |
| 1849 | |
| 1850 | return error.FetchFailed; |
| 1868 | 1851 | } |
| 1869 | 1852 | |
| 1870 | | test filterErrors { |
| 1871 | | var arena_instance = std.heap.ArenaAllocator.init(std.testing.allocator); |
| 1853 | test validate { |
| 1854 | const gpa = std.testing.allocator; |
| 1855 | var arena_instance = std.heap.ArenaAllocator.init(gpa); |
| 1872 | 1856 | defer arena_instance.deinit(); |
| 1873 | 1857 | const arena = arena_instance.allocator(); |
| 1874 | 1858 | |
| 1875 | | // init |
| 1859 | // fill UnpackResult with errors |
| 1876 | 1860 | var res: UnpackResult = .{}; |
| 1877 | | try res.allocErrors(arena, 4, "error"); |
| 1861 | try res.allocErrors(arena, 4, "unable to unpack"); |
| 1878 | 1862 | try std.testing.expectEqual(0, res.errors_count); |
| 1879 | | |
| 1880 | | // create errors |
| 1881 | 1863 | res.unableToCreateFile("dir1/file1", error.File1); |
| 1882 | | res.unableToCreateSymLink("dir2/file2", "", error.File2); |
| 1864 | res.unableToCreateSymLink("dir2/file2", "filename", error.SymlinkError); |
| 1883 | 1865 | res.unableToCreateFile("dir1/file3", error.File3); |
| 1884 | 1866 | res.unsupportedFileType("dir2/file4", 'x'); |
| 1885 | 1867 | try std.testing.expectEqual(4, res.errors_count); |
| 1886 | 1868 | |
| 1887 | | // filter errors |
| 1869 | // create filter, includes dir2, excludes dir1 |
| 1888 | 1870 | var filter: Filter = .{}; |
| 1889 | 1871 | try filter.include_paths.put(arena, "dir2", {}); |
| 1890 | | res.filterErrors(filter); |
| 1891 | | |
| 1892 | | try std.testing.expectEqual(2, res.errors_count); |
| 1893 | | try std.testing.expect(res.errors[0] == Error.unsupported_file_type); |
| 1894 | | try std.testing.expect(res.errors[1] == Error.unable_to_create_sym_link); |
| 1895 | | // filtered: moved to the list end |
| 1896 | | try std.testing.expect(res.errors[2] == Error.unable_to_create_file); |
| 1897 | | try std.testing.expect(res.errors[3] == Error.unable_to_create_file); |
| 1872 | |
| 1873 | // init Fetch |
| 1874 | var fetch: Fetch = undefined; |
| 1875 | fetch.parent_manifest_ast = null; |
| 1876 | fetch.location_tok = 0; |
| 1877 | try fetch.error_bundle.init(gpa); |
| 1878 | defer fetch.error_bundle.deinit(); |
| 1879 | |
| 1880 | // validate errors with filter |
| 1881 | try std.testing.expectError(error.FetchFailed, res.validate(&fetch, filter)); |
| 1882 | |
| 1883 | // output errors to string |
| 1884 | var errors = try fetch.error_bundle.toOwnedBundle(""); |
| 1885 | defer errors.deinit(gpa); |
| 1886 | var out = std.ArrayList(u8).init(gpa); |
| 1887 | defer out.deinit(); |
| 1888 | try errors.renderToWriter(.{ .ttyconf = .no_color }, out.writer()); |
| 1889 | try std.testing.expectEqualStrings( |
| 1890 | \\error: unable to unpack |
| 1891 | \\ note: unable to create symlink from 'dir2/file2' to 'filename': SymlinkError |
| 1892 | \\ note: file 'dir2/file4' has unsupported type 'x' |
| 1893 | \\ |
| 1894 | , out.items); |
| 1898 | 1895 | } |
| 1899 | 1896 | }; |
| 1900 | 1897 | |