master
1const std = @import("std");
2const builtin = @import("builtin");
3
4pub const UncheckedSliceWriter = struct {
5 const Self = @This();
6
7 pos: usize = 0,
8 slice: []u8,
9
10 pub fn write(self: *Self, char: u8) void {
11 self.slice[self.pos] = char;
12 self.pos += 1;
13 }
14
15 pub fn writeSlice(self: *Self, slice: []const u8) void {
16 for (slice) |c| {
17 self.write(c);
18 }
19 }
20
21 pub fn getWritten(self: Self) []u8 {
22 return self.slice[0..self.pos];
23 }
24};
25
26/// Cross-platform 'std.fs.Dir.openFile' wrapper that will always return IsDir if
27/// a directory is attempted to be opened.
28/// TODO: Remove once https://github.com/ziglang/zig/issues/5732 is addressed.
29pub fn openFileNotDir(
30 cwd: std.fs.Dir,
31 path: []const u8,
32 flags: std.fs.File.OpenFlags,
33) (std.fs.File.OpenError || std.fs.File.StatError)!std.fs.File {
34 const file = try cwd.openFile(path, flags);
35 errdefer file.close();
36 // https://github.com/ziglang/zig/issues/5732
37 if (builtin.os.tag != .windows) {
38 const stat = try file.stat();
39
40 if (stat.kind == .directory)
41 return error.IsDir;
42 }
43 return file;
44}
45
46/// Emulates the Windows implementation of `iswdigit`, but only returns true
47/// for the non-ASCII digits that `iswdigit` on Windows would return true for.
48pub fn isNonAsciiDigit(c: u21) bool {
49 return switch (c) {
50 '²',
51 '³',
52 '¹',
53 '\u{660}'...'\u{669}',
54 '\u{6F0}'...'\u{6F9}',
55 '\u{7C0}'...'\u{7C9}',
56 '\u{966}'...'\u{96F}',
57 '\u{9E6}'...'\u{9EF}',
58 '\u{A66}'...'\u{A6F}',
59 '\u{AE6}'...'\u{AEF}',
60 '\u{B66}'...'\u{B6F}',
61 '\u{BE6}'...'\u{BEF}',
62 '\u{C66}'...'\u{C6F}',
63 '\u{CE6}'...'\u{CEF}',
64 '\u{D66}'...'\u{D6F}',
65 '\u{E50}'...'\u{E59}',
66 '\u{ED0}'...'\u{ED9}',
67 '\u{F20}'...'\u{F29}',
68 '\u{1040}'...'\u{1049}',
69 '\u{1090}'...'\u{1099}',
70 '\u{17E0}'...'\u{17E9}',
71 '\u{1810}'...'\u{1819}',
72 '\u{1946}'...'\u{194F}',
73 '\u{19D0}'...'\u{19D9}',
74 '\u{1B50}'...'\u{1B59}',
75 '\u{1BB0}'...'\u{1BB9}',
76 '\u{1C40}'...'\u{1C49}',
77 '\u{1C50}'...'\u{1C59}',
78 '\u{A620}'...'\u{A629}',
79 '\u{A8D0}'...'\u{A8D9}',
80 '\u{A900}'...'\u{A909}',
81 '\u{AA50}'...'\u{AA59}',
82 '\u{FF10}'...'\u{FF19}',
83 => true,
84 else => false,
85 };
86}
87
88pub const ErrorMessageType = enum { err, warning, note };
89
90/// Used for generic colored errors/warnings/notes, more context-specific error messages
91/// are handled elsewhere.
92pub fn renderErrorMessage(writer: *std.Io.Writer, config: std.Io.tty.Config, msg_type: ErrorMessageType, comptime format: []const u8, args: anytype) !void {
93 switch (msg_type) {
94 .err => {
95 try config.setColor(writer, .bold);
96 try config.setColor(writer, .red);
97 try writer.writeAll("error: ");
98 },
99 .warning => {
100 try config.setColor(writer, .bold);
101 try config.setColor(writer, .yellow);
102 try writer.writeAll("warning: ");
103 },
104 .note => {
105 try config.setColor(writer, .reset);
106 try config.setColor(writer, .cyan);
107 try writer.writeAll("note: ");
108 },
109 }
110 try config.setColor(writer, .reset);
111 if (msg_type == .err) {
112 try config.setColor(writer, .bold);
113 }
114 try writer.print(format, args);
115 try writer.writeByte('\n');
116 try config.setColor(writer, .reset);
117}
118
119pub fn isLineEndingPair(first: u8, second: u8) bool {
120 if (first != '\r' and first != '\n') return false;
121 if (second != '\r' and second != '\n') return false;
122
123 // can't be \n\n or \r\r
124 if (first == second) return false;
125
126 return true;
127}