Skip to main content

gear_core/message/
reply.rs

1// Copyright (C) Gear Technologies Inc.
2// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
3
4use super::common::ReplyDetails;
5use crate::{
6    buffer::Payload,
7    ids::{ActorId, MessageId, prelude::*},
8    message::{
9        Dispatch, DispatchKind, GasLimit, Message, Packet, StoredDispatch, StoredMessage, Value,
10    },
11};
12use gear_core_errors::{ErrorReplyReason, ReplyCode, SuccessReplyReason};
13
14/// Message for Reply entry point.
15/// [`ReplyMessage`] is unique because of storing [`MessageId`] from message on what it replies, and can be the only one per some message execution.
16#[derive(Clone, Debug, PartialEq, Eq)]
17pub struct ReplyMessage {
18    /// Message id.
19    id: MessageId,
20    /// Message payload.
21    payload: Payload,
22    /// Message optional gas limit.
23    gas_limit: Option<GasLimit>,
24    /// Message value.
25    value: Value,
26    /// Reply status code.
27    code: ReplyCode,
28}
29
30impl ReplyMessage {
31    /// Create ReplyMessage from ReplyPacket.
32    pub fn from_packet(id: MessageId, packet: ReplyPacket) -> Self {
33        Self {
34            id,
35            payload: packet.payload,
36            gas_limit: packet.gas_limit,
37            value: packet.value,
38            code: packet.code,
39        }
40    }
41
42    /// Create new system generated ReplyMessage.
43    pub fn system(
44        origin_msg_id: MessageId,
45        payload: Payload,
46        value: Value,
47        err: impl Into<ErrorReplyReason>,
48    ) -> Self {
49        let id = MessageId::generate_reply(origin_msg_id);
50        let packet = ReplyPacket::system(payload, value, err);
51
52        Self::from_packet(id, packet)
53    }
54
55    /// Create new auto-generated ReplyMessage.
56    pub fn auto(origin_msg_id: MessageId) -> Self {
57        let id = MessageId::generate_reply(origin_msg_id);
58        let packet = ReplyPacket::auto();
59
60        Self::from_packet(id, packet)
61    }
62
63    /// Convert ReplyMessage into Message.
64    pub fn into_message(
65        self,
66        program_id: ActorId,
67        destination: ActorId,
68        origin_msg_id: MessageId,
69    ) -> Message {
70        Message::new(
71            self.id,
72            program_id,
73            destination,
74            self.payload,
75            self.gas_limit,
76            self.value,
77            Some(ReplyDetails::new(origin_msg_id, self.code).into()),
78        )
79    }
80
81    /// Convert ReplyMessage into StoredMessage.
82    pub fn into_stored(
83        self,
84        program_id: ActorId,
85        destination: ActorId,
86        origin_msg_id: MessageId,
87    ) -> StoredMessage {
88        self.into_message(program_id, destination, origin_msg_id)
89            .into()
90    }
91
92    /// Convert ReplyMessage into Dispatch.
93    pub fn into_dispatch(
94        self,
95        source: ActorId,
96        destination: ActorId,
97        origin_msg_id: MessageId,
98    ) -> Dispatch {
99        Dispatch::new(
100            DispatchKind::Reply,
101            self.into_message(source, destination, origin_msg_id),
102        )
103    }
104
105    /// Convert ReplyMessage into StoredDispatch.
106    pub fn into_stored_dispatch(
107        self,
108        source: ActorId,
109        destination: ActorId,
110        origin_msg_id: MessageId,
111    ) -> StoredDispatch {
112        self.into_dispatch(source, destination, origin_msg_id)
113            .into()
114    }
115
116    /// Message id.
117    pub fn id(&self) -> MessageId {
118        self.id
119    }
120
121    /// Message payload bytes.
122    pub fn payload_bytes(&self) -> &[u8] {
123        &self.payload
124    }
125
126    /// Message optional gas limit.
127    pub fn gas_limit(&self) -> Option<GasLimit> {
128        self.gas_limit
129    }
130
131    /// Message value.
132    pub fn value(&self) -> Value {
133        self.value
134    }
135
136    /// Reply code of the message.
137    pub fn code(&self) -> ReplyCode {
138        self.code
139    }
140}
141
142/// Reply message packet.
143///
144/// This structure is preparation for future reply message sending. Has no message id.
145#[derive(Clone, Debug, PartialEq, Eq)]
146pub struct ReplyPacket {
147    /// Message payload.
148    payload: Payload,
149    /// Message optional gas limit.
150    gas_limit: Option<GasLimit>,
151    /// Message value.
152    value: Value,
153    /// Reply status code.
154    code: ReplyCode,
155}
156
157#[cfg(test)]
158impl Default for ReplyPacket {
159    fn default() -> Self {
160        Self::auto()
161    }
162}
163
164impl ReplyPacket {
165    /// Create new manual ReplyPacket without gas.
166    pub fn new(payload: Payload, value: Value) -> Self {
167        Self {
168            payload,
169            gas_limit: None,
170            value,
171            code: ReplyCode::Success(SuccessReplyReason::Manual),
172        }
173    }
174
175    /// Create new manual ReplyPacket with gas.
176    pub fn new_with_gas(payload: Payload, gas_limit: GasLimit, value: Value) -> Self {
177        Self {
178            payload,
179            gas_limit: Some(gas_limit),
180            value,
181            code: ReplyCode::Success(SuccessReplyReason::Manual),
182        }
183    }
184
185    /// Create new manual ReplyPacket with optional gas.
186    pub fn maybe_with_gas(payload: Payload, gas_limit: Option<GasLimit>, value: Value) -> Self {
187        match gas_limit {
188            None => Self::new(payload, value),
189            Some(gas_limit) => Self::new_with_gas(payload, gas_limit, value),
190        }
191    }
192
193    // TODO: consider using here `impl CoreError` and/or provide `AsStatusCode`
194    // trait or append such functionality to `CoreError` (issue #1083).
195    /// Create new system generated ReplyPacket.
196    pub fn system(payload: Payload, value: Value, err: impl Into<ErrorReplyReason>) -> Self {
197        Self {
198            payload,
199            gas_limit: None,
200            value,
201            code: ReplyCode::error(err),
202        }
203    }
204
205    /// Auto-generated reply after success execution.
206    pub fn auto() -> Self {
207        Self {
208            payload: Default::default(),
209            gas_limit: Some(0),
210            value: 0,
211            code: ReplyCode::Success(SuccessReplyReason::Auto),
212        }
213    }
214
215    /// Prepend payload.
216    pub(super) fn try_prepend(&mut self, mut data: Payload) -> Result<(), Payload> {
217        if data.try_extend_from_slice(self.payload_bytes()).is_err() {
218            Err(data)
219        } else {
220            self.payload = data;
221            Ok(())
222        }
223    }
224
225    /// Packet status code.
226    pub fn code(&self) -> ReplyCode {
227        self.code
228    }
229}
230
231impl Packet for ReplyPacket {
232    fn payload_bytes(&self) -> &[u8] {
233        &self.payload
234    }
235
236    fn payload_len(&self) -> u32 {
237        self.payload.len_u32()
238    }
239
240    fn gas_limit(&self) -> Option<GasLimit> {
241        self.gas_limit
242    }
243
244    fn value(&self) -> Value {
245        self.value
246    }
247
248    fn kind() -> DispatchKind {
249        DispatchKind::Reply
250    }
251}