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}