gear_core/message/
incoming.rs

1// This file is part of Gear.
2
3// Copyright (C) 2022-2025 Gear Technologies Inc.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19use crate::{
20    buffer::Payload,
21    ids::{ActorId, MessageId},
22    message::{
23        ContextStore, DispatchKind, GasLimit, StoredDispatch, StoredMessage, Value,
24        common::MessageDetails,
25    },
26};
27use alloc::sync::Arc;
28use core::ops::Deref;
29
30/// Incoming message.
31///
32/// Used for program execution.
33#[derive(Clone, Debug, PartialEq, Eq)]
34#[cfg_attr(any(feature = "mock", test), derive(Default))]
35pub struct IncomingMessage {
36    /// Message id.
37    id: MessageId,
38    /// Message source.
39    source: ActorId,
40    /// Message payload.
41    payload: Arc<Payload>,
42    /// Message gas limit. Required here.
43    gas_limit: GasLimit,
44    /// Message value.
45    value: Value,
46    /// Message details like reply message ID, status code, etc.
47    details: Option<MessageDetails>,
48}
49
50impl IncomingMessage {
51    /// Create new IncomingMessage.
52    pub fn new(
53        id: MessageId,
54        source: ActorId,
55        payload: Payload,
56        gas_limit: GasLimit,
57        value: Value,
58        details: Option<MessageDetails>,
59    ) -> Self {
60        Self {
61            id,
62            source,
63            gas_limit,
64            value,
65            details,
66            payload: Arc::new(payload),
67        }
68    }
69
70    /// Convert IncomingMessage into gasless StoredMessage.
71    pub fn into_stored(self, destination: ActorId) -> StoredMessage {
72        StoredMessage::new(
73            self.id,
74            self.source,
75            destination,
76            Arc::try_unwrap(self.payload).unwrap_or_else(|payload| {
77                log::error!(
78                    "IncomingMessage payload has multiple references, this is unexpected behavior"
79                );
80                Arc::unwrap_or_clone(payload)
81            }),
82            self.value,
83            self.details,
84        )
85    }
86
87    /// Message payload.
88    pub fn payload(&self) -> Arc<Payload> {
89        self.payload.clone()
90    }
91
92    /// Message id.
93    pub fn id(&self) -> MessageId {
94        self.id
95    }
96
97    /// Message source.
98    pub fn source(&self) -> ActorId {
99        self.source
100    }
101
102    /// Message gas limit.
103    pub fn gas_limit(&self) -> GasLimit {
104        self.gas_limit
105    }
106
107    /// Message value.
108    pub fn value(&self) -> Value {
109        self.value
110    }
111
112    /// Message details.
113    pub fn details(&self) -> Option<MessageDetails> {
114        self.details
115    }
116
117    /// Returns bool defining if message is error reply.
118    pub fn is_error_reply(&self) -> bool {
119        self.details.map(|d| d.is_error_reply()).unwrap_or(false)
120    }
121
122    /// Returns bool defining if message is reply.
123    pub fn is_reply(&self) -> bool {
124        self.details.map(|d| d.is_reply_details()).unwrap_or(false)
125    }
126}
127
128/// Incoming message with entry point and previous execution context, if exists.
129#[derive(Clone, Debug, PartialEq, Eq)]
130#[cfg_attr(any(feature = "mock", test), derive(Default))]
131pub struct IncomingDispatch {
132    /// Entry point.
133    kind: DispatchKind,
134    /// Incoming message.
135    message: IncomingMessage,
136    /// Previous execution context, if exists.
137    context: Option<ContextStore>,
138}
139
140impl From<IncomingDispatch> for (DispatchKind, IncomingMessage, Option<ContextStore>) {
141    fn from(dispatch: IncomingDispatch) -> (DispatchKind, IncomingMessage, Option<ContextStore>) {
142        (dispatch.kind, dispatch.message, dispatch.context)
143    }
144}
145
146impl IncomingDispatch {
147    /// Create new IncomingDispatch.
148    pub fn new(
149        kind: DispatchKind,
150        message: IncomingMessage,
151        context: Option<ContextStore>,
152    ) -> Self {
153        Self {
154            kind,
155            message,
156            context,
157        }
158    }
159
160    /// Convert IncomingDispatch into gasless StoredDispatch with updated (or recently set) context.
161    pub fn into_stored(self, destination: ActorId, context: ContextStore) -> StoredDispatch {
162        StoredDispatch::new(
163            self.kind,
164            self.message.into_stored(destination),
165            Some(context),
166        )
167    }
168
169    /// Decompose IncomingDispatch for it's components: DispatchKind, IncomingMessage and `Option<ContextStore>`.
170    pub fn into_parts(self) -> (DispatchKind, IncomingMessage, Option<ContextStore>) {
171        self.into()
172    }
173
174    /// Entry point for the message.
175    pub fn kind(&self) -> DispatchKind {
176        self.kind
177    }
178
179    /// Dispatch message reference.
180    pub fn message(&self) -> &IncomingMessage {
181        &self.message
182    }
183
184    /// Previous execution context reference, if exists.
185    pub fn context(&self) -> &Option<ContextStore> {
186        &self.context
187    }
188
189    /// Previous execution context mutable reference, if exists.
190    pub fn context_mut(&mut self) -> &mut Option<ContextStore> {
191        &mut self.context
192    }
193}
194
195impl Deref for IncomingDispatch {
196    type Target = IncomingMessage;
197
198    fn deref(&self) -> &Self::Target {
199        self.message()
200    }
201}