Url

Url :: # (opaque)

A validated HTTP or HTTPS URL implemented entirely in Roc.

Http.send! and Http.get! take a Url rather than a Str, so a URL is checked once, before any host effect runs, instead of at the socket.

A URL written out in the source needs no call at all: Roc reaches for from_quote wherever a quoted literal is expected to have type Url, so the literal is validated at compile time and a bad one is a compile error.

body = Http.get_utf8!("http://127.0.0.1:8000/data.json")?

A URL built at runtime goes through parse, which answers a ParseErr. resolve follows a relative reference against a base URL, which is what a link found in a fetched document needs, and to_str turns any of them back into text.

Parsing is deliberately stricter than a browser's. Hosts must be ASCII DNS names, dotted-decimal IPv4 addresses, or bracketed IPv6 addresses made from hexadecimal groups with optional :: elision. IPv4-in-IPv6 and Unicode domain names are unsupported, and inputs a browser would repair -- missing authority slashes, backslashes -- are rejected rather than fixed.

Vendored verbatim from roc-lang/basic-cli's platform/Url.roc, which is published under the same Universal Permissive License 1.0 this repository uses. The shared roc-lang/http package supplies Request, Response, Method and Header but deliberately not a URL parser. Keeping this a byte-for-byte copy means the parser's behaviour, its error set, and its 67 inline tests can be re-synced from upstream by replacing the file.

parse : Str -> Try(Url, ParseErr)

Parse a dynamic string as an absolute HTTP or HTTPS URL.

The input must contain an explicit scheme and authority. Its host is lowercased, default ports are removed, dot path segments are normalized, and non-ASCII path, query, and fragment bytes are percent-encoded.

from_quote : Str -> Try(Url, [BadQuotedBytes(Str)])

Convert a quoted literal to a URL using the same validation as parse.

Roc calls this automatically when a quoted literal is expected to have type Url. A rejected literal reports a descriptive BadQuotedBytes error.

parser_for : encoding -> state -> Try({ value : Url, rest : state }, err)
    where [
        encoding.parse_str : encoding, state -> Try({ value : Str, rest : state }, err),
        encoding.invalid_value : encoding, state -> err,
    ]

Parse a URL from a string value supplied by a generic encoding.

encoder_for : encoding -> Url, state -> Try(state, err)
    where [
        encoding.encode_str : Str, state -> Try(state, err),
    ]

Encode a URL as its canonical string through a generic encoding.

to_str : Url -> Str

Serialize the URL in a stable normalized ASCII form.

Scheme and DNS host names are lowercase, default ports are omitted, paths are absolute, and IPv6 addresses use eight unpadded groups.

to_inspect : Url -> Str

Render a URL for debugging and test failures.

is_eq : Url, Url -> Bool

Compare URLs by their canonical serialized representation.

scheme : Url -> [Http, Https]

Return Http or Https.

host : Url -> Str

Return the canonical host.

IPv6 brackets are omitted; to_str includes them where required.

port : Url -> [None, Some(U16)]

Return an explicit non-default port.

Ports 80 for HTTP and 443 for HTTPS are canonicalized to None.

path : Url -> Str

Return the absolute percent-encoded path, always beginning with slash.

query : Url -> [None, Some(Str)]

Return the percent-encoded query without its leading question mark.

None and Some("") distinguish no query from a present empty query.

fragment : Url -> [None, Some(Str)]

Return the percent-encoded fragment without its leading hash.

None and Some("") distinguish no fragment from a present empty fragment.

without_fragment : Url -> Url

Return this URL without its fragment. HTTP never transmits fragments.

resolve : Url, Str -> Try(Url, ParseErr)

Resolve a strict relative reference or absolute web URL against this URL.

Root-relative, path-relative, query-only, and fragment-only references are supported. Scheme-relative references are rejected.

append_path_segments : Url, List(Str) -> Url

Append unencoded path segments.

Each list item is one segment, so slash characters inside an item are percent-encoded rather than treated as separators.

append_query_param : Url, Str, Str -> Url

Append one application/x-www-form-urlencoded query pair.

Existing parameters, ordering, duplicate names, and the fragment are preserved.

query_pairs : Url -> List((Str, Str))

Decode the query into ordered name/value pairs.

Plus signs decode as spaces, percent escapes decode as UTF-8 bytes, parameters without equals receive an empty value, and duplicates remain.

with_query : Url, [None, Some(Str)] -> Try(Url, ParseErr)

Replace or remove the query.

Some("") produces a present empty query. The supplied query may contain Unicode but must otherwise already obey URL query syntax.

with_fragment : Url, [None, Some(Str)] -> Try(Url, ParseErr)

Replace or remove the fragment.

Some("") produces a present empty fragment. Unicode is percent-encoded.

ParseErr : [
    CredentialsNotAllowed,
    EmptyHost,
    InternationalHostUnsupported,
    InvalidCharacter(U8),
    InvalidHost(Str),
    InvalidIpv4(Str),
    InvalidIpv6(Str),
    InvalidPercentEncoding(U64),
    InvalidPort(Str),
    MissingAuthority,
    MissingScheme,
    PortOutOfRange(U64),
    UnsupportedScheme(Str),
]

A reason a URL or relative reference could not be parsed.

This error set describes this module's strict HTTP/HTTPS subset. Inputs that browsers might repair, such as missing authority slashes or backslashes, are rejected instead.