///|
/// Nested transactions (SQLAlchemy `begin_nested`): `begin_nested` opens a `SAVEPOINT`,
/// auto-naming it and tracking its depth so callers never spell (or mis-spell) a name;
/// the returned handle finishes with `release` (keep the work) or `rollback` (discard
/// it), both terminal and idempotent, each moving the session's savepoint depth. The
/// raw `savepoint` / `rollback_to` / `release` trio is also shown, along with the
/// identifier guard that refuses a non-identifier name before it reaches the backend.
/// Depth is the session's own bookkeeping, exercised here over `@moondb.MockDriver`.
///
///     moon run examples/15-savepoints
#coverage.skip
fn main {
  try {
    let sess = @moonorm.Session::new(@moondb.MockDriver::new())
    println("depth: \{sess.savepoint_depth()}")

    let outer = sess.begin_nested()
    println(
      "opened \{outer.name()} at depth \{outer.depth()} (session depth \{sess.savepoint_depth()})",
    )
    let inner = sess.begin_nested()
    println(
      "opened \{inner.name()} at depth \{inner.depth()} (session depth \{sess.savepoint_depth()})",
    )

    // Discard the inner nested work, keep the outer.
    inner.rollback()
    println("after inner.rollback -> session depth \{sess.savepoint_depth()}")
    outer.release()
    println("after outer.release  -> session depth \{sess.savepoint_depth()}")

    // Terminal and idempotent: a second finish is a no-op.
    inner.rollback()
    println(
      "inner.rollback again is a no-op -> depth \{sess.savepoint_depth()}",
    )

    // `commit` is SQLAlchemy's spelling for "keep the nested work" — an alias for release.
    let kept = sess.begin_nested()
    kept.commit()
    println(
      "after \{kept.name()}.commit -> session depth \{sess.savepoint_depth()}",
    )

    // The raw trio names its own savepoint.
    sess.savepoint("qwq_sp") |> ignore
    sess.rollback_to("qwq_sp") |> ignore
    sess.release("qwq_sp") |> ignore
    println("raw savepoint/rollback_to/release ran")

    // A non-identifier name is refused up front (savepoint names cannot be bound).
    try {
      sess.savepoint("has space") |> ignore
      println("unexpected: bad savepoint name accepted")
    } catch {
      e => println("rejected bad savepoint name: \{e}")
    }
    sess.close()
  } catch {
    e => println("db error: " + e.to_string())
  }
}