diff options
author | Peter Saxton <peterhsaxton@gmail.com> | 2020-06-16 09:36:14 +0100 |
---|---|---|
committer | Louis Pilfold <louis@lpil.uk> | 2020-06-16 13:27:41 +0100 |
commit | 918a9da5f6a3f6bafa107d15faff89f74c90b7e1 (patch) | |
tree | 5e0f78fe8c003ce667d5e1bf5d3eeff5ef8d78fd /src | |
parent | 6855eb99857f7cd791f9b17f9435d39f31d7ea63 (diff) | |
download | gleam_stdlib-918a9da5f6a3f6bafa107d15faff89f74c90b7e1.tar.gz gleam_stdlib-918a9da5f6a3f6bafa107d15faff89f74c90b7e1.zip |
add uri merge functionality
Diffstat (limited to 'src')
-rw-r--r-- | src/gleam/uri.gleam | 128 |
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) + } +} |