Skip to content

deeplink

stable

URI parsing, encoding, and construction utilities for working with deep links and URLs, including query string handling, component extraction, and normalization.

use plugin deeplink::{parse_uri, encode_uri_component, decode_uri_component, …}
13 functions Systems
/ filter jk navigate Esc clear
Functions (13)
  1. parse_uri Parses a URI string into components table
  2. encode_uri_component Percent-encodes a string for use in a URI
  3. decode_uri_component Decodes a percent-encoded URI component
  4. build_uri Builds a URI from scheme, host, path, query
  5. parse_query_string Parses a query string into a table
  6. build_query_string Serializes a table into a query string
  7. get_scheme Extracts the scheme from a URI
  8. get_host Extracts the host from a URI
  9. get_path Extracts the path from a URI
  10. get_query Extracts the raw query string from a URI
  11. get_fragment Extracts the fragment from a URI
  12. join_path Joins a base path and a segment
  13. normalize_uri Normalizes a URI (lowercase, default ports)

Overview

The deeplink plugin is a dependency-free toolkit for working with URIs and deep links. It treats a URI as the familiar scheme://[authority@]host[:port][/path][?query][#fragment] shape and gives you both whole-URI operations (parse, build, normalize) and targeted accessors that pull out a single component. Parsing returns a plain table, so you read fields with parts["host"] and friends — there are no handles or stateful objects to manage.

Reach for it whenever you need to decode an incoming deep link (for example a custom myapp:// scheme), assemble an outgoing URL from parts, or sanitize user-supplied query values with percent-encoding. Because every function is a pure string-in / value-out helper, calls compose freely and are safe to call in any order.

Common patterns

Parse an incoming deep link and route on its components:

use plugin deeplink::{parse_uri, parse_query_string}

let parts = parse_uri("myapp://open/product?id=42&ref=email")
let query = parse_query_string(parts["query"])

print("host: {parts["host"]}")     // "open"
print("path: {parts["path"]}")     // "/product"
print("id: {query["id"]}")         // "42"
print("ref: {query["ref"]}")       // "email"

Build a safe URL from parts, encoding each value before it goes on the wire:

use plugin deeplink::{build_query_string, build_uri, encode_uri_component}

let q = encode_uri_component("zolo lang & friends")
let qs = build_query_string(#{"q": q, "page": "1"})
print(qs)  // "q=zolo%20lang%20%26%20friends&page=1"

let uri = build_uri("https", "api.example.com", "/search", #{"q": q})
print(uri)  // "https://api.example.com/search?q=zolo%20lang%20%26%20friends"

Stitch a path together and normalize the final URI for comparison or storage:

use plugin deeplink::{join_path, normalize_uri}

let path = join_path("/api/v1/", "/users")  // "/api/v1/users"
let uri = "HTTPS://Example.COM:443{path}"
print(normalize_uri(uri))  // "https://example.com/api/v1/users"

Parses a URI string into components table

Parses a URI string into a table with fields: scheme, authority, host, port, path, query, and fragment. The port field is an integer when present and parseable, otherwise nil.

use plugin deeplink::{parse_uri}

let parts = parse_uri("https://example.com:8080/api/v1?key=abc#section")
print(parts["scheme"])    // "https"
print(parts["host"])      // "example.com"
print(parts["port"])      // 8080
print(parts["path"])      // "/api/v1"
print(parts["query"])     // "key=abc"
print(parts["fragment"])  // "section"

Authority (userinfo) is captured separately when present, and a URI with no port leaves port as nil:

use plugin deeplink::{parse_uri}

let parts = parse_uri("ftp://user@files.example.com/data")
print(parts["authority"])  // "user"
print(parts["host"])       // "files.example.com"
print(parts["port"])       // nil

Percent-encodes a string for use in a URI

Percent-encodes a string so it is safe to embed in a URI component. Characters outside the unreserved set (A-Z, a-z, 0-9, -, _, ., ~) are encoded as %XX.

use plugin deeplink::{encode_uri_component}

let encoded = encode_uri_component("hello world & more")
print(encoded)  // "hello%20world%20%26%20more"

Use it on each value before assembling a query string so reserved characters survive the round trip:

use plugin deeplink::{encode_uri_component}

print(encode_uri_component("a/b?c=d"))      // "a%2Fb%3Fc%3Dd"
print(encode_uri_component("safe-name_1.0")) // "safe-name_1.0"

Decodes a percent-encoded URI component

Decodes a percent-encoded URI component back to its original string form. Bytes are reassembled and interpreted as UTF-8.

use plugin deeplink::{decode_uri_component}

let decoded = decode_uri_component("hello%20world%20%26%20more")
print(decoded)  // "hello world & more"

It is the exact inverse of encode_uri_component, so encoding then decoding yields the original value:

use plugin deeplink::{encode_uri_component, decode_uri_component}

let original = "café & crème"
let round_trip = decode_uri_component(encode_uri_component(original))
print(round_trip)  // "café & crème"

Builds a URI from scheme, host, path, query

Constructs a URI from its parts. query_table is an optional table of string key-value pairs that will be serialized as query parameters; pass nil (or omit it) to produce a URI with no query string.

use plugin deeplink::{build_uri}

let uri = build_uri("https", "api.example.com", "/search", #{"q": "zolo lang", "page": "1"})
print(uri)
// "https://api.example.com/search?q=zolo lang&page=1"

let simple = build_uri("myapp", "open", "/profile", nil)
print(simple)  // "myapp://open/profile"

It pairs naturally with encode_uri_component to keep special characters intact in the generated query:

use plugin deeplink::{build_uri, encode_uri_component}

let uri = build_uri("myapp", "share", "/post", #{"title": encode_uri_component("Hi there!")})
print(uri)  // "myapp://share/post?title=Hi%20there%21"

Parses a query string into a table

Parses a URL query string (with or without a leading ?) into a table of key-value string pairs. Empty segments are skipped, and a key with no = maps to an empty string.

use plugin deeplink::{parse_query_string}

let params = parse_query_string("?name=Alice&role=admin&active=true")
print(params["name"])    // "Alice"
print(params["role"])    // "admin"

Combine it with get_query to pull parameters straight out of a full URI:

use plugin deeplink::{get_query, parse_query_string}

let q = get_query("https://example.com/items?sort=price&dir=asc")
let params = parse_query_string(q)
print(params["sort"])  // "price"
print(params["dir"])   // "asc"

Serializes a table into a query string

Serializes a table of string key-value pairs into a query string (without a leading ?). Non-string keys or values are skipped.

use plugin deeplink::{build_query_string}

let qs = build_query_string(#{"user": "alice", "tab": "settings"})
print(qs)  // "user=alice&tab=settings"

It round-trips with parse_query_string, so you can parse, tweak, and re-serialize:

use plugin deeplink::{parse_query_string, build_query_string}

let params = parse_query_string("page=1&size=20")
let qs = build_query_string(params)
print(qs)  // "page=1&size=20"

Extracts the scheme from a URI

Extracts just the scheme (protocol) portion from a URI string. Returns an empty string when there is no :// separator.

use plugin deeplink::{get_scheme}

print(get_scheme("ftp://files.example.com/data"))  // "ftp"
print(get_scheme("myapp://launch"))               // "myapp"

Use it to dispatch on link type before doing any further parsing:

use plugin deeplink::{get_scheme}

let scheme = get_scheme("myapp://open/settings")
if scheme == "myapp" {
  print("internal deep link")
}

Extracts the host from a URI

Extracts the hostname from a URI string (without the port).

use plugin deeplink::{get_host}

print(get_host("https://api.example.com:443/path"))  // "api.example.com"

Extracts the path from a URI

Extracts the path portion from a URI string, including the leading /.

use plugin deeplink::{get_path}

print(get_path("https://example.com/users/42/profile?edit=true"))
// "/users/42/profile"

Extracts the raw query string from a URI

Extracts the raw query string (without ?) from a URI. Returns an empty string if there is no query.

use plugin deeplink::{get_query}

print(get_query("https://example.com/search?q=test&page=2"))
// "q=test&page=2"

Extracts the fragment from a URI

Extracts the fragment (hash) portion from a URI. Returns an empty string if absent.

use plugin deeplink::{get_fragment}

print(get_fragment("https://docs.example.com/page#section-3"))
// "section-3"

Joins a base path and a segment

Joins a base path and a segment, normalizing the slash between them to produce a single /. A trailing slash on base and a leading slash on segment are both trimmed so the join never doubles up.

use plugin deeplink::{join_path}

print(join_path("/api/v1/", "/users"))   // "/api/v1/users"
print(join_path("/api/v1", "users"))     // "/api/v1/users"

It is handy for building paths incrementally before handing them to build_uri:

use plugin deeplink::{join_path, build_uri}

let path = join_path("/api", "v2/items")
let uri = build_uri("https", "example.com", path, nil)
print(uri)  // "https://example.com/api/v2/items"

Normalizes a URI (lowercase, default ports)

Returns a normalized form of the URI: scheme and host are lowercased, default ports (80 for http, 443 for https) are removed, and a path that is just / is stripped.

use plugin deeplink::{normalize_uri}

let norm = normalize_uri("HTTPS://Example.COM:443/Home")
print(norm)  // "https://example.com/Home"

Use it to canonicalize URIs before comparing or de-duplicating them, while non-default ports are preserved:

use plugin deeplink::{normalize_uri}

print(normalize_uri("HTTP://API.test:80/"))        // "http://api.test"
print(normalize_uri("https://Cache.io:8443/v1"))   // "https://cache.io:8443/v1"
enespt-br