///|
/// A credential-free HTTP repository base without query or fragment.
pub struct RepositoryUrl {
  priv url : HttpUrl
} derive(Eq, Debug)

///|
pub fn RepositoryUrl::parse(text : String) -> RepositoryUrl? {
  guard HttpUrl::parse(text) is Some(url) && !text.contains("?") else {
    return None
  }
  Some({
    url: HttpUrl::parse(url.to_string().trim_end(chars="/").to_owned()).unwrap(),
  })
}

///|
pub fn RepositoryUrl::to_string(self : RepositoryUrl) -> String {
  self.url.to_string()
}

///|
pub fn RepositoryUrl::location(self : RepositoryUrl) -> Location {
  self.url.location()
}

///|
fn RepositoryUrl::endpoint(
  self : RepositoryUrl,
  suffix : String,
) -> HttpUrl raise ResourceError {
  guard HttpUrl::parse(self.to_string() + "/" + suffix) is Some(url) else {
    raise ResourceError(
      InvalidResolution,
      Provenance::new(self.location()),
      "Repository endpoint exceeds supported URL bounds",
    )
  }
  url
}

///|
fn encode_repository_segment(text : String) -> String {
  let out = StringBuilder()
  let hex = "0123456789ABCDEF"
  for byte in @utf8.encode(text) {
    let n = byte.to_int()
    if (n >= 65 && n <= 90) ||
      (n >= 97 && n <= 122) ||
      (n >= 48 && n <= 57) ||
      "-._~".contains(n.unsafe_to_char().to_string()) {
      out.write_char(n.unsafe_to_char())
    } else {
      out.write_string(
        "%" +
        hex[n / 16:n / 16 + 1].to_owned() +
        hex[n % 16:n % 16 + 1].to_owned(),
      )
    }
  }
  out.to_string()
}

///|
fn provider_json(data : AcquiredBytes) -> Json raise ResourceError {
  @json.parse(@utf8.decode(data.body)) catch {
    _ =>
      raise ResourceError(
        InvalidResolution,
        Provenance::new(data.metadata.resolved.location()).acquired(
          data.metadata,
        ),
        "Expected valid repository JSON metadata",
      )
  }
}