aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/gleam/gleam_uri_native.erl53
-rw-r--r--src/gleam/uri.gleam78
2 files changed, 131 insertions, 0 deletions
diff --git a/src/gleam/gleam_uri_native.erl b/src/gleam/gleam_uri_native.erl
new file mode 100644
index 0000000..e0f9d2d
--- /dev/null
+++ b/src/gleam/gleam_uri_native.erl
@@ -0,0 +1,53 @@
+-module (gleam_uri_native).
+-export ([parse/1, to_string/1, parse_query/1, query_to_string/1]).
+
+find_key(Key, Map) ->
+ case maps:find(Key, Map) of
+ {ok, Value} ->
+ {ok, Value};
+ error ->
+ {error, nil}
+ end.
+
+parse(String) ->
+ case uri_string:parse(String) of
+ {error, _Reason, _Term} ->
+ {error, nil};
+ Map ->
+ {ok, {
+ uri,
+ find_key(scheme, Map),
+ find_key(userinfo, Map),
+ find_key(host, Map),
+ find_key(port, Map),
+ maps:get(path, Map),
+ find_key(query, Map),
+ find_key(fragment, Map)
+ }}
+ end.
+
+to_string({uri, MaybeScheme, MaybeUserinfo, MaybeHost, MaybePort, Path, MaybeQuery, MaybeFragment}) ->
+ Components = [{scheme, MaybeScheme}, {userinfo, MaybeUserinfo}, {host, MaybeHost}, {port, MaybePort}, {path, {ok, Path}}, {query, MaybeQuery}, {fragment, MaybeFragment}],
+ Map = maps:from_list([{K, V} || {K, {ok, V}} <- Components]),
+ case uri_string:recompose(Map) of
+ String when is_binary(String) ->
+ String;
+ % Return value when empty
+ [] -> <<"">>
+ end.
+
+parse_query(String) ->
+ case uri_string:dissect_query(String) of
+ {error, _Reason, _Term} ->
+ {error, nil};
+ Parts ->
+ {ok, Parts}
+ end.
+
+query_to_string(Parts) ->
+ case uri_string:compose_query(Parts, [{encoding, utf8}]) of
+ String when is_binary(String) ->
+ String;
+ % Return value when empty
+ [] -> <<"">>
+ end.
diff --git a/src/gleam/uri.gleam b/src/gleam/uri.gleam
new file mode 100644
index 0000000..84b2360
--- /dev/null
+++ b/src/gleam/uri.gleam
@@ -0,0 +1,78 @@
+//// Utilities for working with URIs
+////
+//// This module provides functions for working with URIs (for example, parsing
+//// URIs or encoding query strings). The functions in this module are implemented
+//// according to [RFC 3986](https://tools.ietf.org/html/rfc3986).
+////
+//// Query encoding (Form encoding) is defined in the w3c specification.
+//// https://www.w3.org/TR/html52/sec-forms.html#urlencoded-form-data
+
+import gleam/list
+import gleam/result.{Option}
+import gleam/string
+
+/// Type representing holding the parsed components of an URI.
+/// All components of a URI are optional, except the path.
+pub type Uri {
+ Uri(
+ scheme: Option(String),
+ userinfo: Option(String),
+ host: Option(String),
+ port: Option(Int),
+ path: String,
+ query: Option(String),
+ fragment: Option(String),
+ )
+}
+
+/// Parses a complient URI string into the `Uri` Type.
+/// If the string is not a valid URI string then an error is returned.
+///
+/// The opposite operation is `uri.to_string`
+pub external fn parse(String) -> Result(Uri, Nil) =
+ "gleam_uri_native" "parse"
+
+/// Parses an urlencoded query string into a list of key value pairs.
+/// Returns an error for invalid encoding.
+///
+/// The opposite operation is `uri.query_to_string`.
+pub external fn parse_query(
+ String,
+) -> Result(List(tuple(String, String)), Nil) =
+ "gleam_uri_native" "parse_query"
+
+/// Encode a list of key value pairs as a URI query string.
+///
+/// The opposite operation is `uri.parse_query`.
+pub external fn query_to_string(List(tuple(String, String))) -> String =
+ "gleam_uri_native" "query_to_string"
+
+fn do_path_segments(input, accumulator) {
+ case input {
+ [] -> list.reverse(accumulator)
+ [segment, ..rest] -> {
+ let accumulator = case segment, accumulator {
+ "", accumulator -> accumulator
+ ".", accumulator -> accumulator
+ "..", [] -> []
+ "..", [_, ..accumulator] -> accumulator
+ segment, accumulator -> [segment, ..accumulator]
+ }
+ do_path_segments(rest, accumulator)
+ }
+ }
+}
+
+/// Split the path section of a URI into it's constituent segments.
+///
+/// Removes empty segments and resolves dot-segments as specified in
+/// [section 5.2](https://www.ietf.org/rfc/rfc3986.html#section-5.2) of the RFC.
+pub fn path_segments(path) {
+ do_path_segments(string.split(path, "/"), [])
+}
+
+/// Encode a `Uri` value as a URI string.
+///
+/// The opposite operation is `uri.parse`.
+pub external fn to_string(Uri) -> String =
+ "gleam_uri_native" "to_string"