aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPeter Saxton <peterhsaxton@gmail.com>2020-06-16 09:36:14 +0100
committerLouis Pilfold <louis@lpil.uk>2020-06-16 13:27:41 +0100
commit918a9da5f6a3f6bafa107d15faff89f74c90b7e1 (patch)
tree5e0f78fe8c003ce667d5e1bf5d3eeff5ef8d78fd /src
parent6855eb99857f7cd791f9b17f9435d39f31d7ea63 (diff)
downloadgleam_stdlib-918a9da5f6a3f6bafa107d15faff89f74c90b7e1.tar.gz
gleam_stdlib-918a9da5f6a3f6bafa107d15faff89f74c90b7e1.zip
add uri merge functionality
Diffstat (limited to 'src')
-rw-r--r--src/gleam/uri.gleam128
1 files changed, 122 insertions, 6 deletions
diff --git a/src/gleam/uri.gleam b/src/gleam/uri.gleam
index 9224adf..8f8d754 100644
--- a/src/gleam/uri.gleam
+++ b/src/gleam/uri.gleam
@@ -110,7 +110,7 @@ pub fn query_to_string(query: List(tuple(String, String))) -> String {
|> result.unwrap("")
}
-fn do_path_segments(input, accumulator) {
+fn do_remove_dot_segments(input, accumulator) {
case input {
[] -> list.reverse(accumulator)
[segment, ..rest] -> {
@@ -121,18 +121,22 @@ fn do_path_segments(input, accumulator) {
"..", [_, ..accumulator] -> accumulator
segment, accumulator -> [segment, ..accumulator]
}
- do_path_segments(rest, accumulator)
+ do_remove_dot_segments(rest, accumulator)
}
}
}
+fn remove_dot_segments(input) {
+ do_remove_dot_segments(input, [])
+}
+
/// 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, "/"), [])
+ remove_dot_segments(string.split(path, "/"))
}
external fn erl_to_string(Map(UriKey, Dynamic)) -> Dynamic =
@@ -165,12 +169,12 @@ pub fn to_string(uri: Uri) -> String {
|> result.unwrap("")
}
-/// Fetch the origin of a url
+/// Fetch the origin of a uri
///
-/// Return the origin of a url as defined in
+/// Return the origin of a uri as defined in
/// https://tools.ietf.org/html/rfc6454
///
-/// The supported url schemes are `http` and `https`
+/// The supported uri schemes are `http` and `https`
/// Urls without a scheme will return Error
pub fn origin(uri: Uri) -> Result(String, Nil) {
let Uri(scheme: scheme, host: host, port: port, ..) = uri
@@ -182,3 +186,115 @@ pub fn origin(uri: Uri) -> Result(String, Nil) {
_ -> Error(Nil)
}
}
+
+fn option_or(first: Option(a), second: Option(a)) -> Option(a) {
+ case first {
+ Some(_) -> first
+ None -> second
+ }
+}
+
+fn drop_last(elements) {
+ let tuple(keep, _last) = list.split(elements, list.length(elements) - 1)
+ keep
+}
+
+fn join_segments(segments) {
+ case segments {
+ [] -> ""
+ _ -> string.append("/", string.join(segments, "/"))
+ }
+}
+
+/// Resolve 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 described in [RFC 3986](https://tools.ietf.org/html/rfc3986#section-5.2)
+pub fn merge(base: Uri, relative: Uri) -> Result(Uri, Nil) {
+ case base {
+ Uri(scheme: Some(_), host: Some(_), ..) -> case relative {
+ Uri(scheme: Some(_), ..) -> {
+ let path = string.split(relative.path, "/")
+ |> remove_dot_segments()
+ |> join_segments()
+ let resolved = Uri(
+ relative.scheme,
+ None,
+ relative.host,
+ relative.port,
+ path,
+ relative.query,
+ relative.fragment,
+ )
+ Ok(resolved)
+ }
+ Uri(scheme: None, host: Some(_), ..) -> {
+ let path = string.split(relative.path, "/")
+ |> remove_dot_segments()
+ |> join_segments()
+ let resolved = Uri(
+ base.scheme,
+ None,
+ relative.host,
+ relative.port,
+ path,
+ relative.query,
+ relative.fragment,
+ )
+ Ok(resolved)
+ }
+ Uri(scheme: None, host: None, path: "", ..) -> {
+ let resolved = Uri(
+ base.scheme,
+ None,
+ base.host,
+ base.port,
+ base.path,
+ option_or(relative.query, base.query),
+ relative.fragment,
+ )
+ Ok(resolved)
+ }
+ Uri(
+ scheme: None,
+ host: None,
+ path: path,
+ ..,
+ ) -> case string.starts_with(path, "/") {
+ True -> {
+ let path = string.split(relative.path, "/")
+ |> remove_dot_segments()
+ |> join_segments()
+ let resolved = Uri(
+ base.scheme,
+ None,
+ base.host,
+ base.port,
+ path,
+ relative.query,
+ relative.fragment,
+ )
+ Ok(resolved)
+ }
+ False -> {
+ let path = string.split(base.path, "/")
+ |> drop_last()
+ |> list.append(string.split(relative.path, "/"))
+ |> remove_dot_segments()
+ |> join_segments()
+ let resolved = Uri(
+ base.scheme,
+ None,
+ base.host,
+ base.port,
+ path,
+ relative.query,
+ relative.fragment,
+ )
+ Ok(resolved)
+ }
+ }
+ }
+ _ -> Error(Nil)
+ }
+}