diff --git a/.github/workflows/Install-dependencies/action.yml b/.github/workflows/Install-dependencies/action.yml index 5822912..f57dda9 100644 --- a/.github/workflows/Install-dependencies/action.yml +++ b/.github/workflows/Install-dependencies/action.yml @@ -1,4 +1,10 @@ -name: Install Zig +name: Install Dependencies + +inputs: + which-dependencies: + description: "Which dependencies to install" + required: true + default: "codespell" runs: using: "composite" @@ -6,6 +12,10 @@ runs: - name: Checkout repository uses: actions/checkout@v4 - - name: Install Zig + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + if: ${{ inputs.which-dependencies == 'codespell'}} + with: + python-version: "3.9" + - run: pip install codespell shell: bash - run: sudo snap install --beta zig --classic diff --git a/.github/workflows/test-spelling.yml b/.github/workflows/test-spelling.yml index 36cd9bb..0af14b6 100644 --- a/.github/workflows/test-spelling.yml +++ b/.github/workflows/test-spelling.yml @@ -7,16 +7,18 @@ on: jobs: test: - runs-on: ubuntu-latest + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + runs-on: ${{ matrix.os }} steps: - name: Checkout code uses: actions/checkout@v4 - - name: install dependencies - run: | - sudo apt-get update - sudo apt-get install python3-pip - pip3 install codespell + - name: Install Dependencies + uses: ./.github/workflows/Install-dependencies + with: + which-dependencies: codespell - name: Run codespell working-directory: ./ diff --git a/.github/workflows/test-zig-files.yml b/.github/workflows/test-zig-files.yml index df4edfe..b4e8dd2 100644 --- a/.github/workflows/test-zig-files.yml +++ b/.github/workflows/test-zig-files.yml @@ -10,28 +10,45 @@ on: - ".github/workflows/test-zig-files.yml" jobs: - test: + Simple-tests: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - - name: Install Dependencies - uses: ./.github/workflows/Install-dependencies - - - name: Run tests on ZIG file change - working-directory: ziging - shell: bash - run: | + - uses: actions/checkout@v2 + - uses: goto-bus-stop/setup-zig@v2 + - run: | zig test beginner_basics.zig zig test testing_stuff.zig zig test intermediate_basics.zig zig test advanced_basics.zig echo 'Avi Fenesh' | zig test standard_library.zig - - - name: Run tests on Project0 - working-directory: Project0-word + working-directory: ziging shell: bash - run: | + + HW-test: + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + runs-on: ${{ matrix.os }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - uses: actions/checkout@v2 + - uses: goto-bus-stop/setup-zig@v2 + - run: | + echo 'testing P0-tests.zig' zig test P0-tests.zig + echo 'testing utils.zig' zig test utils.zig + working-directory: Project0-word + shell: bash + + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: goto-bus-stop/setup-zig@v2 + - run: zig fmt --check . diff --git a/Project0-word/main.zig b/Project0-word/main.zig index 511b803..53202b6 100644 --- a/Project0-word/main.zig +++ b/Project0-word/main.zig @@ -19,7 +19,7 @@ pub fn main() !void { const exit_message = printErrMessageAndExit(err); std.process.exit(exit_message); }; - count_words.countWords(path); + _ = count_words.countWords(path); } else { const stdin = std.io.getStdIn(); defer stdin.close(); @@ -56,7 +56,7 @@ pub fn main() !void { const exit_message = printErrMessageAndExit(err); std.process.exit(exit_message); }; - count_words.countWords(path); + _ = count_words.countWords(path); } } diff --git a/Project0-word/utils.zig b/Project0-word/utils.zig index cd1e85b..c3a5650 100644 --- a/Project0-word/utils.zig +++ b/Project0-word/utils.zig @@ -2,21 +2,23 @@ const std = @import("std"); const Reader = std.fs.File.Reader; const print = std.debug.print; const os_tag = @import("builtin").os.tag; -const eql = std.mem.eql; const File = std.fs.File; const CreateFlags = File.CreateFlags; const isAlphabetic = std.ascii.isAlphabetic; const isWhitespace = std.ascii.isWhitespace; const expect = @import("std").testing.expect; const test_allocator = std.testing.allocator; +const unicode = std.unicode; +const cwd = std.fs.cwd; +const mem = std.mem; +const eql = mem.eql; +const GPA = std.heap.GeneralPurposeAllocator; +const Allocator = std.mem.Allocator; -pub fn nextLine(reader: Reader, buffer: []u8) !?[]const u8 { - const line = reader.readUntilDelimiterOrEof(buffer, '\n') catch |err| { - print("Err: {}\n", .{err}); - return err; - }; +pub fn nextLine(reader: anytype, buffer: []u8) !?[]const u8 { + const line = (try reader.readUntilDelimiterOrEof(buffer, '\n')) orelse return null; if (os_tag == .windows) { - return std.mem.trimRight(u8, line, '\r'); + return mem.trimRight(u8, line, "\r"); } else { return line; } @@ -29,114 +31,143 @@ pub fn parseArgs(args: [][]const u8) error{ InvalidCommand, ErrorWritingToStdout return args[1]; } else if (eql(u8, args[0], "--help")) { stdout.writeAll("Optional args are:\nFor counting words in a file: -cw \nFor help: --help \n") catch |err| { - std.debug.print("Error writing to stdout: {any}\n", .{err}); + print("Error writing to stdout: {any}\n", .{err}); return error.ErrorWritingToStdout; }; return error.Retry; } else { - std.debug.print("Unknown command: {any}\n", .{args[0]}); + print("Unknown command: {any}\n", .{args[0]}); return error.InvalidCommand; } } -// TODO: Add different path encoding for different OS -// TODO: Break this function into smaller functions pub fn cleanFile(path: []u8, buffer: []u8) ![]u8 { - var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){}; + var general_purpose_allocator = GPA(.{}){}; const gpa = general_purpose_allocator.allocator(); - var slices = [_][]const u8{ path, ".cleaned" }; - var file_read: File = try std.fs.cwd().openFile(path, .{}); + var encoded_path_buffer = gpa.alloc(u8, path.len) catch unreachable; + const encoded_file_sub_path = try encodePathForOs(path, encoded_path_buffer); + defer gpa.free(encoded_path_buffer); + var file_read: File = try cwd().openFile(encoded_file_sub_path, .{}); defer file_read.close(); const file_reader = file_read.reader(); - const new_file_sub_path = std.mem.concat(gpa, u8, &slices) catch unreachable; - var new_file: File = try std.fs.cwd().createFile(new_file_sub_path, CreateFlags{ .truncate = true }); + + const new_file_sub_path = mem.concat(gpa, u8, &.{ path, ".cleaned" }) catch unreachable; + gpa.free(encoded_path_buffer); + encoded_path_buffer = gpa.alloc(u8, new_file_sub_path.len) catch unreachable; + const encoded_new_file_sub_path = try encodePathForOs(new_file_sub_path, encoded_path_buffer); + var new_file: File = try cwd().createFile(encoded_new_file_sub_path, CreateFlags{ .truncate = true }); defer new_file.close(); const writer = new_file.writer(); + while (try file_reader.readUntilDelimiterOrEof(buffer, '\n')) |line| { - if (line.len == 0) { - continue; - } - if (eql(u8, line, " ")) { - continue; - } - if (eql(u8, line, "\n")) { + if (continueToNextLine(line)) { continue; } - if (eql(u8, line, "\r")) { - continue; - } - if (eql(u8, line, "\t")) { - continue; - } - if (line[0] == '#') { - continue; - } - if (line.len > 0) { - var word: [1024]u8 = undefined; - var i: usize = 0; - var c: usize = 0; - while (i < line.len) { - const char = line[i]; - if (isAlphabetic(char)) { - word[c] = char; - c += 1; - } else if (isWhitespace(char)) { - if (c > 0) { - const string = std.ascii.lowerString(word[0..c], word[0..c]); - writer.writeAll(string) catch |err| { - return err; - }; - writer.writeAll("\n") catch |err| { - return err; - }; - c = 0; - } + var word: [1024]u8 = undefined; + var i: usize = 0; + var c: usize = 0; + while (i < line.len) { + const char = line[i]; + if (isAlphabetic(char)) { + word[c] = char; + c += 1; + } else if (isWhitespace(char)) { + if (c > 0) { + const string = std.ascii.lowerString(word[0..c], word[0..c]); + writer.writeAll(string) catch |err| { + return err; + }; + writer.writeAll("\n") catch |err| { + return err; + }; + c = 0; } - i += 1; } + i += 1; } } - return new_file_sub_path; } +fn encodePathForOs(path: []u8, encoded_path_buffer: []u8) ![]u8 { + if (os_tag == .windows) { + var i: usize = 0; + var c: usize = 0; + while (i < path.len) : (i += 1) { + const codepoint = try unicode.utf8Decode(path[i]); + c += try unicode.wtf8Encode(codepoint, encoded_path_buffer[c..]); + } + return encoded_path_buffer; + } else { + return path; + } +} + +fn continueToNextLine(line: []u8) bool { + if (line.len == 0) { + return true; + } + if (eql(u8, line, " ")) { + return true; + } + if (eql(u8, line, "\n")) { + return true; + } + if (eql(u8, line, "\r")) { + return true; + } + if (eql(u8, line, "\t")) { + return true; + } + if (line[0] == '#') { + return true; + } + if (line.len == 0) { + return true; + } + return false; +} + test "clean file" { var file_path = "test_file.txt".*; var file: File = try std.fs.cwd().createFile(&file_path, CreateFlags{ .truncate = true, .read = true }); defer file.close(); var writer = file.writer(); writer.writeAll("Hello, this is a test file\n") catch |err| { - std.debug.print("Error writing to file: {any}\n", .{err}); + print("Error writing to file: {any}\n", .{err}); return err; }; writer.writeAll("This is a test file\n") catch |err| { - std.debug.print("Error writing to file: {any}\n", .{err}); + print("Error writing to file: {any}\n", .{err}); return err; }; writer.writeAll("This is a test file\n") catch |err| { - std.debug.print("Error writing to file: {any}\n", .{err}); + print("Error writing to file: {any}\n", .{err}); return err; }; writer.writeAll("This is a test file\n") catch |err| { - std.debug.print("Error writing to file: {any}\n", .{err}); + print("Error writing to file: {any}\n", .{err}); return err; }; var buffer: [1024]u8 = undefined; - const cleaned_file: []const u8 = try cleanFile(&file_path, &buffer); - const cleaned_file_read = try std.fs.cwd().openFile(cleaned_file, .{}); + const cleaned_file: []const u8 = cleanFile(&file_path, &buffer) catch |err| { + print("Error cleaning file: {any}\n", .{err}); + return err; + }; + const cleaned_file_read = try cwd().openFile(cleaned_file, .{}); defer cleaned_file_read.close(); const cleaned_file_reader = cleaned_file_read.reader(); var cleaned_buffer: [1024]u8 = undefined; const expected: [6][:0]u8 = .{ @constCast("hello"), @constCast("this"), @constCast("is"), @constCast("a"), @constCast("test"), @constCast("file") }; - std.debug.print("expected: {s}\n", .{expected[0..expected.len]}); + print("expected: {s}\n", .{expected[0..expected.len]}); var foundExpected = false; while (try nextLine(cleaned_file_reader, &cleaned_buffer)) |line| { - std.debug.print("line: {s}\n", .{line}); + print("line: {s}\n", .{line}); inner: for (&expected) |expectedValue| { - std.debug.print("{s}\n", .{expectedValue}); + print("{s}\n", .{expectedValue}); if (eql(u8, line, expectedValue)) { - std.debug.print("found expected: {s}\n", .{expectedValue}); + print("found expected: {s}\n", .{expectedValue}); foundExpected = true; break :inner; }