Json lens

class val JsonLens
  """
  Composable, reusable JSON path for reading and modifying nested values.

  Define a lens by chaining key/index steps, then apply it to any document:

  ```pony
  let host_lens = JsonLens("config")("database")("host")

  // Read
  match host_lens.get(doc)
  | let host: String => env.out.print(host)
  | JsonNotFound => env.out.print("no host configured")
  end

  // Modify (returns new document with the change applied)
  match host_lens.set(doc, "newhost.example.com")
  | let updated: JsonValue => // updated doc
  | JsonNotFound => // path didn't exist
  end
  ```
  """

  let _traversal: _JsonTraversal

  new val create() =>
    """Create an identity lens (focuses on the root value)."""
    _traversal = _NoTraversal

  new val _trav(trav': _JsonTraversal) =>
    _traversal = trav'

  fun apply(key_or_index: (String | USize)): JsonLens =>
    """Compose a navigation step onto this lens."""
    let step: _JsonTraversal = match key_or_index
    | let k: String => _TravObjKey(k)
    | let i: USize => _TravArrayIndex(i)
    end
    JsonLens._trav(_traversal.compose(step))

  fun get(input: JsonValue): (JsonValue | JsonNotFound) =>
    """Apply this lens to read a value."""
    _traversal(input)

  fun set(input: JsonValue, value: JsonValue): (JsonValue | JsonNotFound) =>
    """
    Apply this lens to update a value, returning a new root.
    Returns JsonNotFound if the path doesn't exist.
    """
    _traversal.update(input, value)

  fun remove(input: JsonValue): (JsonValue | JsonNotFound) =>
    """
    Apply this lens to remove a value, returning a new root.
    Returns JsonNotFound if the path doesn't exist.
    """
    _traversal.update(input, _Delete)

  fun compose(other: JsonLens): JsonLens =>
    """Sequential composition: navigate this lens, then the other."""
    JsonLens._trav(_traversal.compose(other._traversal))

  fun or_else(alt: JsonLens): JsonLens =>
    """Choice: try this lens, fall back to alt if JsonNotFound."""
    JsonLens._trav(_traversal.or_else(alt._traversal))