///|
pub fn to_geojson_linestring(
  points : ArrayView[Point],
) -> Result[String, RouteError] {
  match require_points(points) {
    Ok(_) => ()
    Err(err) => return Err(err)
  }
  let out = StringBuilder(size_hint=points.length() * 32)
  out.write_string("{\"type\":\"LineString\",\"coordinates\":")
  write_coordinates(out, points)
  out.write_char('}')
  Ok(out.to_string())
}

///|
pub fn to_geojson_feature(
  points : ArrayView[Point],
  name? : String = "route",
) -> Result[String, RouteError] {
  match require_points(points) {
    Ok(_) => ()
    Err(err) => return Err(err)
  }
  let out = StringBuilder(size_hint=points.length() * 32 + name.length())
  out.write_string("{\"type\":\"Feature\",\"properties\":{\"name\":\"")
  out.write_string(escape_json_string(name))
  out.write_string("\",\"point_count\":")
  out.write_string(points.length().to_string())
  out.write_string("},\"geometry\":{\"type\":\"LineString\",\"coordinates\":")
  write_coordinates(out, points)
  out.write_string("}}")
  Ok(out.to_string())
}

///|
fn write_coordinates(out : StringBuilder, points : ArrayView[Point]) -> Unit {
  out.write_char('[')
  for i, point in points {
    if i > 0 {
      out.write_char(',')
    }
    out.write_char('[')
    out.write_string(point.lon.to_string())
    out.write_char(',')
    out.write_string(point.lat.to_string())
    out.write_char(']')
  }
  out.write_char(']')
}

///|
fn escape_json_string(value : String) -> String {
  let out = StringBuilder(size_hint=value.length())
  for ch in value {
    match ch {
      '"' => out.write_string("\\\"")
      '\\' => out.write_string("\\\\")
      '\n' => out.write_string("\\n")
      '\r' => out.write_string("\\r")
      '\t' => out.write_string("\\t")
      _ => out.write_char(ch)
    }
  }
  out.to_string()
}