refactor: Renamed as gluri
This commit is contained in:
		
							
								
								
									
										282
									
								
								src/gluri.gleam
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										282
									
								
								src/gluri.gleam
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,282 @@
 | 
			
		||||
import gleam/bool
 | 
			
		||||
import gleam/int
 | 
			
		||||
import gleam/list
 | 
			
		||||
import gleam/option.{Some}
 | 
			
		||||
import gleam/string
 | 
			
		||||
import gleam/uri
 | 
			
		||||
import gluri/internal/parser
 | 
			
		||||
import gluri/internal/utils
 | 
			
		||||
import gluri/types.{type Uri, Uri}
 | 
			
		||||
 | 
			
		||||
/// Parses a string to the RFC3986 standard.
 | 
			
		||||
/// `Error` is returned if it fails parsing.
 | 
			
		||||
///
 | 
			
		||||
/// ## Examples
 | 
			
		||||
///
 | 
			
		||||
/// ```gleam
 | 
			
		||||
/// parse("https://me@host.com:9999/path?q=1#fragment")
 | 
			
		||||
/// // -> Ok(
 | 
			
		||||
/// //   Uri(
 | 
			
		||||
/// //     scheme: Some("https"),
 | 
			
		||||
/// //     userinfo: Some("me"),
 | 
			
		||||
/// //     host: Some("host.com"),
 | 
			
		||||
/// //     port: Some(9999),
 | 
			
		||||
/// //     path: "/path",
 | 
			
		||||
/// //     query: Some("q=1"),
 | 
			
		||||
/// //     fragment: Some("fragment")
 | 
			
		||||
/// //   )
 | 
			
		||||
/// // )
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
pub fn parse(uri: String) -> Result(Uri, Nil) {
 | 
			
		||||
  parser.parse(uri)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Encodes a `Uri` value as a URI string.
 | 
			
		||||
///
 | 
			
		||||
///
 | 
			
		||||
/// ## Examples
 | 
			
		||||
///
 | 
			
		||||
/// ```gleam
 | 
			
		||||
/// let uri = Uri(
 | 
			
		||||
///      scheme: Some("https"),
 | 
			
		||||
///      userinfo: Some("me"),
 | 
			
		||||
///      host: Some("host.com"),
 | 
			
		||||
///      port: Some(9999),
 | 
			
		||||
///      path: "/path",
 | 
			
		||||
///      query: Some("q=1"),
 | 
			
		||||
///      fragment: Some("fragment")
 | 
			
		||||
///    )
 | 
			
		||||
/// to_string(uri)
 | 
			
		||||
/// // -> "https://me@host.com:9999/path?q=1#fragment"
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
pub fn to_string(uri: Uri) -> String {
 | 
			
		||||
  let uri_string = case uri.scheme {
 | 
			
		||||
    Some(scheme) -> scheme <> ":"
 | 
			
		||||
    _ -> ""
 | 
			
		||||
  }
 | 
			
		||||
  let uri_string = case uri.host {
 | 
			
		||||
    Some(_) -> {
 | 
			
		||||
      uri_string
 | 
			
		||||
      <> "//"
 | 
			
		||||
      <> case uri.userinfo {
 | 
			
		||||
        Some(userinfo) -> userinfo <> "@"
 | 
			
		||||
        _ -> ""
 | 
			
		||||
      }
 | 
			
		||||
      <> case uri.host {
 | 
			
		||||
        Some(host) -> host
 | 
			
		||||
        _ -> ""
 | 
			
		||||
      }
 | 
			
		||||
      <> case uri.port {
 | 
			
		||||
        Some(port) -> ":" <> int.to_string(port)
 | 
			
		||||
        _ -> ""
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    _ -> uri_string
 | 
			
		||||
  }
 | 
			
		||||
  let uri_string = uri_string <> uri.path
 | 
			
		||||
  let uri_string =
 | 
			
		||||
    uri_string
 | 
			
		||||
    <> case uri.query {
 | 
			
		||||
      Some(query) -> "?" <> query
 | 
			
		||||
      _ -> ""
 | 
			
		||||
    }
 | 
			
		||||
  let uri_string =
 | 
			
		||||
    uri_string
 | 
			
		||||
    <> case uri.fragment {
 | 
			
		||||
      Some(fragment) -> "#" <> fragment
 | 
			
		||||
      _ -> ""
 | 
			
		||||
    }
 | 
			
		||||
  uri_string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Resolves a URI with respect to the given base URI.
 | 
			
		||||
///
 | 
			
		||||
/// The base URI must be an absolute URI or this function will return an error.
 | 
			
		||||
/// The algorithm for merging uris is as described in
 | 
			
		||||
/// [RFC 3986](https://tools.ietf.org/html/rfc3986#section-5.2).
 | 
			
		||||
///
 | 
			
		||||
pub fn merge(base: Uri, relative: Uri) -> Result(Uri, Nil) {
 | 
			
		||||
  utils.merge(base, relative)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Normalises the `Uri`
 | 
			
		||||
///
 | 
			
		||||
/// This follows the normalisation process in RFC3986
 | 
			
		||||
/// - Case normalisation (scheme/host -> lowercase, percent-encoding -> uppercase)
 | 
			
		||||
/// - Percent-encoding normalisation (removal of non-necessary encoding)
 | 
			
		||||
/// - Path segement normalisation (processing of /, .. and .)
 | 
			
		||||
/// - Scheme based normalisation (removal of default ports for http/https/ftp/ws/wss, setting empty path to / for valid http(s) uri)
 | 
			
		||||
///
 | 
			
		||||
/// ## Examples
 | 
			
		||||
///
 | 
			
		||||
/// ```gleam
 | 
			
		||||
/// let uri = Uri(
 | 
			
		||||
///      scheme: Some("Https"),
 | 
			
		||||
///      userinfo: None,
 | 
			
		||||
///      host: Some("host.com"),
 | 
			
		||||
///      port: Some(443),
 | 
			
		||||
///      path: "",
 | 
			
		||||
///      query: Some("q=1"),
 | 
			
		||||
///      fragment: Some("fragment")
 | 
			
		||||
///    )
 | 
			
		||||
/// normalise(uri)
 | 
			
		||||
/// // -> "https://host.com/?q=1#fragment"
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
pub fn normalise(uri: Uri) -> Uri {
 | 
			
		||||
  utils.normalise(uri)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Determines whether 2 Uris are equivalent, i.e. denote the same endpoint
 | 
			
		||||
///
 | 
			
		||||
/// This will perform normalisation if the Uris are not exactly the same
 | 
			
		||||
///
 | 
			
		||||
/// ## Examples
 | 
			
		||||
///
 | 
			
		||||
/// ```gleam
 | 
			
		||||
/// let uri = parse("Https://host.com:443?q=1#fragment")
 | 
			
		||||
/// let uri2 = parse("https://HOST.com/?q=1#fragment")
 | 
			
		||||
/// are_equivalent(uri, uri2)
 | 
			
		||||
/// // -> True
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
pub fn are_equivalent(uri1: Uri, uri2: Uri) -> Bool {
 | 
			
		||||
  use <- bool.guard(when: uri1 == uri2, return: True)
 | 
			
		||||
 | 
			
		||||
  let uri1 = normalise(uri1)
 | 
			
		||||
  let uri2 = normalise(uri2)
 | 
			
		||||
 | 
			
		||||
  uri1 == uri2
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Converts a uri library Uri value to the Gleam stdlib Uri value
 | 
			
		||||
pub fn to_uri(uri: Uri) -> uri.Uri {
 | 
			
		||||
  uri.Uri(
 | 
			
		||||
    uri.scheme,
 | 
			
		||||
    uri.userinfo,
 | 
			
		||||
    uri.host,
 | 
			
		||||
    uri.port,
 | 
			
		||||
    uri.path,
 | 
			
		||||
    uri.query,
 | 
			
		||||
    uri.fragment,
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Converts a Gleam stdlib Uri value to a uri library Uri value
 | 
			
		||||
pub fn from_uri(uri: uri.Uri) -> Uri {
 | 
			
		||||
  Uri(
 | 
			
		||||
    uri.scheme,
 | 
			
		||||
    uri.userinfo,
 | 
			
		||||
    uri.host,
 | 
			
		||||
    uri.port,
 | 
			
		||||
    uri.path,
 | 
			
		||||
    uri.query,
 | 
			
		||||
    uri.fragment,
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Decodes a percent encoded string.
 | 
			
		||||
///
 | 
			
		||||
/// Will return an `Error` if the encoding is not valid
 | 
			
		||||
///
 | 
			
		||||
/// ## Examples
 | 
			
		||||
///
 | 
			
		||||
/// ```gleam
 | 
			
		||||
/// percent_decode("This%20is%20worth%20%E2%82%AC1+")
 | 
			
		||||
/// // -> Ok("This is worth €1+")
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
pub fn percent_decode(value: String) -> Result(String, Nil) {
 | 
			
		||||
  utils.percent_decode(value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Encodes a string into a percent encoded string.
 | 
			
		||||
///
 | 
			
		||||
/// ## Examples
 | 
			
		||||
///
 | 
			
		||||
/// ```gleam
 | 
			
		||||
/// percent_encode("This is worth €1+")
 | 
			
		||||
/// // -> "This%20is%20worth%20%E2%82%AC1+"
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
pub fn percent_encode(value: String) -> String {
 | 
			
		||||
  utils.do_percent_encode(value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Encodes a list of key/value pairs into a URI query string
 | 
			
		||||
///
 | 
			
		||||
/// Empty keys/values are encoded so would need to be filtered before
 | 
			
		||||
/// passing into this function if required
 | 
			
		||||
///
 | 
			
		||||
/// ## Examples
 | 
			
		||||
///
 | 
			
		||||
/// ```gleam
 | 
			
		||||
/// query_to_string([#("first", "1"), #("", ""), #("last", "2")])
 | 
			
		||||
/// // -> "first=1&=&last=2"
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
pub fn query_to_string(query: List(#(String, String))) -> String {
 | 
			
		||||
  list.map(query, fn(q) {
 | 
			
		||||
    [utils.do_percent_encode(q.0), "=", utils.do_percent_encode(q.1)]
 | 
			
		||||
  })
 | 
			
		||||
  |> list.intersperse(["&"])
 | 
			
		||||
  |> list.flatten
 | 
			
		||||
  |> string.concat
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Takes a query string and returns a list of key/value pairs
 | 
			
		||||
///
 | 
			
		||||
/// As this decodes the keys & values an `Error` may be returned if the
 | 
			
		||||
/// encoding is invalid
 | 
			
		||||
///
 | 
			
		||||
/// As in query_to_string entries with blank key and values are returned
 | 
			
		||||
/// Empty entries (i.e. without a = separator) are omitted as they cannot
 | 
			
		||||
/// be generated using query_to_string
 | 
			
		||||
///
 | 
			
		||||
/// ## Examples
 | 
			
		||||
///
 | 
			
		||||
/// ```gleam
 | 
			
		||||
/// parse_query("first=1&=&last=2")
 | 
			
		||||
/// // -> Ok([#("first", "1"), #("", ""), #("last", "2")])
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
pub fn parse_query(query: String) -> Result(List(#(String, String)), Nil) {
 | 
			
		||||
  parser.parse_query_parts(query)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Returns the origin of the passed URI.
 | 
			
		||||
///
 | 
			
		||||
/// Returns the origin of a uri based on
 | 
			
		||||
/// [RFC 6454](https://tools.ietf.org/html/rfc6454)
 | 
			
		||||
///
 | 
			
		||||
/// If the URI scheme is not `http` and `https`.
 | 
			
		||||
/// `Error` will be returned.
 | 
			
		||||
///
 | 
			
		||||
/// ## Examples
 | 
			
		||||
///
 | 
			
		||||
/// ```gleam
 | 
			
		||||
/// let assert Ok(uri) = parse("https://blah.com/test?this#that")
 | 
			
		||||
/// origin(uri)
 | 
			
		||||
/// // -> Ok("https://blah.com")
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
pub fn origin(uri: Uri) -> Result(String, Nil) {
 | 
			
		||||
  case
 | 
			
		||||
    uri.scheme |> option.map(string.lowercase),
 | 
			
		||||
    uri.host |> option.map(string.lowercase),
 | 
			
		||||
    utils.scheme_normalisation(uri.port, uri.scheme)
 | 
			
		||||
  {
 | 
			
		||||
    Some("http" as scheme), Some(host), port
 | 
			
		||||
    | Some("https" as scheme), Some(host), port
 | 
			
		||||
    -> {
 | 
			
		||||
      let port =
 | 
			
		||||
        port
 | 
			
		||||
        |> option.map(fn(p) { ":" <> int.to_string(p) })
 | 
			
		||||
        |> option.unwrap("")
 | 
			
		||||
      Ok(scheme <> "://" <> host <> port)
 | 
			
		||||
    }
 | 
			
		||||
    _, _, _ -> Error(Nil)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user