///|
pub fn Path::segment_count(self : Path) -> Int {
  self.segments.length()
}

///|
pub fn Path::is_definite(self : Path) -> Bool {
  for segment in self.segments {
    match segment {
      Member(_) | Element(_) => ()
      _ => return false
    }
  }
  true
}

///|
pub fn Path::explain(self : Path) -> Array[String] {
  self.segments.map(describe_path_segment)
}

///|
fn describe_path_segment(segment : PathSegment) -> String {
  match segment {
    Member(key) => "member \{key}"
    Element(index) => "element \{index}"
    Wildcard => "wildcard"
    RecursiveMember(key) => "recursive member \{key}"
    Slice(start, end, step) =>
      "slice \{format_optional_int(start)}:\{format_optional_int(end)}\{format_optional_step(step)}"
    Filter(filter) => "filter \{format_filter_for_explain(filter)}"
    Union(items) => "union \{format_union_for_explain(items)}"
  }
}

///|
fn format_optional_int(value : Int?) -> String {
  match value {
    Some(value) => value.to_string()
    None => ""
  }
}

///|
fn format_optional_step(value : Int?) -> String {
  match value {
    Some(value) => ":\{value}"
    None => ""
  }
}

///|
fn format_union_for_explain(items : Array[PathSegment]) -> String {
  let out = StringBuilder::new()
  out.write_char('[')
  for index, item in items {
    if index > 0 {
      out.write_char(',')
    }
    match item {
      Member(key) => write_quoted_member(out, key)
      Element(value) => out.write_object(value)
      _ => {
        let nested = describe_path_segment(item)
        out.write_string(nested)
      }
    }
  }
  out.write_char(']')
  out.to_string()
}

///|
fn format_filter_for_explain(filter : FilterExpr) -> String {
  let out = StringBuilder::new()
  write_filter_expr(out, filter)
  out.to_string()
}