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}