Uri-glob: A library for working with globs in URI references

Hi,

I created a library for working with globs in URI references. The library is inspired by
URL Pattern.

Features

  • Parsing: Parsing of URI references with support for captures.
  • Matching: Matching URI references against a glob.
  • Expanding: Expanding of URI globs to all possible combinations of URI strings.
  • Scanning: Scanning of iterators of URI references for matches.

Usage

The following snippet shows how to use the library:

const std = @import("std");
const heap = std.heap;
const mem = std.mem;
const debug = std.debug;
const urig = @import("uri_glob");

pub fn main() !void {
    var gpa = heap.GeneralPurposeAllocator(.{}){};
    defer debug.assert(gpa.deinit() == .ok);
    var arena = heap.ArenaAllocator.init(gpa.allocator());
    defer arena.deinit();
    const allocator = arena.allocator();

    debug.print("- Creating glob.\n", .{});

    const template = "./repos/{repo}/index.(css|html|[jt]s?[x])";
    var glob, const remaining = try urig.Sequence.create(allocator, template);

    // Ensure that the whole `template` has been parsed.
    debug.assert(remaining.len == 0);

    debug.print("- Parsing.\n", .{});

    const parse_filenames = &[_][]const u8{
        "./repos/website/index.css",
        "./repos/website/index.html",
        "./repos/website/index.js",
    };

    for (parse_filenames, 0..) |filename, i| {
        const parsed, const captures = try glob.parse(filename);
        debug.assert(mem.eql(u8, parse_filenames[i], parsed));
        debug.assert(mem.eql(u8, "website", captures.get("repo").?));
    }

    debug.print("- Matching.\n", .{});

    const match_filename = "./repos/website/index.tsx";
    debug.assert(glob.match(match_filename));

    debug.print("- Expanding.\n", .{});

    const capture_entries = &[_]urig.Glob.ExpansionVariableEntry{
        .{ "repo", "website" },
    };

    const expansion_expectations = &[_][]const u8{
        "./repos/website/index.css",
        "./repos/website/index.html",
        "./repos/website/index.js",
        "./repos/website/index.ts",
        "./repos/website/index.jsx",
        "./repos/website/index.tsx",
    };

    const expansions = try glob.expand(capture_entries);
    debug.assert(expansion_expectations.len == expansions.len);

    for (expansion_expectations, 0..) |expected, i| {
        debug.assert(mem.eql(u8, expected, expansions[i]));
    }

    debug.print("- Scanning.\n", .{});

    const base_uri_reference = "./repos/website/";

    const scan_filenames = &[_][]const u8{
        "index.css",
        "index.html",
        "src/index.html",
        "index.js",
        "main.js",
    };

    const Iterator = urig.Glob.UriListIterator;
    var iterator = Iterator{ .data = .{ .list = scan_filenames } };

    var uri_iterator = urig.Glob.UriIterator(Iterator){
        .data = &iterator,
        .next = Iterator.next,
    };

    var scanner = glob.scan(Iterator, &uri_iterator, base_uri_reference);

    const scan_expectations = &[_][]const u8{
        "./repos/website/index.css",
        "./repos/website/index.html",
        "./repos/website/index.js",
    };

    var i: usize = 0;

    while (scanner.next()) |match| : (i += 1) {
        debug.assert(mem.eql(u8, scan_expectations[i], match));
    }

    debug.assert(scan_expectations.len == i);

    debug.print("- Done!\n", .{});
}

Examples

Some of the URI references the library can handle are shown the table below.

Glob Example URI
https://github.com/{owner}/{repo} https://github.com/oven-sh/bun
https://{sub_domain}(+[[:alnum:]]).google.com https://mail.google.com
*(*/)*.zig project/src/main.zig
https+[.] https://zig.guide

Installation

The library is published on GitHub.

3 Likes