fork of gitlab.com/devnw/zig/uri for zig 0.17

Files
README.md

URI #

RFC 3986 compliant URI/URL parsing and manipulation library for Zig.

Project Status: Active Development Current Version: v0.1.5 GitLab: devnw/zig/uri

Status #

Aspect Info
API Stability Beta
Zig Version 0.16.0+
Platforms Linux, macOS, Windows

Features #

  • RFC Compliant: Strict adherence to RFC 3986 (URI Generic Syntax) and RFC 8141 (URN)
  • Minimal Dependencies: Core parsing uses only Zig standard library; optional thread-pool dependency for parallel batch operations
  • Zero-Copy Parsing: No allocations for basic parsing operations
  • Cross-Platform: Works on all Zig-supported targets
  • Full Component Access: Scheme, authority, userinfo, host, port, path, query, fragment
  • URI Resolution: RFC 3986 Section 5 reference resolution algorithm
  • Normalization: Case, percent-encoding, path, and scheme-based normalization per RFC 3986 Section 6
  • Percent-Encoding: Complete encode/decode support
  • Parse Options: Strict/allow_spaces/iri_mode validation applied on input
  • Component-Aware Encoding: Percent-encoding respects URI component rules
  • URN Support: Full RFC 8141 URN parsing with NID, NSS, and r/q/f-components
  • Resource Registry: Document storage with fetch callbacks and anchor support

Installation #

Add to your build.zig.zon:

.dependencies = .{
    .uri = .{
        .url = "https://gitlab.com/devnw/zig/uri/-/archive/main/uri-main.tar.gz",
        .hash = "...",
    },
},

Then in your build.zig:

const uri_dep = b.dependency("uri", .{
    .target = target,
    .optimize = optimize,
});
exe.root_module.addImport("uri", uri_dep.module("uri"));

Quick Start #

Parse a URI #

const uri = @import("uri");

pub fn main() !void {
    const parsed = try uri.parse("https://user:pass@example.com:8080/path?query=value#fragment");

    // Access components
    std.debug.print("Scheme: {s}\n", .{parsed.scheme.?});        // "https"
    std.debug.print("Host: {s}\n", .{parsed.host.?});            // "example.com"
    std.debug.print("Port: {d}\n", .{parsed.port.?});            // 8080
    std.debug.print("Path: {s}\n", .{parsed.path});              // "/path"
    std.debug.print("Query: {s}\n", .{parsed.query.?});          // "query=value"
    std.debug.print("Fragment: {s}\n", .{parsed.fragment.?});    // "fragment"
}

Parse a URN #

const parsed = try uri.parse("urn:isbn:0451450523");

std.debug.print("NID: {s}\n", .{parsed.urn.?.nid.?});   // "isbn"
std.debug.print("NSS: {s}\n", .{parsed.urn.?.nss.?});   // "0451450523"

Resolve URI References #

const allocator = std.heap.page_allocator;
const base = try uri.parse("http://example.com/a/b/c");
const reference = try uri.parse("../d");
const resolved = try uri.resolve(allocator, base, reference);
// Result: "http://example.com/a/d"

Percent-Encoding #

// Encode
const encoded = try uri.percentEncode(allocator, "hello world!", .path);
// Result: "hello%20world%21"

// Decode
const decoded = try uri.percentDecode(allocator, "hello%20world%21");
// Result: "hello world!"

Resource Registry #

var reg = uri.Registry.init(allocator);
defer reg.deinit();

try reg.register("https://example.com/schema.json", "{\"type\":\"object\"}", "application/json");
const resource = try reg.get("https://example.com/schema.json", false);

URI Components #

         foo://user:pass@example.com:8042/over/there?name=ferret#nose
         \_/   \________________________/\_________/ \_________/ \__/
          |              |                    |            |       |
       scheme        authority              path        query   fragment
          |   _________________|________________
         / \ /                                  \
         urn:example:animal:ferret:nose

The authority component breaks down into:

         userinfo       host          port
         +--+---+ +------+------+ +---+---+
         user:pass@www.example.com:8080

Documentation #

Roadmap #

v0.1.0 - Foundation ✅ #

Core URI parsing and manipulation functionality.

v0.2.0 - Enhanced Parsing #

Improved validation and encoding options.

v0.3.0 - IRI Support #

RFC 3987 Internationalized Resource Identifiers.

v0.4.0 - IPv6 Enhancements #

RFC 6874 IPv6 Zone ID support.

v1.0.0 - Stable Release #

Production-ready release.

RFC Compliance #

RFC Title Status Details
RFC 3986 URI Generic Syntax Implemented #1, #2, #3
RFC 8141 URN Syntax Implemented #5
RFC 3987 IRI Syntax Planned #13, #14, #15
RFC 6874 IPv6 Zone ID Planned #16, #17, #18
WHATWG URL Standard Partial #19

See RFC_COMPLIANCE.md for detailed compliance information.

Building #

# Build
zig build

# Run tests
zig build test

# Run RFC test vectors
zig build test-vectors

# Run acceptance tests
zig build test-accept

# Run dataset tests (500+ URIs)
zig build test-dataset

# Run all tests
zig build test-all

Or use Make:

make          # Build and test
make test     # Run tests
make dev      # Development workflow (fmt + check + test)
make help     # Show all targets

Development #

With Nix #

nix develop
make dev

Requirements #

  • Zig 0.16.0 or compatible

Cross-Platform Support #

The library supports all Zig target platforms:

  • Linux (x86_64, ARM64, ARM, RISC-V)
  • macOS (x86_64, ARM64)
  • Windows (x86_64, ARM64)
  • FreeBSD, NetBSD, OpenBSD
  • And more...

License #

Apache-2.0 License - see LICENSE file.

Contributing #

Contributions are welcome! Please see our open issues for areas where help is needed.

Getting Started #

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Ensure all tests pass (zig build test-all)
  5. Submit a merge request

Guidelines #

  1. Code follows Zig style guidelines (zig fmt)
  2. All tests pass
  3. New features include tests
  4. RFC compliance is maintained
  5. Reference relevant issues in your MR

Priority Areas #

  • RFC 3986 - Uniform Resource Identifier (URI): Generic Syntax
  • RFC 8141 - Uniform Resource Names (URNs)
  • RFC 3987 - Internationalized Resource Identifiers (IRIs)
  • RFC 6874 - Representing IPv6 Zone Identifiers in URIs
  • WHATWG URL Standard - Living Standard for URLs