deeplink
stableURI 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, …} Functions (13)
- parse_uri Parses a URI string into components table
- encode_uri_component Percent-encodes a string for use in a URI
- decode_uri_component Decodes a percent-encoded URI component
- build_uri Builds a URI from scheme, host, path, query
- parse_query_string Parses a query string into a table
- build_query_string Serializes a table into a query string
- get_scheme Extracts the scheme from a URI
- get_host Extracts the host from a URI
- get_path Extracts the path from a URI
- get_query Extracts the raw query string from a URI
- get_fragment Extracts the fragment from a URI
- join_path Joins a base path and a segment
- 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"