uri

    RFC3986 compliant URI parsing library for MoonBit

    uri
    url
    rfc3986
    parsing
    web
    Download zip
    Author
    Version
    0.1.1
    License
    Apache-2.0
    Last updated
    10 days ago
    Downloads
    48

    #MoonBit URI Library

    A comprehensive RFC3986 compliant URI parsing and manipulation library for MoonBit, ported from the OCaml ocaml-uri library with enhanced features and extensive testing.

    #Features

    • RFC3986 Compliant: Full compliance with the URI specification
    • Comprehensive Parsing: Parse all URI components (scheme, authority, path, query, fragment)
    • Error Handling: Robust error handling with detailed error types
    • URI Manipulation: Builder pattern for creating and modifying URIs
    • Normalization: URI normalization including path normalization and default port removal
    • Resolution: Resolve relative URIs against base URIs
    • Query Parsing: Parse and build query strings
    • URL Encoding: Basic URL encoding support
    • IPv6 Support: Handle IPv6 addresses in URIs
    • Extensive Testing: Comprehensive test suite with edge cases

    #Installation

    Add this library to your MoonBit project by including it in your moon.mod.json:

    { "deps": { "username/uri": "^0.1.0" } }

    #Quick Start

    ///|
    test "quick_start_example" {
    // Parse a URI
    let uri = @uri.parse("https://example.com:8080/path?query=value#fragment")
    debug_inspect(uri.scheme(), content="Some(\"https\")")
    debug_inspect(uri.host(), content="Some(\"example.com\")")
    debug_inspect(uri.port(), content="Some(8080)")
    inspect(uri.path(), content="/path")
    debug_inspect(uri.query(), content="Some(\"query=value\")")
    debug_inspect(uri.fragment(), content="Some(\"fragment\")")

    // Build a URI using the builder methods
    let built_uri = @uri.empty()
    .with_scheme(Some("https"))
    .with_host(Some("api.example.com"))
    .with_path("/v1/users")
    .with_query(Some("limit=10&offset=0"))
    inspect(
    built_uri.to_string(),
    content="https://api.example.com/v1/users?limit=10&offset=0",
    )
    }

    #API Reference

    #Core Types

    #Uri

    The main URI data structure containing all URI components:
    • scheme: String? - The URI scheme (e.g., "http", "https")
    • authority: Authority? - The authority component
    • path: String - The path component
    • query: String? - The query string
    • fragment: String? - The fragment identifier

    #Authority

    The authority component of a URI:
    • userinfo: String? - User information (username:password)
    • host: String - The host (domain name or IP address)
    • port: Int? - The port number

    #UriError

    Error types for URI operations:
    • InvalidScheme(String) - Invalid scheme format
    • InvalidAuthority(String) - Invalid authority format
    • InvalidPath(String) - Invalid path format
    • InvalidQuery(String) - Invalid query format
    • InvalidFragment(String) - Invalid fragment format
    • InvalidPort(String) - Invalid port number
    • EmptyUri - Empty URI string

    #Parsing and Serialization

    #of_string(uri_str: String) -> Result[Uri, UriError]

    Parse a URI string into a Uri structure.

    ///|
    test "of_string_example" {
    let uri = @uri.parse("https://example.com/path")
    debug_inspect(uri.host(), content="Some(\"example.com\")")
    }

    #Uri::to_string(self: Uri) -> String

    Convert a Uri structure back to a string representation.

    ///|
    test "to_string_example" {
    let uri = @uri.parse("https://example.com/path")
    let uri_string = uri.to_string()
    inspect(uri_string, content="https://example.com/path")
    }

    #Component Accessors

    #scheme(uri: Uri) -> String?

    Get the scheme component.

    #host(uri: Uri) -> String?

    Get the host component.

    #port(uri: Uri) -> Int?

    Get the port component.

    #path(uri: Uri) -> String

    Get the path component.

    #query(uri: Uri) -> String?

    Get the query component.

    #fragment(uri: Uri) -> String?

    Get the fragment component.

    #URI Construction and Modification

    #empty() -> Uri

    Create an empty URI with default values.

    #with_scheme(uri: Uri, scheme: String?) -> Uri

    Create a new URI with the specified scheme.

    #with_host(uri: Uri, host: String?) -> Uri

    Create a new URI with the specified host.

    #with_port(uri: Uri, port: Int?) -> Uri

    Create a new URI with the specified port.

    #with_path(uri: Uri, path: String) -> Uri

    Create a new URI with the specified path.

    #with_query(uri: Uri, query: String?) -> Uri

    Create a new URI with the specified query.

    #with_fragment(uri: Uri, fragment: String?) -> Uri

    Create a new URI with the specified fragment.

    #URI Analysis

    #is_absolute(uri: Uri) -> Bool

    Check if the URI is absolute (has a scheme).

    #is_relative(uri: Uri) -> Bool

    Check if the URI is relative (no scheme).

    #effective_port(uri: Uri) -> Int?

    Get the effective port (explicit port or default port for scheme).

    #URI Utilities

    #normalize(uri: Uri) -> Uri

    Normalize a URI by removing default ports and normalizing the path.

    ///|
    test "normalize_example" {
    let uri = @uri.parse("https://example.com:443/path")
    let normalized = uri.normalize()
    // Default HTTPS port (443) should be removed
    debug_inspect(normalized.port(), content="None")
    debug_inspect(normalized.host(), content="Some(\"example.com\")")
    }

    #resolve(base: Uri, relative: Uri) -> Result[Uri, UriError]

    Resolve a relative URI against a base URI.

    ///|
    test "resolve_example" {
    let base = @uri.parse("https://example.com/dir/")
    let relative = @uri.parse("../other/file.html")
    let resolved = @uri.resolve(base, relative)
    inspect(resolved.to_string(), content="https://example.com/other/file.html")
    }

    #Examples

    #Basic URI Parsing

    ///|
    test "parse_http_uri" {
    let uri = @uri.parse(
    "https://user:pass@example.com:8080/path?query=value#section",
    )
    debug_inspect(uri.scheme(), content="Some(\"https\")")
    debug_inspect(uri.host(), content="Some(\"example.com\")")
    debug_inspect(uri.port(), content="Some(8080)")
    inspect(uri.path(), content="/path")
    debug_inspect(uri.query(), content="Some(\"query=value\")")
    debug_inspect(uri.fragment(), content="Some(\"section\")")
    }

    #Building URIs

    ///|
    test "build_api_uri" {
    let api_uri = @uri.empty()
    .with_scheme(Some("https"))
    .with_host(Some("api.github.com"))
    .with_path("/repos/owner/repo/issues")
    .with_query(Some("state=open&per_page=50"))

    inspect(
    api_uri.to_string(),
    content="https://api.github.com/repos/owner/repo/issues?state=open&per_page=50",
    )
    }

    #URI Resolution

    ///|
    test "resolve_relative_uri" {
    let base = @uri.parse("https://example.com/docs/guide/")
    let relative = @uri.parse("../api/reference.html")

    let resolved = @uri.resolve(base, relative)
    inspect(
    resolved.to_string(),
    content="https://example.com/docs/api/reference.html",
    )
    }

    #Query Parameter Handling

    ///|
    test "query_parameters" {
    let uri = @uri.parse("https://search.example.com/?q=moonbit&lang=en&safe=on")

    match uri.query() {
    Some(query_str) => {
    inspect(query_str, content="q=moonbit&lang=en&safe=on")

    // Use built-in query parameter methods
    let q_param = uri.get_query_param("q")
    debug_inspect(q_param, content="Some(\"moonbit\")")

    let lang_param = uri.get_query_param("lang")
    debug_inspect(lang_param, content="Some(\"en\")")
    }
    None => inspect("Should have query", content="\"Should have query\"")
    }
    }

    #IPv6 Support

    ///|
    test "ipv6_uri" {
    let uri = @uri.parse("http://[2001:db8::1]:8080/path")
    debug_inspect(uri.scheme(), content="Some(\"http\")")
    debug_inspect(uri.host(), content="Some(\"[2001:db8::1]\")")
    debug_inspect(uri.port(), content="Some(8080)")
    inspect(uri.path(), content="/path")
    }

    #URI Normalization

    ///|
    test "uri_normalization" {
    let uri = @uri.parse("https://example.com:443/./path/../other/./file.html")
    let normalized = uri.normalize()

    // Default HTTPS port (443) should be removed
    debug_inspect(normalized.port(), content="None")
    // Path should be normalized
    inspect(normalized.path(), content="/other/file.html")

    inspect(normalized.to_string(), content="https://example.com/other/file.html")
    }

    #Supported URI Schemes

    The library recognizes default ports for common schemes:

    • http → 80
    • https → 443
    • ftp → 21
    • ssh → 22
    • telnet → 23
    • smtp → 25
    • dns → 53
    • pop3 → 110
    • imap → 143
    • ldap → 389
    • imaps → 993
    • pop3s → 995

    #Error Handling

    The library uses MoonBit's Result type for error handling. All parsing operations return Result[Uri, UriError] where UriError provides detailed information about parsing failures:

    ///|
    test "error_handling_example" {
    // Test with a valid but unusual URI
    let uri = @uri.parse("custom://example.com")
    debug_inspect(uri.scheme(), content="Some(\"custom\")")
    debug_inspect(uri.host(), content="Some(\"example.com\")")
    }

    #RFC3986 Compliance

    This library implements the URI specification as defined in RFC3986. Key compliance features include:

    • Proper parsing of all URI components
    • Scheme validation (must start with letter, contain only alphanumeric, +, -, .)
    • Authority parsing with IPv6 support
    • Path normalization (resolving . and .. segments)
    • Query and fragment handling
    • Percent-encoding support
    • Relative URI resolution

    #Testing

    The library includes comprehensive tests covering:

    • Basic URI parsing and serialization
    • All URI components (scheme, authority, path, query, fragment)
    • Edge cases (empty components, special characters)
    • IPv6 addresses
    • URI normalization
    • Relative URI resolution
    • Query parameter parsing
    • Error conditions
    • RFC3986 compliance

    Run tests with:

    moon test

    #Contributing

    Contributions are welcome! Please ensure that:

    1. All tests pass
    2. New features include comprehensive tests
    3. Code follows MoonBit style guidelines
    4. Documentation is updated for new features

    #License

    This project is licensed under the Apache-2.0 License - see the LICENSE file for details.

    #Acknowledgments

    This library is ported from the excellent OCaml URI library by the Mirage team, with enhancements for MoonBit's type system and additional utility functions.

    UriError

    pub suberror UriError {
    InvalidScheme(String)
    InvalidAuthority(String)
    InvalidPath(String)
    InvalidQuery(String)
    InvalidFragment(String)
    InvalidPort(String)
    EmptyUri
    } derive(ToJson)

    Result type for URI parsing operations

    UriError::to_json

    fn UriError::to_json(UriError) -> Json

    Keep err.to_json() available in dot form for existing users.

    Authority

    pub struct Authority {
    userinfo : String?
    host : String
    port : Int?
    } derive(ToJson)

    Authority component of a URI Contains optional user info, required host, and optional port

    Authority::to_json

    fn Authority::to_json(Authority) -> Json

    Keep authority.to_json() available in dot form for existing users.

    Uri

    pub struct Uri {
    scheme : String?
    authority : Authority?
    path : String
    query : String?
    fragment : String?
    } derive(ToJson)

    MoonBit URI Library

    A comprehensive RFC3986-compliant URI parsing and manipulation library for MoonBit. This library provides robust URI parsing, validation, and manipulation capabilities with full support for all URI components and operations.

    Features

    • RFC3986 Compliant: Full compliance with URI specification
    • Immutable Operations: All URI modifications return new instances
    • Comprehensive Parsing: Support for all URI components
    • Percent Encoding: Built-in encoding/decoding utilities
    • Query Parameter Helpers: Easy query string manipulation
    • Path Segment Operations: Convenient path manipulation
    • URI Resolution: Resolve relative URIs against base URIs
    • Validation: Robust error handling and validation

    Quick Start

    // Parse a URI
    let uri = @uri.parse("https://user:pass@example.com:8080/path?query=value#fragment")

    // Access components
    inspect(uri.scheme(), content="Some(\"https\")")
    inspect(uri.host(), content="Some(\"example.com\")")
    inspect(uri.port(), content="Some(8080)")
    inspect(uri.path(), content="/path")

    // Modify URI (immutable)
    let new_uri = uri.with_host(Some("newhost.com")).with_port(Some(9000))
    inspect(new_uri.to_string(), content="https://user:pass@newhost.com:9000/path?query=value#fragment")

    // Query parameter helpers
    let search_uri = @uri.parse("https://example.com/search")
    let with_params = search_uri.with_query_param("q", "moonbit").with_query_param("lang", "en")
    inspect(with_params.to_string(), content="https://example.com/search?q=moonbit&lang=en")

    URI Structure

    URI data structure representing a parsed URI according to RFC3986

    A URI has the general form: scheme://authority/path?query#fragment

    Where:
    • scheme: identifies the protocol (e.g., "http", "https", "ftp")
    • authority: contains user info, host, and port
    • path: hierarchical path to resource
    • query: additional parameters
    • fragment: reference to a secondary resource
    impl Show for Uri

    Uri::build_query

    fn Uri::build_query(pairs : Array[(String, String)]) -> String

    Build query string from key-value pairs

    Example

    let query = @uri.Uri::build_query([("name", "John Doe"), ("age", "30")])
    inspect(query, content="name=John%20Doe&age=30")

    Parameters

    • pairs: Array of key-value pairs

    Returns

    Encoded query string

    Uri::decode

    fn Uri::decode(input : String) -> String

    URL decode a percent-encoded string according to RFC3986.

    Decodes %XX sequences back to their original characters. Invalid percent sequences are left as-is for robustness.

    Examples

    // Basic decoding
    let decoded = @uri.Uri::decode("hello%20world%21")
    inspect(decoded, content="hello world!")

    // Decoding special characters
    let email = @uri.Uri::decode("user%40domain.com")
    inspect(email, content="user@domain.com")

    // Mixed encoded and unencoded content
    let mixed = @uri.Uri::decode("path%2Fto%2Fresource")
    inspect(mixed, content="path/to/resource")

    // Invalid sequences are preserved
    let invalid = @uri.Uri::decode("hello%ZZ%20world")
    inspect(invalid, content="hello%ZZ world")

    // Already decoded strings remain unchanged
    let plain = @uri.Uri::decode("hello world")
    inspect(plain, content="hello world")

    Parameters

    • input: The percent-encoded string to decode

    Returns

    The decoded string with %XX sequences converted to characters

    See Also

    • encode() - Encode strings for safe URI usage

    Uri::effective_port

    fn Uri::effective_port(self : Uri) -> Int?

    Get the effective port (explicit port or default port for scheme)

    Uri::encode

    fn Uri::encode(input : String) -> String

    URL encode a string using percent encoding according to RFC3986.

    Encodes unsafe characters as %XX where XX is the hexadecimal representation. This is essential for proper URI component handling, especially in query parameters, path segments, and other URI components that may contain special characters.

    Examples

    // Basic encoding with spaces and punctuation
    let encoded = @uri.Uri::encode("hello world!")
    inspect(encoded, content="hello%20world%21")

    // Encoding special characters
    let special = @uri.Uri::encode("user@domain.com")
    inspect(special, content="user%40domain.com")

    // Encoding query parameter values
    let query_value = @uri.Uri::encode("search term with spaces")
    inspect(query_value, content="search%20term%20with%20spaces")

    // Safe characters remain unchanged
    let safe = @uri.Uri::encode("ABCabc123-_.~")
    inspect(safe, content="ABCabc123-_.~")

    Parameters

    • input: The string to encode

    Returns

    The percent-encoded string safe for use in URI components

    See Also

    • decode() - Decode percent-encoded strings

    Uri::fragment

    fn Uri::fragment(self : Uri) -> String?

    Get the fragment component of the URI.

    The fragment identifies a secondary resource or section within the primary resource, typically used for anchors in HTML documents or specific sections.

    Examples

    // Fragment in HTML document
    let uri = @uri.parse("https://example.com/page.html#section1")
    inspect(uri.fragment(), content="Some(\"section1\")")

    // Fragment with encoded characters
    let encoded_fragment = @uri.parse("https://example.com/doc#user%20guide")
    inspect(encoded_fragment.fragment(), content="Some(\"user%20guide\")")

    // No fragment
    let no_fragment = @uri.parse("https://example.com/page.html")
    inspect(no_fragment.fragment(), content="None")

    // Empty fragment
    let empty_fragment = @uri.parse("https://example.com/page.html#")
    inspect(empty_fragment.fragment(), content="Some(\"\")")

    Returns

    The fragment as Some(String) if present, None otherwise.

    See Also

    • with_fragment() - Create new URI with different fragment

    Uri::get_query_param

    fn Uri::get_query_param(self : Uri, param_name : String) -> String?

    Get a specific query parameter value

    Uri::host

    fn Uri::host(self : Uri) -> String?

    Get the host component of the URI.

    The host identifies the server where the resource is located. It can be a domain name, IPv4 address, or IPv6 address.

    Examples

    // Domain name
    let uri = @uri.parse("https://example.com:8080/path")
    inspect(uri.host(), content="Some(\"example.com\")")

    // IPv4 address
    let ipv4_uri = @uri.parse("http://192.168.1.1/api")
    inspect(ipv4_uri.host(), content="Some(\"192.168.1.1\")")

    // IPv6 address (with brackets)
    let ipv6_uri = @uri.parse("http://[2001:db8::1]:8080/path")
    inspect(ipv6_uri.host(), content="Some(\"[2001:db8::1]\")")

    // Relative URI without host
    let relative_uri = @uri.parse("/path/to/resource")
    inspect(relative_uri.host(), content="None")

    Returns

    The host as Some(String) if present, None for relative URIs or URIs without authority.

    Uri::is_absolute

    fn Uri::is_absolute(self : Uri) -> Bool

    Check if the URI is absolute (has a scheme)

    Uri::is_relative

    fn Uri::is_relative(self : Uri) -> Bool

    Check if the URI is relative (no scheme)

    Uri::normalize

    fn Uri::normalize(self : Uri) -> Uri

    Normalize a URI (remove default ports, normalize path, etc.)

    Uri::output

    fn Uri::output(self : Uri, logger : &Logger) -> Unit

    Keep uri.output(logger) available in dot form for existing users.

    Uri::parse_query

    fn Uri::parse_query(query_string : String) -> Array[(String, String)]

    Parse query string into key-value pairs

    Example

    let params = @uri.Uri::parse_query("name=John&age=30&city=New%20York")
    inspect(params.length(), content="3")

    Parameters

    • query_string: The query string to parse

    Returns

    Array of key-value pairs

    Uri::path

    fn Uri::path(self : Uri) -> String

    Get the path component of the URI.

    The path identifies the specific resource within the host. It always starts with "/" for absolute URIs, or can be relative.

    Examples

    // Absolute path
    let uri = @uri.parse("https://example.com/path/to/resource")
    inspect(uri.path(), content="/path/to/resource")

    // Root path
    let root_uri = @uri.parse("https://example.com/")
    inspect(root_uri.path(), content="/")

    // Empty path (defaults to empty string)
    let no_path_uri = @uri.parse("https://example.com")
    inspect(no_path_uri.path(), content="")

    // Relative path
    let relative_uri = @uri.parse("path/to/resource")
    inspect(relative_uri.path(), content="path/to/resource")

    // Path with encoded characters
    let encoded_uri = @uri.parse("https://example.com/path%20with%20spaces")
    inspect(encoded_uri.path(), content="/path%20with%20spaces")

    Returns

    The path as a String. Empty string if no path is specified.

    See Also

    • path_segments() - Get path as array of segments
    • with_path() - Create new URI with different path

    Uri::path_segments

    fn Uri::path_segments(self : Uri) -> Array[String]

    Get path segments as an array

    Example

    let uri = @uri.parse("/path/to/resource")

    let _segments = uri.path_segments()
    // _segments will contain ["path", "to", "resource"]

    Returns

    Array of path segments (excluding empty segments from leading/trailing slashes)

    Uri::port

    fn Uri::port(self : Uri) -> Int?

    Get the explicit port component of the URI.

    This returns only explicitly specified ports in the URI string. Use effective_port() to get the effective port including scheme defaults.

    Examples

    // Explicit port
    let uri = @uri.parse("https://example.com:8080/path")
    inspect(uri.port(), content="Some(8080)")

    // No explicit port (uses scheme default)
    let default_uri = @uri.parse("https://example.com/path")
    inspect(default_uri.port(), content="None")

    // Different port numbers
    let custom_port = @uri.parse("http://localhost:3000/api")
    inspect(custom_port.port(), content="Some(3000)")

    // IPv6 with port
    let ipv6_uri = @uri.parse("http://[::1]:8080/path")
    inspect(ipv6_uri.port(), content="Some(8080)")

    Returns

    The port as Some(Int) if explicitly specified, None otherwise.

    See Also

    • effective_port() - Get the effective port including scheme defaults

    Uri::query

    fn Uri::query(self : Uri) -> String?

    Get the query component of the URI.

    The query string contains additional parameters for the resource, typically in the form of key=value pairs separated by "&".

    Examples

    // Query with multiple parameters
    let uri = @uri.parse("https://example.com/path?key=value&foo=bar")
    inspect(uri.query(), content="Some(\"key=value&foo=bar\")")

    // Single parameter
    let single_param = @uri.parse("https://example.com/search?q=moonbit")
    inspect(single_param.query(), content="Some(\"q=moonbit\")")

    // Empty query
    let empty_query = @uri.parse("https://example.com/path?")
    inspect(empty_query.query(), content="Some(\"\")")

    // No query
    let no_query = @uri.parse("https://example.com/path")
    inspect(no_query.query(), content="None")

    // Query with encoded characters
    let encoded_query = @uri.parse("https://example.com/search?q=hello%20world")
    inspect(encoded_query.query(), content="Some(\"q=hello%20world\")")

    Returns

    The query string as Some(String) if present, None otherwise.

    See Also

    • get_query_param() - Get specific query parameter value
    • parse_query() - Parse query string into key-value pairs

    Uri::remove_query_param

    fn Uri::remove_query_param(self : Uri, param_name : String) -> Uri

    Remove a query parameter

    Uri::scheme

    fn Uri::scheme(self : Uri) -> String?

    Get the scheme component of the URI.

    The scheme identifies the protocol used to access the resource. Common schemes include "http", "https", "ftp", "file", "mailto", etc.

    Examples

    // Absolute URI with scheme
    let uri = @uri.parse("https://example.com")
    inspect(uri.scheme(), content="Some(\"https\")")

    // Different schemes
    let ftp_uri = @uri.parse("ftp://files.example.com/file.txt")
    inspect(ftp_uri.scheme(), content="Some(\"ftp\")")

    // Relative URI without scheme
    let relative_uri = @uri.parse("/path/to/resource")
    inspect(relative_uri.scheme(), content="None")

    Returns

    The scheme as Some(String) if present, None for relative URIs.

    Uri::to_json

    fn Uri::to_json(Uri) -> Json

    Keep uri.to_json() available in dot form for existing users of this published package (the implicit promotion of impl ToJson is deprecated).

    Uri::to_string

    fn Uri::to_string(self : Uri) -> String

    Convert a Uri structure back to a string representation

    Uri::userinfo_components

    fn Uri::userinfo_components(self : Uri) -> (String, String?)?

    Parse userinfo into username and password components

    Example

    let uri = @uri.parse("https://user:pass@example.com")
    match uri.userinfo_components() {
    Some((user, Some(pass))) => {
    inspect(user, content="user")
    inspect(pass, content="pass")
    }
    _ => ()
    }

    Returns

    Optional tuple of (username, optional password)

    Uri::with_fragment

    fn Uri::with_fragment(self : Uri, new_fragment : String?) -> Uri

    Create a new URI with the specified fragment

    Uri::with_host

    fn Uri::with_host(self : Uri, new_host : String?) -> Uri

    Create a new URI with the specified host.

    Returns a new URI instance with the host component replaced. If setting a host on a URI without authority, creates a new authority. If removing the host (None), removes the entire authority.

    Examples

    // Change host
    let uri = @uri.parse("https://old-host.com/path")
    let new_uri = uri.with_host(Some("new-host.com"))
    inspect(new_uri.to_string(), content="https://new-host.com/path")

    // Add host to relative URI
    let relative = @uri.parse("/path/to/resource")
    let with_host = relative.with_host(Some("example.com"))
    inspect(with_host.to_string(), content="example.com/path/to/resource")

    // Remove host (makes URI relative)
    let absolute = @uri.parse("https://example.com/path")
    let relative_uri = absolute.with_host(None)
    inspect(relative_uri.to_string(), content="https:/path")

    Parameters

    • new_host: The new host as Some(String), or None to remove

    Returns

    A new Uri with the specified host

    Uri::with_path

    fn Uri::with_path(self : Uri, new_path : String) -> Uri

    Create a new URI with the specified path

    Uri::with_path_segments

    fn Uri::with_path_segments(self : Uri, segments : Array[String]) -> Uri

    Create a new URI with the specified path segments

    Example

    let uri = @uri.empty().with_path_segments(["api", "v1", "users"])
    inspect(uri.path(), content="/api/v1/users")

    Parameters

    • segments: Array of path segments

    Returns

    New URI with the specified path segments

    Uri::with_port

    fn Uri::with_port(self : Uri, new_port : Int?) -> Uri

    Create a new URI with the specified port

    Uri::with_query

    fn Uri::with_query(self : Uri, new_query : String?) -> Uri

    Create a new URI with the specified query

    Uri::with_query_param

    fn Uri::with_query_param(self : Uri, param_name : String, param_value : String) -> Uri

    Add or update a query parameter

    Uri::with_scheme

    fn Uri::with_scheme(self : Uri, new_scheme : String?) -> Uri

    Create a new URI with the specified scheme.

    Returns a new URI instance with the scheme component replaced. All other components remain unchanged. This is an immutable operation.

    Examples

    // Change HTTP to HTTPS
    let http_uri = @uri.parse("http://example.com/path")
    let https_uri = http_uri.with_scheme(Some("https"))
    inspect(https_uri.to_string(), content="https://example.com/path")

    // Remove scheme (make relative)
    let absolute_uri = @uri.parse("https://example.com/path")
    let relative_uri = absolute_uri.with_scheme(None)
    inspect(relative_uri.to_string(), content="example.com/path")

    // Add scheme to relative URI
    let relative = @uri.parse("/path/to/resource")
    let absolute = relative.with_scheme(Some("https"))
    inspect(absolute.to_string(), content="https:/path/to/resource")

    Parameters

    • new_scheme: The new scheme as Some(String), or None to remove

    Returns

    A new Uri with the specified scheme

    Uri::with_userinfo

    fn Uri::with_userinfo(self : Uri, username : String?, password : String?) -> Uri

    Create a new URI with the specified username and password

    Example

    let _uri = @uri.empty()
    .with_host(Some("example.com"))
    .with_userinfo(Some("user"), Some("pass"))
    // Creates URI with encoded userinfo

    Parameters

    • username: Optional username
    • password: Optional password

    Returns

    New URI with the specified userinfo

    empty

    fn empty() -> Uri

    Create an empty URI with default values.

    Example

    let uri = @uri.empty()
    inspect(uri.path(), content="")
    inspect(uri.scheme(), content="None")

    Returns

    A new Uri with all components set to their default values.

    parse

    fn parse(uri_str : String) -> Uri raise UriError

    Parse a URI string into a Uri structure according to RFC3986.

    Example

    let uri = @uri.parse("https://user:pass@example.com:8080/path?query=value#fragment")
    inspect(uri.scheme(), content="Some(\"https\")")
    inspect(uri.host(), content="Some(\"example.com\")")
    inspect(uri.port(), content="Some(8080)")

    Parameters

    • uri_str: The URI string to parse

    Returns

    A parsed Uri structure

    Raises

    • UriError::EmptyUri if the input string is empty
    • UriError::InvalidScheme if the scheme is malformed
    • UriError::InvalidAuthority if the authority is malformed
    • Other UriError variants for various parsing failures

    resolve

    fn resolve(base : Uri, relative : Uri) -> Uri raise UriError

    Resolve a relative URI against a base URI according to RFC3986.

    This function implements URI resolution as defined in RFC3986 Section 5.2. It combines a base URI with a relative URI to produce an absolute URI. If the relative URI is already absolute, it is returned unchanged.

    Examples

    // Resolve relative path
    let base = @uri.parse("https://example.com/docs/guide/")
    let relative = @uri.parse("../api/reference.html")
    let resolved = @uri.resolve(base, relative)
    inspect(resolved.to_string(), content="https://example.com/docs/api/reference.html")

    // Resolve absolute path (replaces base path)
    let base2 = @uri.parse("https://example.com/old/path")
    let absolute_path = @uri.parse("/new/path")
    let resolved2 = @uri.resolve(base2, absolute_path)
    inspect(resolved2.to_string(), content="https://example.com/new/path")

    // Absolute URI returns unchanged
    let base3 = @uri.parse("https://example.com/")
    let absolute_uri = @uri.parse("https://other.com/path")
    let resolved3 = @uri.resolve(base3, absolute_uri)
    inspect(resolved3.to_string(), content="https://other.com/path")

    Parameters

    • base: The base URI (must be absolute)
    • relative: The relative URI to resolve

    Returns

    The resolved absolute URI

    Raises

    • UriError::InvalidPath if the base URI is not absolute

    See Also

    • RFC3986 Section 5.2 - Reference Resolution