~subsetpark/erasmus

1c1b9c89c5766d4934048f7820307271301b8efa — Zach Smith 4 months ago 11f4cdc
Use an array of structs instead of hashmap
2 files changed, 69 insertions(+), 63 deletions(-)

M src/main.zig
M src/util.zig
M src/main.zig => src/main.zig +66 -62
@@ 19,17 19,33 @@ const RuntimeError = error{
const begin_brackets = '{';
const end_brackets = '}';

const CharBuffer = std.ArrayList(u8);

const Brackets = struct {
    brackets: std.ArrayList([]const u8),
    error_slice: ?[]const u8,
};

const EscapedString = struct {
    escaped: std.ArrayList(u8),
    escaped: CharBuffer,
    error_slice: ?[]const u8,
};

const CharBuffer = std.ArrayList(u8);
const Note = struct {
    name: []const u8,
    body: CharBuffer,
    new_body: CharBuffer,
    backlinks: std.ArrayList([]const u8),
    references: std.ArrayList([]const u8),

    fn append(self: *Note, c: u8) !void {
        try self.new_body.append(c);
    }

    fn appendSlice(self: *Note, slice: []u8) !void {
        try self.new_body.appendSlice(slice);
    }
};

const help_text =
    \\Usage: er [-h] [-d] [DIRECTORY_NAME]


@@ 123,13 139,13 @@ fn findBracketedStrings(note_body: []const u8, brackets: *Brackets) !bool {
}

// Create a reference line and add it to a note.
fn appendRef(note_body: *CharBuffer, referent: []const u8, allocator: *mem.Allocator) !void {
fn appendRef(note: *Note, referent: []const u8, allocator: mem.Allocator) !void {
    const ref = try fmt.allocPrint(allocator, "{s}:{s}", .{
        ref_prefix,
        referent,
    });
    try note_body.appendSlice(ref);
    try note_body.append('\n');
    try note.appendSlice(ref);
    try note.append('\n');
}

// Process a string and escape any delimiters for reference creation.


@@ 161,40 177,35 @@ fn escape(s: []const u8, res: *EscapedString) RuntimeError!void {
// Backlinks: if any other note body refers to this one, add a reference to
// that body's name.
fn appendBacklinks(
    note_name: []const u8,
    note_body: *CharBuffer,
    notes: std.StringHashMap(CharBuffer),
    allocator: *mem.Allocator,
    note: *Note,
    notes: *std.ArrayList(Note),
    allocator: mem.Allocator,
) !void {
    var found_one = false;
    var all_notes = notes.iterator();

    const link_here = try fmt.allocPrint(allocator, "{c}{s}{c}", .{
        begin_brackets,
        note_name,
        note.name,
        end_brackets,
    });

    while (all_notes.next()) |entry| {
        const items = entry.value_ptr.*.items;
    for (notes.items) |other_note| {
        const items = other_note.body.items;
        if (mem.indexOf(u8, items, link_here) != null) {
            found_one = true;
            try appendRef(note_body, entry.key_ptr.*, allocator);
            try appendRef(note, other_note.name, allocator);
        }
    }

    // Backlinks padding.
    if (found_one) {
        try note_body.append('\n');
        try note.append('\n');
    }
}

// References: gather every reference in the body of the note.
fn appendReferences(
    note_name: []const u8,
    note_body: *CharBuffer,
    notes: std.StringHashMap(CharBuffer),
    allocator: *mem.Allocator,
    note: *Note,
    allocator: mem.Allocator,
) !void {
    var brackets = Brackets{
        .brackets = std.ArrayList([]const u8).init(allocator),


@@ 202,7 213,7 @@ fn appendReferences(
    };
    defer brackets.brackets.deinit();

    const found_one = findBracketedStrings(note_body.items, &brackets) catch |err| switch (err) {
    const found_one = findBracketedStrings(note.body.items, &brackets) catch |err| switch (err) {
        RuntimeError.BracketInBrackets => {
            std.debug.print("Found improperly nested brackets: {s}.\n", .{
                brackets.error_slice,


@@ 215,7 226,7 @@ fn appendReferences(
    };
    // References padding.
    if (found_one) {
        try note_body.append('\n');
        try note.append('\n');
    }
    // Escape and add forward references to note.
    for (brackets.brackets.items) |phrase| {


@@ 225,21 236,21 @@ fn appendReferences(
        };
        defer escaped.escaped.deinit();

        escape(phrase, &escaped) catch |err| {
        escape(phrase, &escaped) catch {
            std.debug.print("Found illegal character in brackets: `{s}`\n", .{
                escaped.error_slice,
            });
            std.os.exit(1);
        };
        try appendRef(note_body, escaped.escaped.items, allocator);
        try appendRef(note, escaped.escaped.items, allocator);
    }
}

pub fn main() !void {
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();
    const allocator = arena.allocator();

    const allocator = &arena.allocator;
    // Process CLI args.
    var maybe_directory_name: ?[]const u8 = null;
    var args = std.process.args();


@@ 258,7 269,8 @@ pub fn main() !void {
    }
    const directory_name = maybe_directory_name orelse ".";

    var notes = std.StringHashMap(CharBuffer).init(allocator);
    var notes = std.ArrayList(Note).init(allocator);
    defer notes.deinit();

    // Load note bodies into memory.
    const dir = try fs.cwd().openDir(directory_name, .{ .iterate = true });


@@ 269,53 281,45 @@ pub fn main() !void {
                directory_name,
                entry.name,
            });
            var contents = CharBuffer.init(allocator);
            try readBody(path, &contents);
            try notes.put(entry.name, contents);
            var note = Note{
                .name = entry.name,
                .body = CharBuffer.init(allocator),
                .new_body = CharBuffer.init(allocator),
                .backlinks = std.ArrayList([]const u8).init(allocator),
                .references = std.ArrayList([]const u8).init(allocator),
            };
            try readBody(path, &note.body);
            try notes.append(note);
        }
    }

    var next_generation = std.StringHashMap(CharBuffer).init(allocator);
    defer next_generation.deinit();

    // For each note, gather backlinks and references, and generate
    // contents.
    var note_names = notes.keyIterator();
    while (note_names.next()) |note_name_ptr| {
        const note_name = note_name_ptr.*;
        const maybe_note_body = notes.get(note_name);
        if (maybe_note_body) |note_body| {
            var new_note_body = CharBuffer.init(allocator);

            try appendBacklinks(note_name, &new_note_body, notes, allocator);
            try new_note_body.appendSlice(note_body.items);
            try appendReferences(note_name, &new_note_body, notes, allocator);

            // Optional debugging.
            if (debug_mode) {
                std.debug.print("{s}:\n\n{s}\n", .{
                    note_name,
                    new_note_body.items,
                });
            }

            try next_generation.put(note_name, new_note_body);
        } else unreachable;
    for (notes.items) |*note| {
        try appendBacklinks(note, &notes, allocator);
        try note.appendSlice(note.body.items);
        try appendReferences(note, allocator);

        // Optional debugging.
        if (debug_mode) {
            std.debug.print("{s}:\n\n{s}\n", .{
                note.name,
                note.new_body.items,
            });
        }
    }

    // Free the original note bodies.
    var all_notes = notes.iterator();
    while (all_notes.next()) |entry| {
        entry.value_ptr.deinit();
    for (notes.items) |note| {
        note.body.deinit();
    }
    notes.deinit();

    // Write to disk.
    var notes_to_write = next_generation.iterator();
    while (notes_to_write.next()) |entry| {
    for (notes.items) |note| {
        const path = try fs.path.join(allocator, &[_][]const u8{
            directory_name,
            entry.key_ptr.*,
            note.name,
        });
        const note_body = entry.value_ptr.*.items;
        try fs.cwd().writeFile(path, note_body);
        try fs.cwd().writeFile(path, note.new_body.items);
    }
}

M src/util.zig => src/util.zig +3 -1
@@ 1,3 1,5 @@
const std = @import("std");

pub fn nextLine(reader: anytype, buffer: []u8) !?[]const u8 {
    var line = (try reader.readUntilDelimiterOrEof(
        buffer,


@@ 5,7 7,7 @@ pub fn nextLine(reader: anytype, buffer: []u8) !?[]const u8 {
    )) orelse return null;
    // trim annoying windows-only carriage return character
    if (@import("builtin").os.tag == .windows) {
        return mem.trimRight(u8, line, "\r");
        return std.mem.trimRight(u8, line, "\r");
    } else {
        return line;
    }