//! # DQL Transactions
//!
//! ```rust,no_run
//! # use dittolive_ditto::prelude::*;
//! # #[tokio::main]
//! # async fn main() {
//! # let ditto: Ditto = todo!();
//! let result: Result<_, DittoError> = ditto
//!     .store()
//!     .transaction(async |txn| {
//!         let _qr1 = txn.execute("SELECT * FROM foo").await.unwrap();
//!         let _qr2 = txn.execute("SELECT * FROM bar").await.unwrap();
//!
//!         Ok(TransactionCompletionAction::Commit)
//!     })
//!     .await;
//!
//! assert_eq!(result.unwrap(), TransactionCompletionAction::Commit);
//! # }
//! ```
//!
//! Note that it is an error to try to store the transaction outside the scope of the callback:
//! ```rust,compile_fail
//! # use dittolive_ditto::prelude::*;
//! # #[tokio::main]
//! # async fn main() {
//! # let ditto: Ditto = todo!();
//! let mut stowaway = None;
//!
//! let _result: Result<_, DittoError> = ditto
//!     .store()
//!     .transaction(async |txn| {
//!         stowaway = Some(txn);
//!         Ok(TransactionCompletionAction::Commit)
//!     })
//!     .await;
//!
//! # }
//! ```
//! # Return type
//!
//! Both [`.transaction()`][`transaction`] and
//! [`.transaction_with_options()`][`transaction_with_options`] allow the caller to choose both the
//! success and error type of the returned [`Result`]. For advice on choosing an error type, see
//! the "errors" section below.
//!
//! The return type may be any type that implements [`Any`][core::any::Any], which is every
//! `'static` type. If the type is
//! [`TransactionCompletionAction'][api::TransactionCompletionAction], then it is used to determine
//! the behaviour of the transaction:
//! - if the value is [`TransactionCompletionAction::Commit`], the transaction is committed
//! - if the value is [`TransactionCompletionAction::Rollback`], the transaction is rolled back
//! - if the value is any other type, the transaction is implicitly committed
//! - if the closure returns an error, it is implicitly rolled back
//!
//! If you want to use [`TransactionCompletionAction`], be careful to make sure that you are
//! actually returning a [`TransactionCompletionAction`], and not a type that looks similar. In
//! particular, a [`&'static TransactionCompletionAction`]  will be treated like a "normal value",
//! which could lead to the following surprising scenario:
//!
//! The type check is performed by casting converting the returned `Ok` value to a [`&dyn
//! Any`][core::any::Any] and then calling `.downcast_ref()` on it. Consider reading the docs for
//! the [`Any`][core::any::Any], as well as the docs for the [`core::any`] module to understand
//! more about how this type-check works.
//! ```rust,no_run
//! # use dittolive_ditto::prelude::*;
//! # #[tokio::main]
//! # async fn main() {
//! # let ditto: Ditto = todo!();
//! # fn something_went_wrong() -> bool { true }
//! // WARNING - this looks like it should roll back but actually commits
//! let action = ditto
//!     .store()
//!     .transaction(async |txn| {
//!         // do some stuff
//!
//!         // something bad has happened, we should rollback here
//!         if something_went_wrong() {
//!             // WARNING - this does NOT roll back
//!             return Ok::<_, DittoError>(&TransactionCompletionAction::Rollback);
//!         }
//!
//!         Ok::<_, DittoError>(&TransactionCompletionAction::Commit)
//!     })
//!     .await
//!     .unwrap();
//! # }
//! ```
//! # Errors
//!
//! Both [`transaction`] and [`transaction_with_options`] are generic over their error type. This
//! is because transactions can return [`DittoError`], but you may also want to use a custom error
//! type. For example:
//! ```rust,no_run
//! # use dittolive_ditto::prelude::*;
//! # use serde_json::json;
//! # use serde_cbor::Value;
//! # #[tokio::main]
//! # async fn main() {
//! # let ditto: Ditto = todo!();
//! #[derive(Debug, thiserror::Error)]
//! enum MyError {
//!     #[error("{0}")]
//!     Ditto(#[from] DittoError),
//!
//!     #[error("insufficient funds")]
//!     InsufficientFunds,
//! }
//!
//! ditto.store().transaction::<_, MyError>(async |txn| {
//!     let result = txn.execute((
//!         "SELECT * FROM accounts WHERE _id = :id",
//!         json!({"id": "alice_id"}),
//!     )).await?;  // DittoError potentially returned here
//!
//!     let first = result.get_item(0).unwrap().value();
//!     let Value::Integer(balance) = first.get("balance").unwrap() else {
//!         panic!();
//!     };
//!
//!     if *balance < 10 {
//!         return Err(MyError::InsufficientFunds);
//!     }
//!
//!     // ...
//!
//!     Ok(TransactionCompletionAction::Commit)
//! }).await.unwrap();
//! # }
//! ```
//!
//! The error type has to implement [`From<DittoError>`]. If you don't have your own custom error
//! type, just use [`DittoError`].
//!
//! ### Limitations
//!
//! Because Rust's `?` operator will try to convert errors using [`Into`], it can sometimes
//! struggle to infer the error type. In general, if you are using `?`, you will likely run into
//! inference errors unless you specify the return type. For example:
//! - on the call to `transaction`: `ditto.store().transaction::<_, MyError>(/* ... */)`
//! - on an `Ok` or `Err` return value: `Ok::<_, MyError>(/* ... */)`
//! - on the closure itself: `async |txn| -> Result<_, MyError> { /* ... */ }`
//!
//! See also:
//! - [`ditto.store().transaction()`][`transaction`]
//! - [`ditto.store().transaction_with_options()`][`transaction_with_options`]
//!
//! [`transaction`]: crate::prelude::Store::transaction
//! [`transaction_with_options`]: crate::prelude::Store::transaction_with_options
//! [`DittoError`]: crate::prelude::DittoError

// Separate modules for clean separation between API and implementations
//
// Some function signatures are duplicated, but it's worth it IMO for the readability
mod api;
mod impls;

pub use api::{
    CreateTransactionOptions, Transaction, TransactionCompletionAction, TransactionInfo,
};
