Skip to main content

godot_core/meta/error/strat/
mod.rs

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//! Built-in [`ErrorToGodot`] strategies for mapping `Result<T, E>` return types of `#[func]` methods.
9//!
10//! This module is intended to be used qualified with `strat::` module: `strat::Unexpected` etc.
11//! It is re-exported in the prelude to enable this.
12//!
13//! # Overview
14//! Each type in this module implements [`ErrorToGodot`] with a different mapping strategy. Some strategies are not provided out-of-the-box
15//! but could serve as inspiration to build your own. If you find any of those useful, let us know, and we may consider adding them.
16//!
17//! | Strategy                                       | `Mapped` type                       | Ok path            | Err path                  | GDScript ergonomics                     |
18//! |------------------------------------------------|-------------------------------------|--------------------|---------------------------|-----------------------------------------|
19//! | [`strat::Unexpected`]<br>Unexpected errors     | `T`                                 | `val`              | Default or<br>failed call | Sees `T`; `?` works with any `Error`    |
20//! | `()`<br>Nil on error                           | `Variant`                           | `val.to_variant()` | `null`                    | `if val == null`                        |
21//! | [`global::Error`]<br>Godot error enum          | `global::Error`                     | `OK` constant      | `ERR_*` constant          | `val == OK`                             |
22//! | _(not provided)_<br>`Variant`                  | `Variant`                           | `val.to_variant()` | `err.to_variant()`        | Must check type/value                   |
23//! | _(not provided)_<br>Dictionary `ok`/`err`      | `Dictionary`<br>`<GString,Variant>` | `{"ok" => val}`    | `{"err" => msg}`          | `d.has("ok")`<br>`d["ok"]`              |
24//! | _(not provided)_<br>Array 0/1 elems            | `Array<T>`                          | `[val]`            | `[]`                      | `a.is_empty()`<br>`a.front()` -- typed! |
25//! | _(not provided)_<br>Custom class               | `Gd<RustResult>`                    | wrap in class      | wrap in class             | `r.is_ok()`<br>`r.unwrap()`             |
26
27mod unexpected;
28
29pub use unexpected::*;
30
31use super::{CallOutcome, ErrorToGodot};
32use crate::builtin::Variant;
33use crate::global;
34use crate::meta::ToGodot;
35#[expect(unused)] // for docs.
36use crate::meta::error::strat;
37
38// ----------------------------------------------------------------------------------------------------------------------------------------------
39// () impl: GDScript sees Variant -- nil on error, val.to_variant() on success.
40
41/// Error strategy that returns `null` on error, instead of making the call fail.
42///
43/// Use this when an absent value is a normal outcome that GDScript should handle, for example a missing save file
44/// for a new player. GDScript receives a `Variant` containing either the value or `null`.
45///
46/// Since `()` discards all error information, use `.map_err(|_| ())?` to propagate any error into it.
47///
48/// # Example
49/// ```no_run
50/// use godot::prelude::*;
51/// # #[derive(GodotClass)] #[class(init, base=Node)] struct PlayerData;
52///
53/// #[godot_api]
54/// impl PlayerData {
55///     // Returns the high score from a save file, or null if absent or unreadable.
56///     // A missing file is normal for new players -- GDScript handles null gracefully.
57///     #[func]
58///     fn load_high_score(&self, save_path: String) -> Result<i64, ()> {
59///         let text = std::fs::read_to_string(save_path).map_err(|_| ())?;
60///         text.trim().parse::<i64>().map_err(|_| ())
61///     }
62/// }
63/// ```
64///
65/// GDScript usage:
66/// ```gdscript
67/// var score = player.load_high_score("user://highscore.dat")
68/// if score == null:
69///     # New player, no save file yet.
70/// ```
71impl<T: ToGodot> ErrorToGodot<T> for () {
72    type Mapped = Variant;
73
74    fn result_to_godot(result: Result<T, Self>) -> CallOutcome<Variant> {
75        match result {
76            Ok(val) => CallOutcome::Return(val.to_variant()),
77            Err(()) => CallOutcome::Return(Variant::nil()),
78        }
79    }
80}
81
82// ----------------------------------------------------------------------------------------------------------------------------------------------
83// global::Error impl: GDScript sees the Error enum.
84//
85// Note: ok_to_mapped discards the Ok value and returns Error::OK. The typical use case is Result<(), global::Error>.
86
87impl<T: ToGodot> ErrorToGodot<T> for global::Error {
88    type Mapped = global::Error;
89
90    fn result_to_godot(result: Result<T, Self>) -> CallOutcome<global::Error> {
91        match result {
92            Ok(_) => CallOutcome::Return(global::Error::OK),
93            Err(e) => CallOutcome::Return(e),
94        }
95    }
96}