feat: URI merging and tests
This commit is contained in:
		@@ -39,6 +39,67 @@ pub fn to_string(uri: Uri) -> String {
 | 
				
			|||||||
  string.concat(parts)
 | 
					  string.concat(parts)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn merge(base: Uri, relative: Uri) -> Result(Uri, Nil) {
 | 
				
			||||||
 | 
					  use <- bool.guard(when: base.scheme == None, return: Error(Nil))
 | 
				
			||||||
 | 
					  let uri = case relative.scheme {
 | 
				
			||||||
 | 
					    Some(_) -> {
 | 
				
			||||||
 | 
					      Uri(..relative, path: remove_dot_segments(relative.path))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    None -> {
 | 
				
			||||||
 | 
					      let scheme = base.scheme
 | 
				
			||||||
 | 
					      case relative.host, relative.port, relative.userinfo {
 | 
				
			||||||
 | 
					        Some(_), _, _ | _, Some(_), _ | _, _, Some(_) -> {
 | 
				
			||||||
 | 
					          Uri(..relative, scheme:, path: remove_dot_segments(relative.path))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        _, _, _ -> {
 | 
				
			||||||
 | 
					          case relative.path {
 | 
				
			||||||
 | 
					            "" -> {
 | 
				
			||||||
 | 
					              let query = case relative.query {
 | 
				
			||||||
 | 
					                Some(_) -> relative.query
 | 
				
			||||||
 | 
					                _ -> base.query
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					              Uri(..base, query:)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            "/" <> _ -> {
 | 
				
			||||||
 | 
					              Uri(
 | 
				
			||||||
 | 
					                ..base,
 | 
				
			||||||
 | 
					                path: remove_dot_segments(relative.path),
 | 
				
			||||||
 | 
					                query: relative.query,
 | 
				
			||||||
 | 
					              )
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            _ -> {
 | 
				
			||||||
 | 
					              let path = merge_paths(base, relative)
 | 
				
			||||||
 | 
					              Uri(
 | 
				
			||||||
 | 
					                ..base,
 | 
				
			||||||
 | 
					                path: remove_dot_segments(path),
 | 
				
			||||||
 | 
					                query: relative.query,
 | 
				
			||||||
 | 
					              )
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Uri(..uri, fragment: relative.fragment) |> Ok
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn has_authority(uri: Uri) -> Bool {
 | 
				
			||||||
 | 
					  case uri.host, uri.userinfo, uri.port {
 | 
				
			||||||
 | 
					    Some(_), _, _ | _, Some(_), _ | _, _, Some(_) -> True
 | 
				
			||||||
 | 
					    _, _, _ -> False
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn merge_paths(base: Uri, relative: Uri) -> String {
 | 
				
			||||||
 | 
					  case has_authority(base), base.path {
 | 
				
			||||||
 | 
					    True, "" -> "/" <> relative.path
 | 
				
			||||||
 | 
					    _, _ -> {
 | 
				
			||||||
 | 
					      remove_segment(base.path) <> "/" <> relative.path
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn normalize(uri: Uri) -> Uri {
 | 
					pub fn normalize(uri: Uri) -> Uri {
 | 
				
			||||||
  normalise(uri)
 | 
					  normalise(uri)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -51,28 +112,28 @@ pub fn normalise(uri: Uri) -> Uri {
 | 
				
			|||||||
  let port = uri.port
 | 
					  let port = uri.port
 | 
				
			||||||
  let host =
 | 
					  let host =
 | 
				
			||||||
    uri.host |> option.map(string.lowercase) |> option.map(percent_normaliser)
 | 
					    uri.host |> option.map(string.lowercase) |> option.map(percent_normaliser)
 | 
				
			||||||
  let path = uri.path |> percent_normaliser |> normalise_path
 | 
					  let path = uri.path |> percent_normaliser |> remove_dot_segments
 | 
				
			||||||
  let query = uri.query |> option.map(percent_normaliser)
 | 
					  let query = uri.query |> option.map(percent_normaliser)
 | 
				
			||||||
  let fragment = uri.fragment |> option.map(percent_normaliser)
 | 
					  let fragment = uri.fragment |> option.map(percent_normaliser)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Uri(scheme, userinfo, host, port, path, query, fragment)
 | 
					  Uri(scheme, userinfo, host, port, path, query, fragment)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn normalise_path(path: String) -> String {
 | 
					fn remove_dot_segments(path: String) -> String {
 | 
				
			||||||
  do_normalise_path(path, "")
 | 
					  do_remove_dot_segments(path, "")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn do_normalise_path(path: String, acc: String) -> String {
 | 
					fn do_remove_dot_segments(path: String, acc: String) -> String {
 | 
				
			||||||
  case path {
 | 
					  case path {
 | 
				
			||||||
    "../" <> rest | "./" <> rest -> do_normalise_path(rest, acc)
 | 
					    "../" <> rest | "./" <> rest -> do_remove_dot_segments(rest, acc)
 | 
				
			||||||
    "/./" <> rest -> do_normalise_path("/" <> rest, acc)
 | 
					    "/./" <> rest -> do_remove_dot_segments("/" <> rest, acc)
 | 
				
			||||||
    "/." -> do_normalise_path("/", acc)
 | 
					    "/." -> acc <> "/"
 | 
				
			||||||
    "/../" <> rest -> do_normalise_path("/" <> rest, remove_segment(acc))
 | 
					    "/../" <> rest -> do_remove_dot_segments("/" <> rest, remove_segment(acc))
 | 
				
			||||||
    "/.." -> do_normalise_path("/", remove_segment(acc))
 | 
					    "/.." -> remove_segment(acc) <> "/"
 | 
				
			||||||
    "." | ".." | "" -> acc
 | 
					    "." | ".." | "" -> acc
 | 
				
			||||||
    _ -> {
 | 
					    _ -> {
 | 
				
			||||||
      let assert Ok(#(char, rest)) = string.pop_grapheme(path)
 | 
					      let assert Ok(#(char, rest)) = string.pop_grapheme(path)
 | 
				
			||||||
      do_normalise_path(rest, acc <> char)
 | 
					      do_remove_dot_segments(rest, acc <> char)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -802,6 +802,99 @@ pub fn parse_failure_tests() {
 | 
				
			|||||||
    }),
 | 
					    }),
 | 
				
			||||||
  ])
 | 
					  ])
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn merge_tests() {
 | 
				
			||||||
 | 
					  describe("merging", [
 | 
				
			||||||
 | 
					    it("relative merge", fn() {
 | 
				
			||||||
 | 
					      let uri1 = uri.parse("/relative") |> should.be_ok
 | 
				
			||||||
 | 
					      let uri2 = uri.parse("") |> should.be_ok
 | 
				
			||||||
 | 
					      uri.merge(uri1, uri2) |> should.be_error
 | 
				
			||||||
 | 
					    }),
 | 
				
			||||||
 | 
					    it("simple merge", fn() {
 | 
				
			||||||
 | 
					      let uri1 = uri.parse("http://google.com/weebl") |> should.be_ok
 | 
				
			||||||
 | 
					      let uri2 = uri.parse("http://example.com/baz") |> should.be_ok
 | 
				
			||||||
 | 
					      uri.merge(uri1, uri2) |> should.equal(uri.parse("http://example.com/baz"))
 | 
				
			||||||
 | 
					    }),
 | 
				
			||||||
 | 
					    it("segments merge", fn() {
 | 
				
			||||||
 | 
					      let uri1 = uri.parse("http://google.com/weebl") |> should.be_ok
 | 
				
			||||||
 | 
					      let uri2 =
 | 
				
			||||||
 | 
					        uri.parse("http://example.com/.././bob/../../baz") |> should.be_ok
 | 
				
			||||||
 | 
					      uri.merge(uri1, uri2) |> should.equal(uri.parse("http://example.com/baz"))
 | 
				
			||||||
 | 
					    }),
 | 
				
			||||||
 | 
					    it("base with authority merge", fn() {
 | 
				
			||||||
 | 
					      let uri1 = uri.parse("http://google.com/weebl") |> should.be_ok
 | 
				
			||||||
 | 
					      let uri2 = uri.parse("//example.com/baz") |> should.be_ok
 | 
				
			||||||
 | 
					      uri.merge(uri1, uri2) |> should.equal(uri.parse("http://example.com/baz"))
 | 
				
			||||||
 | 
					    }),
 | 
				
			||||||
 | 
					    it("base with authority segments merge", fn() {
 | 
				
			||||||
 | 
					      let uri1 = uri.parse("http://google.com/weebl") |> should.be_ok
 | 
				
			||||||
 | 
					      let uri2 =
 | 
				
			||||||
 | 
					        uri.parse("//example.com/.././bob/../../../baz") |> should.be_ok
 | 
				
			||||||
 | 
					      uri.merge(uri1, uri2) |> should.equal(uri.parse("http://example.com/baz"))
 | 
				
			||||||
 | 
					    }),
 | 
				
			||||||
 | 
					    it("base with absolute merge", fn() {
 | 
				
			||||||
 | 
					      let uri1 = uri.parse("http://google.com/weebl/eh") |> should.be_ok
 | 
				
			||||||
 | 
					      let uri2 = uri.parse("/baz") |> should.be_ok
 | 
				
			||||||
 | 
					      uri.merge(uri1, uri2) |> should.equal(uri.parse("http://google.com/baz"))
 | 
				
			||||||
 | 
					    }),
 | 
				
			||||||
 | 
					    it("base with relative merge", fn() {
 | 
				
			||||||
 | 
					      let uri1 = uri.parse("http://google.com/weebl/eh") |> should.be_ok
 | 
				
			||||||
 | 
					      let uri2 = uri.parse("baz") |> should.be_ok
 | 
				
			||||||
 | 
					      uri.merge(uri1, uri2)
 | 
				
			||||||
 | 
					      |> echo
 | 
				
			||||||
 | 
					      |> should.equal(uri.parse("http://google.com/weebl/baz"))
 | 
				
			||||||
 | 
					      let uri1 = uri.parse("http://google.com/weebl/") |> should.be_ok
 | 
				
			||||||
 | 
					      let uri2 = uri.parse("baz") |> should.be_ok
 | 
				
			||||||
 | 
					      uri.merge(uri1, uri2)
 | 
				
			||||||
 | 
					      |> echo
 | 
				
			||||||
 | 
					      |> should.equal(uri.parse("http://google.com/weebl/baz"))
 | 
				
			||||||
 | 
					      let uri1 = uri.parse("http://google.com") |> should.be_ok
 | 
				
			||||||
 | 
					      let uri2 = uri.parse("baz") |> should.be_ok
 | 
				
			||||||
 | 
					      uri.merge(uri1, uri2)
 | 
				
			||||||
 | 
					      |> echo
 | 
				
			||||||
 | 
					      |> should.equal(uri.parse("http://google.com/baz"))
 | 
				
			||||||
 | 
					    }),
 | 
				
			||||||
 | 
					    it("base with relative segments merge", fn() {
 | 
				
			||||||
 | 
					      let uri1 = uri.parse("http://google.com") |> should.be_ok
 | 
				
			||||||
 | 
					      let uri2 = uri.parse("/.././bob/../../../baz") |> should.be_ok
 | 
				
			||||||
 | 
					      uri.merge(uri1, uri2)
 | 
				
			||||||
 | 
					      |> echo
 | 
				
			||||||
 | 
					      |> should.equal(uri.parse("http://google.com/baz"))
 | 
				
			||||||
 | 
					    }),
 | 
				
			||||||
 | 
					    it("base with empty uri merge", fn() {
 | 
				
			||||||
 | 
					      let uri1 = uri.parse("http://google.com/weebl/bob") |> should.be_ok
 | 
				
			||||||
 | 
					      let uri2 = uri.parse("") |> should.be_ok
 | 
				
			||||||
 | 
					      uri.merge(uri1, uri2)
 | 
				
			||||||
 | 
					      |> echo
 | 
				
			||||||
 | 
					      |> should.equal(uri.parse("http://google.com/weebl/bob"))
 | 
				
			||||||
 | 
					    }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it("base with fragment merge", fn() {
 | 
				
			||||||
 | 
					      let uri1 = uri.parse("http://google.com/weebl/bob") |> should.be_ok
 | 
				
			||||||
 | 
					      let uri2 = uri.parse("#fragment") |> should.be_ok
 | 
				
			||||||
 | 
					      uri.merge(uri1, uri2)
 | 
				
			||||||
 | 
					      |> echo
 | 
				
			||||||
 | 
					      |> should.equal(uri.parse("http://google.com/weebl/bob#fragment"))
 | 
				
			||||||
 | 
					    }),
 | 
				
			||||||
 | 
					    it("base with query merge", fn() {
 | 
				
			||||||
 | 
					      let uri1 = uri.parse("http://google.com/weebl/bob") |> should.be_ok
 | 
				
			||||||
 | 
					      let uri2 = uri.parse("?query") |> should.be_ok
 | 
				
			||||||
 | 
					      uri.merge(uri1, uri2)
 | 
				
			||||||
 | 
					      |> echo
 | 
				
			||||||
 | 
					      |> should.equal(uri.parse("http://google.com/weebl/bob?query"))
 | 
				
			||||||
 | 
					      let uri1 = uri.parse("http://google.com/weebl/bob?query1") |> should.be_ok
 | 
				
			||||||
 | 
					      let uri2 = uri.parse("?query2") |> should.be_ok
 | 
				
			||||||
 | 
					      uri.merge(uri1, uri2)
 | 
				
			||||||
 | 
					      |> echo
 | 
				
			||||||
 | 
					      |> should.equal(uri.parse("http://google.com/weebl/bob?query2"))
 | 
				
			||||||
 | 
					      let uri1 = uri.parse("http://google.com/weebl/bob?query1") |> should.be_ok
 | 
				
			||||||
 | 
					      let uri2 = uri.parse("") |> should.be_ok
 | 
				
			||||||
 | 
					      uri.merge(uri1, uri2)
 | 
				
			||||||
 | 
					      |> echo
 | 
				
			||||||
 | 
					      |> should.equal(uri.parse("http://google.com/weebl/bob?query1"))
 | 
				
			||||||
 | 
					    }),
 | 
				
			||||||
 | 
					  ])
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
// gleeunit test functions end in `_test`
 | 
					// gleeunit test functions end in `_test`
 | 
				
			||||||
// pub fn uri_test() {
 | 
					// pub fn uri_test() {
 | 
				
			||||||
//   match("uri:")
 | 
					//   match("uri:")
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user