|
| 1 | +/* |
| 2 | + * Copyright (c) godot-rust; Bromeon and contributors. |
| 3 | + * This Source Code Form is subject to the terms of the Mozilla Public |
| 4 | + * License, v. 2.0. If a copy of the MPL was not distributed with this |
| 5 | + * file, You can obtain one at https://mozilla.org/MPL/2.0/. |
| 6 | + */ |
| 7 | + |
| 8 | +//! The [`ErrorToGodot`] trait for mapping `Result<T, E>` to Godot return types. |
| 9 | +//! |
| 10 | +//! Built-in strategies are in the [`strat`][super::strat] module. |
| 11 | +
|
| 12 | +use crate::meta::ToGodot; |
| 13 | + |
| 14 | +/// Defines how `Result<T, E>` returned by `#[func]` is mapped to Godot. |
| 15 | +/// |
| 16 | +/// When implemented for a type `E`, this trait enables `Result<T, E>` return types |
| 17 | +/// [through a blanket impl](../trait.ToGodot.html#impl-ToGodot-for-Result%3CT,+E%3E). |
| 18 | +/// |
| 19 | +/// # Implementing the trait |
| 20 | +/// The associated type [`Mapped`][Self::Mapped] determines what GDScript sees as the function's return type. This type |
| 21 | +/// can depend on `T` -- the ok-value type of the `Result` -- because the trait is generic over `T`. |
| 22 | +/// |
| 23 | +/// Users then override [`result_to_godot()`][Self::result_to_godot], returning a [`CallOutcome`]: |
| 24 | +/// - [`CallOutcome::Return(mapped)`][CallOutcome::Return] -- the call succeeds; pass `mapped` back to GDScript. |
| 25 | +/// - [`CallOutcome::CallFailed(msg)`][CallOutcome::CallFailed] -- an unexpected error occurred; log `msg` and fail the call. |
| 26 | +/// |
| 27 | +/// # Built-in strategies |
| 28 | +/// See the [`strat`][crate::meta::error::strat] module for all provided implementations, or for inspirations for custom error handling. |
| 29 | +/// |
| 30 | +/// # Example: typed `Array<T>` with 0 or 1 elements |
| 31 | +/// Since the trait is generic over `T`, custom implementations can require tighter bounds (such as [`Element`][crate::meta::Element]) and use |
| 32 | +/// a typed `Array<T>` as the mapped type. |
| 33 | +/// |
| 34 | +/// This example returns a 1-element array on success, or a 0-element one on error -- a poor man's `Option<T>` in GDScript. |
| 35 | +/// |
| 36 | +/// ```no_run |
| 37 | +/// # use godot::prelude::*; |
| 38 | +/// use godot::builtin::Array; |
| 39 | +/// use godot::meta::error::{CallOutcome, ErrorToGodot}; |
| 40 | +/// use godot::meta::{Element, ref_to_arg}; |
| 41 | +/// |
| 42 | +/// struct MyError(String); |
| 43 | +/// |
| 44 | +/// impl<T: Element> ErrorToGodot<T> for MyError { |
| 45 | +/// // GDScript sees Array[T] as the #[func]'s return type. |
| 46 | +/// type Mapped = Array<T>; |
| 47 | +/// |
| 48 | +/// fn result_to_godot(result: Result<&T, &Self>) -> CallOutcome<Array<T>> { |
| 49 | +/// // Construct [elem] or []. |
| 50 | +/// let array = match result { |
| 51 | +/// Ok(elem) => array![ref_to_arg(elem)], |
| 52 | +/// Err(_) => Array::new(), |
| 53 | +/// }; |
| 54 | +/// |
| 55 | +/// // We always return a value, never fail the call -> only use CallOutcome::Return. |
| 56 | +/// CallOutcome::Return(array) |
| 57 | +/// } |
| 58 | +/// } |
| 59 | +/// ``` |
| 60 | +/// |
| 61 | +/// GDScript usage: |
| 62 | +/// ```gdscript |
| 63 | +/// var result := node.some_fn() # typed Array[...] |
| 64 | +/// if result.is_empty(): |
| 65 | +/// print("Operation failed") |
| 66 | +/// else: |
| 67 | +/// var value := result.front() # typed! |
| 68 | +/// ``` |
| 69 | +pub trait ErrorToGodot<T: ToGodot>: Sized { |
| 70 | + /// The type to which `Result<T, Self>` is mapped on Godot side. |
| 71 | + type Mapped: ToGodot; |
| 72 | + |
| 73 | + /// Map a `Result<T, Self>` to a Godot return value or an unexpected-error message. |
| 74 | + fn result_to_godot(result: Result<&T, &Self>) -> CallOutcome<Self::Mapped>; |
| 75 | +} |
| 76 | + |
| 77 | +/// Outcome of mapping a `Result<T, E>` for a `#[func]` return value. |
| 78 | +/// |
| 79 | +/// Returned by [`ErrorToGodot::result_to_godot()`]. Decides how Godot handles the result of a user-defined `#[func]`. |
| 80 | +pub enum CallOutcome<R> { |
| 81 | + /// Pass this value back to GDScript; the call succeeds. |
| 82 | + Return(R), |
| 83 | + |
| 84 | + /// The call encounters an unexpected error; log provided message and perform best-effort failure handling. |
| 85 | + /// |
| 86 | + /// This either stops the calling GDScript function or results in a default value of `R` on Godot side. Rust callers using |
| 87 | + /// `Object::try_call()` always receive `Err`. For detailed Godot-side semantics and an example, see |
| 88 | + /// [`strat::Unexpected`][crate::meta::error::strat::Unexpected]. |
| 89 | + CallFailed(String), |
| 90 | +} |
| 91 | + |
| 92 | +// ---------------------------------------------------------------------------------------------------------------------------------------------- |
| 93 | +// Macro for immediately exiting function. |
| 94 | + |
| 95 | +/// Return early from a `#[func]`, creating an error value from a format string (including string literals). |
| 96 | +/// |
| 97 | +/// Same principle as [`eyre::bail!`](https://docs.rs/eyre/latest/eyre/macro.bail.html), |
| 98 | +/// [`miette::bail!`](https://docs.rs/miette/latest/miette/macro.bail.html), and |
| 99 | +/// [`anyhow::bail!`](https://docs.rs/anyhow/latest/anyhow/macro.bail.html). |
| 100 | +/// |
| 101 | +/// This macro expands to `return Err(E::from(format!(...)))`, where `E` is inferred from the function's return type. |
| 102 | +/// Accepts a string literal or a `format!`-style format string with arguments. |
| 103 | +/// |
| 104 | +/// Works with any error type `E` that implements `From<String>`, e.g. [`strat::Unexpected`][crate::meta::error::strat::Unexpected]. |
| 105 | +#[macro_export] |
| 106 | +macro_rules! func_bail { |
| 107 | + ($($arg:tt)*) => { |
| 108 | + return ::std::result::Result::Err(::std::convert::From::from( |
| 109 | + ::std::format!($($arg)*) |
| 110 | + )) |
| 111 | + }; |
| 112 | +} |
0 commit comments