///|
/// URL path representation per WHATWG URL spec.
/// See: https://url.spec.whatwg.org/#url-path
/// A URL's path is either a URL path segment (opaque) or a list of URL path
/// segments (hierarchical).
enum Path {
  Opaque(String)
  Segments(Array[String])
}

///|
/// Serialize path per WHATWG URL spec.
/// See: https://url.spec.whatwg.org/#url-path-serializer
pub fn Path::to_string(self : Path) -> String {
  match self {
    Opaque(path) => path
    Segments(segments) =>
      if segments.is_empty() {
        ""
      } else {
        "/" + segments.iter().map(fn(s) { s }).join("/")
      }
  }
}

///|
pub impl Show for Path with fn output(self : Path, logger : &Logger) -> Unit {
  logger.write_string(self.to_string())
}

///|
/// Shorten a url's path per WHATWG URL spec.
/// See: https://url.spec.whatwg.org/#shorten-a-urls-path
/// To shorten a url's path:
/// 1. Assert: url does not have an opaque path.
/// 2. Let path be url's path.
/// 3. If url's scheme is "file", path's size is 1, and path[0] is a normalized
///    Windows drive letter, then return.
/// 4. Remove path's last item, if any.
fn Path::shorten(self : Path, scheme~ : String) -> Unit {
  match self {
    // 1. Assert: url does not have an opaque path
    Opaque(_) => ()
    Segments(segments) => {
      // 3. If scheme is "file" and path is a Windows drive letter, don't shorten
      if scheme == "file" && segments.length() == 1 {
        match segments.get(0) {
          Some(first) if first.length() == 2 =>
            match first {
              [letter, ':'] if letter.is_ascii_alphabetic() => return
              _ => ()
            }
          _ => ()
        }
      }
      // 4. Remove path's last item, if any
      if !segments.is_empty() {
        segments.pop() |> ignore()
      }
    }
  }
}

///|
/// Create a deep copy of the path
fn Path::clone(self : Path) -> Path {
  match self {
    Opaque(s) => Opaque(s)
    Segments(segments) => Segments(segments.copy())
  }
}