Skip to content

Commit 1cc11a3

Browse files
Merge branch 'feature/util' into dev
2 parents 6bde7b8 + ca4d2f7 commit 1cc11a3

9 files changed

Lines changed: 600 additions & 55 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ compile_flags.txt
77
zig-out
88
.cache
99
init
10+
tests
1011

1112
# direnv
1213
.direnv

TODO.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,22 @@
44
- [ ] diff and patches; solutions and testing
55
- [ ] improve software design and implementation; commands pattern + MPSC queue
66
- [ ] add exercises
7-
- [ ] variables
8-
- [ ] functions
7+
- [x] variables
8+
- [x] functions
99
- [ ] control-flow (if-else)
1010
- [ ] primitives (datatypes)
11+
- [ ] type-conversion
1112
- [ ] strings (c style vs std::strings)
1213
- [ ] containers (c style containers vs std containers)
13-
- [ ] modules (#include, headers)
14+
- [ ] modules (#include, headers, C++20 modules; import export, std lib)
1415
- [ ] structs
1516
- [ ] enums
1617
- [ ] classes
17-
- [ ] memory (memory module, raw pointers, smart pointers)
1818
- [ ] generics (templating)
19-
- [ ] type-conversion
20-
- [ ] raii (memory, ownership, objects)
21-
- [ ] exception-handling (errors)
22-
- [ ] threads (concurrency, multi-threading, OS, hardware)
19+
- [ ] exceptions (errors, exception handling)
20+
- [ ] memory (memory module, raw pointers, smart pointers, computer architecture, operating systems, raii, performance)
21+
- [ ] threads (concurrency, multi-threading, operation systems, computer architecture)
2322
- [ ] preprocessors (#include, #def, #ifdef)
2423
- [ ] cpphistory (creator, why was it created, cpp versions, features, c++11, c++14, c++17, c++20, c++23, c++26)
2524
- [ ] bit-manipultion (bitwise arithmetic, operations, logic gates, hardware, OS)
25+
- [ ] network programming (sockets, server, client, IPC)

flake.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
gnumake
5757
hyperfine
5858
lldb
59+
perf
5960
treefmt
6061
valgrind
6162
];

src/cli/cli.zig renamed to src/cli.zig

Lines changed: 6 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
const STYLES = @import("styles.zig");
21
const STD = @import("std");
32

3+
const STYLES = @import("styles.zig");
4+
45
var stdin_buffer: [1024]u8 = undefined;
56
var stdin_reader = STD.fs.File.stdin().reader(&stdin_buffer);
67
const STDIN = &stdin_reader.interface;
@@ -30,48 +31,10 @@ const CLI = struct {
3031
current_exercise_prev_mod_time: i128 = 0,
3132
current_chapter: []const u8 = "",
3233
list_of_chapter_support_files: STD.ArrayList([]const u8) = .empty,
34+
list_of_patches: STD.ArrayList([]const u8) = .empty,
3335
};
3436

35-
pub fn iterateDirectory(
36-
allocator: STD.mem.Allocator,
37-
extra_options: struct {
38-
dir_path: []const u8 = "exercises",
39-
},
40-
) !STD.ArrayList([]const u8) {
41-
var list_of_contents: STD.ArrayList([]const u8) = .empty;
42-
43-
const TOP_LEVEL_DIR = try STD.fs.cwd().openDir(extra_options.dir_path, .{ .iterate = true });
44-
var chapters = TOP_LEVEL_DIR.iterate();
45-
46-
while (try chapters.next()) |chapter| {
47-
const CHAPTER_DIR_PATH = try STD.fs.path.join(allocator, &.{ extra_options.dir_path, chapter.name });
48-
49-
const CHAPTER_DIR = try STD.fs.cwd().openDir(CHAPTER_DIR_PATH, .{ .iterate = true });
50-
var contents = CHAPTER_DIR.iterate();
51-
52-
while (try contents.next()) |content| {
53-
const CONTENT_FILE_PATH = try STD.fs.path.join(allocator, &.{ extra_options.dir_path, chapter.name, content.name });
54-
55-
if (STD.mem.containsAtLeast(u8, content.name, 1, ".cpp")) {
56-
try list_of_contents.append(allocator, CONTENT_FILE_PATH);
57-
}
58-
}
59-
}
60-
61-
STD.sort.insertion(
62-
[]const u8,
63-
list_of_contents.items,
64-
{},
65-
struct {
66-
fn lessThan(_: void, a: []const u8, b: []const u8) bool {
67-
return STD.mem.lessThan(u8, a, b);
68-
}
69-
}.lessThan,
70-
);
71-
72-
return list_of_contents;
73-
}
74-
37+
// TODO: strategy + observer pattern; iterate exercises, iterate patches
7538
fn iterateExerciseDirectory(
7639
self: *CLI,
7740
exercise_dir: struct { dir_path: []const u8 = "exercises" },
@@ -160,6 +123,7 @@ fn iterateNextExercise(self: *CLI) !void {
160123
try compileCurrentExercise(self);
161124
}
162125

126+
// TODO: implement abstract factory/protoype; watch current exercise and support files
163127
fn watchFileChanges(self: *CLI, polling_rate_ms: u64) !void {
164128
while (true) {
165129
const CURRENT_EXERCISE_METADATA = try STD.fs.cwd().statFile(self.current_exercise);
@@ -375,3 +339,4 @@ pub fn run(allocator: STD.mem.Allocator, extra_options: struct { exercises_dir_p
375339

376340
try userInput(self);
377341
}
342+

src/main.zig

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const STD = @import("std");
2-
const CLI = @import("cli/cli.zig");
3-
const PATCH_SYSTEM = @import("cli/patch.zig");
2+
3+
const CLI = @import("cli.zig");
4+
const PATCH_SYSTEM = @import("patch.zig");
45

56
pub fn help_documentation() void {
67
STD.debug.print("{s}\n", .{CLI.ASCII_ART});
@@ -14,24 +15,24 @@ pub fn help_documentation() void {
1415
pub fn main() !void {
1516
var mem_arena = STD.heap.ArenaAllocator.init(STD.heap.page_allocator);
1617
defer mem_arena.deinit();
17-
const mem_allocator = mem_arena.allocator();
18+
const MEM_ALLOCATOR = mem_arena.allocator();
1819

1920
if (STD.os.argv.len > 2) {
2021
STD.debug.print("Too many arguments\n", .{});
2122
help_documentation();
2223
}
2324

2425
if (STD.os.argv.len == 1) {
25-
try CLI.run(mem_allocator, .{});
26+
try CLI.run(MEM_ALLOCATOR, .{});
2627
return;
2728
}
2829

2930
const PROGRAM_ARGUMENT = STD.mem.span(STD.os.argv[1]);
3031

3132
if (STD.mem.eql(u8, PROGRAM_ARGUMENT, "-s") or STD.mem.eql(u8, PROGRAM_ARGUMENT, "--solutions")) {
32-
try CLI.run(mem_allocator, .{ .exercises_dir_path = ".patches/solutions" });
33+
try CLI.run(MEM_ALLOCATOR, .{ .exercises_dir_path = ".patches/solutions" });
3334
} else if (STD.mem.eql(u8, PROGRAM_ARGUMENT, "-p") or STD.mem.eql(u8, PROGRAM_ARGUMENT, "--patch")) {
34-
try PATCH_SYSTEM.run(mem_allocator);
35+
try PATCH_SYSTEM.run(MEM_ALLOCATOR);
3536
} else if (STD.mem.eql(u8, PROGRAM_ARGUMENT, "-h") or STD.mem.eql(u8, PROGRAM_ARGUMENT, "--help") or STD.mem.eql(u8, PROGRAM_ARGUMENT, "help")) {
3637
help_documentation();
3738
} else {

src/patch.zig

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
const STD = @import("std");
2+
3+
const CLI = @import("cli.zig");
4+
const UTIL = @import("util.zig");
5+
const STYLES = @import("styles.zig");
6+
7+
const PatchSystem = struct {
8+
list_of_original_file_paths: STD.ArrayList([]const u8) = .empty,
9+
list_of_modified_file_paths: STD.ArrayList([]const u8) = .empty,
10+
};
11+
12+
fn iterateDirs(allocator: STD.mem.Allocator, patch_system: *PatchSystem) !void {
13+
_ = patch_system;
14+
15+
try UTIL.iterateDirectory(allocator, ".patches/solutions", .{ .is_print_list_of_contents = true });
16+
17+
}
18+
19+
// TODO: clean up
20+
fn generatePatches(allocator: STD.mem.Allocator, patch_system: *PatchSystem) !void {
21+
for (patch_system.list_of_exercises.items, patch_system.list_of_solutions.items) |exercise, solution| {
22+
const BUFFER_SIZE = comptime STD.math.pow(usize, 2, 16);
23+
24+
// TODO: DRY
25+
var current_solution_filepath_iterator = STD.mem.splitAny(u8, solution, "/");
26+
var current_solution_filepath_list: STD.ArrayList([]const u8) = .empty;
27+
28+
while (current_solution_filepath_iterator.next()) |current_solution_filepath_slice_element| {
29+
try current_solution_filepath_list.append(allocator, current_solution_filepath_slice_element);
30+
}
31+
32+
const TOP_LEVEL_DIR = current_solution_filepath_list.items[0];
33+
const CHAPTER_DIR = current_solution_filepath_list.items[2];
34+
const SOLUTION_FILENAME = current_solution_filepath_list.items[3];
35+
36+
const OUTPUT_FILENAME = try STD.mem.replaceOwned(u8, allocator, SOLUTION_FILENAME, ".cpp", ".patch");
37+
38+
const OUTPUT_FILEPATH = try STD.fs.path.join(allocator, &[_][]const u8{ TOP_LEVEL_DIR, "patches", CHAPTER_DIR, OUTPUT_FILENAME });
39+
40+
STD.debug.print("diff {s} {s} --> {s}{s}{s}\n", .{
41+
exercise,
42+
solution,
43+
STYLES.ASCII_STYLES.underline,
44+
OUTPUT_FILEPATH,
45+
STYLES.ASCII_STYLES.clear_style,
46+
});
47+
48+
// TODO: DRY
49+
var process_args: STD.ArrayList([]const u8) = .empty;
50+
51+
try process_args.appendSlice(allocator, &[_][]const u8{ "diff", "-u", exercise, solution });
52+
var process = STD.process.Child.init(process_args.items, allocator);
53+
54+
process.stderr_behavior = .Pipe;
55+
process.stdout_behavior = .Pipe;
56+
57+
var process_stdout_buffer: STD.ArrayList(u8) = .empty;
58+
var process_stderr_buffer: STD.ArrayList(u8) = .empty;
59+
60+
process.spawn() catch {
61+
STD.debug.print("{s}Failed to generate patches!\n{s}", .{
62+
STYLES.ASCII_STYLES.red,
63+
STYLES.ASCII_STYLES.clear_style,
64+
});
65+
return;
66+
};
67+
68+
try process.collectOutput(allocator, &process_stdout_buffer, &process_stderr_buffer, BUFFER_SIZE);
69+
70+
const PATCHES_DIR_PATH = try STD.fs.path.join(allocator, &[_][]const u8{ TOP_LEVEL_DIR, "patches" });
71+
const PATCHES_CHAPTER_DIR_PATH = try STD.fs.path.join(allocator, &[_][]const u8{ TOP_LEVEL_DIR, "patches", CHAPTER_DIR });
72+
73+
STD.fs.cwd().access(PATCHES_DIR_PATH, .{}) catch {
74+
try STD.fs.cwd().makeDir(PATCHES_DIR_PATH);
75+
};
76+
77+
STD.fs.cwd().access(PATCHES_CHAPTER_DIR_PATH, .{}) catch {
78+
try STD.fs.cwd().makeDir(PATCHES_CHAPTER_DIR_PATH);
79+
};
80+
81+
try STD.fs.cwd().writeFile(.{ .sub_path = OUTPUT_FILEPATH, .data = process_stdout_buffer.items });
82+
}
83+
84+
STD.debug.print("\n{s}Generated patches -> {s}./patches/patches{s}\n", .{ STYLES.ASCII_STYLES.bold, STYLES.ASCII_STYLES.underline, STYLES.ASCII_STYLES.clear_style });
85+
}
86+
87+
// TODO: implement patch function
88+
fn patch(
89+
allocator: STD.mem.Allocator,
90+
file: []const u8,
91+
patch_file: []const u8,
92+
extra_options: struct { reverse: bool = true },
93+
) !void {
94+
_ = extra_options;
95+
96+
// TODO: DRY
97+
const BUFFER_SIZE = comptime STD.math.pow(usize, 2, 16);
98+
99+
var process_args: STD.ArrayList([]const u8) = .empty;
100+
101+
try process_args.appendSlice(allocator, &[_][]const u8{ "patch", "-u", file, patch_file });
102+
var process = STD.process.Child.init(process_args.items, allocator);
103+
104+
process.stderr_behavior = .Pipe;
105+
process.stdout_behavior = .Pipe;
106+
107+
var process_stdout_buffer: STD.ArrayList(u8) = .empty;
108+
var process_stderr_buffer: STD.ArrayList(u8) = .empty;
109+
110+
process.spawn() catch {
111+
STD.debug.print("{s}Failed to patch {s}file!\n{s}", .{
112+
STYLES.ASCII_STYLES.red,
113+
file,
114+
STYLES.ASCII_STYLES.clear_style,
115+
});
116+
return;
117+
};
118+
119+
try process.collectOutput(allocator, &process_stdout_buffer, &process_stderr_buffer, BUFFER_SIZE);
120+
}
121+
122+
pub fn run(allocator: STD.mem.Allocator) !void {
123+
STD.debug.print("{s}\n\n", .{CLI.ASCII_ART});
124+
125+
STD.debug.print("Creating patch system...\n", .{});
126+
const patch_system: *PatchSystem = try allocator.create(PatchSystem);
127+
defer allocator.destroy(patch_system);
128+
129+
STD.debug.print("Iterating exercises...\n", .{});
130+
try iterateDirs(allocator, patch_system);
131+
132+
// STD.debug.print("Generating patches...\n", .{});
133+
// try generatePatches(allocator, patch_system);
134+
}
135+
136+
// tests
137+
138+
const TEST_PATCHES_DIR = "tests/patches";
139+
const ORIGINAL_FILES_DIR = TEST_PATCHES_DIR ++ "/" ++ "original";
140+
const ORIGINAL_FILE_PATH = ORIGINAL_FILES_DIR ++ "/" ++ "original.txt";
141+
const MODIFIED_FILES_DIR = TEST_PATCHES_DIR ++ "/" ++ "modified";
142+
const MODIFIED_FILE_PATH = MODIFIED_FILES_DIR ++ "/" ++ "modified.txt";
143+
144+
fn initTest() !void {
145+
STD.fs.cwd().access(ORIGINAL_FILES_DIR, .{}) catch {
146+
try STD.fs.cwd().makePath(ORIGINAL_FILES_DIR);
147+
};
148+
149+
STD.fs.cwd().access(MODIFIED_FILES_DIR, .{}) catch {
150+
try STD.fs.cwd().makePath(MODIFIED_FILES_DIR);
151+
};
152+
153+
STD.fs.cwd().access(ORIGINAL_FILE_PATH, .{}) catch {
154+
try STD.fs.cwd().writeFile(.{ .sub_path = ORIGINAL_FILE_PATH, .data = "hello", .flags = .{ .truncate = true } });
155+
};
156+
157+
STD.fs.cwd().access(MODIFIED_FILE_PATH, .{}) catch {
158+
try STD.fs.cwd().writeFile(.{ .sub_path = MODIFIED_FILE_PATH, .data = "hey", .flags = .{ .truncate = true } });
159+
};
160+
}
161+
162+
test "patch system" {
163+
var allocator: STD.mem.Allocator = STD.testing.allocator;
164+
165+
try PATCH.run(allocator);
166+
}
167+
168+
test "patch" {
169+
var allocator: STD.mem.Allocator = STD.testing.allocator;
170+
171+
_ = allocator;
172+
}
File renamed without changes.

src/test.zig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const STD = @import("std");
2+
3+
const CLI = @import("cli.zig");
4+
const PATCH = @import("patch.zig");
5+
const UTIL = @import("util.zig");
6+
7+
test "run all tests" {
8+
STD.testing.refAllDecls(@This());
9+
}

0 commit comments

Comments
 (0)