1use alloc::vec::Vec;
2#[cfg(feature = "std")]
3use std::{collections::HashMap, sync::OnceLock};
4
5#[expect(missing_docs)]
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
10pub enum StageId {
11 #[deprecated(
12 note = "Static Files are generated outside of the pipeline and do not require a separate stage"
13 )]
14 StaticFile,
15 #[deprecated(
16 note = "MerkleChangeSets stage has been removed; kept for DB checkpoint compatibility"
17 )]
18 MerkleChangeSets,
19 Era,
20 Headers,
21 Bodies,
22 SenderRecovery,
23 Execution,
24 PruneSenderRecovery,
25 MerkleUnwind,
26 AccountHashing,
27 StorageHashing,
28 MerkleExecute,
29 TransactionLookup,
30 IndexStorageHistory,
31 IndexAccountHistory,
32 Prune,
33 Finish,
34 Other(&'static str),
36}
37
38#[cfg(feature = "std")]
42static ENCODED_STAGE_IDS: OnceLock<HashMap<StageId, Vec<u8>>> = OnceLock::new();
43
44impl StageId {
45 pub const ALL: [Self; 15] = [
47 Self::Era,
48 Self::Headers,
49 Self::Bodies,
50 Self::SenderRecovery,
51 Self::Execution,
52 Self::PruneSenderRecovery,
53 Self::MerkleUnwind,
54 Self::AccountHashing,
55 Self::StorageHashing,
56 Self::MerkleExecute,
57 Self::TransactionLookup,
58 Self::IndexStorageHistory,
59 Self::IndexAccountHistory,
60 Self::Prune,
61 Self::Finish,
62 ];
63
64 pub const STATE_REQUIRED: [Self; 9] = [
66 Self::Execution,
67 Self::PruneSenderRecovery,
68 Self::MerkleUnwind,
69 Self::AccountHashing,
70 Self::StorageHashing,
71 Self::MerkleExecute,
72 Self::IndexStorageHistory,
73 Self::IndexAccountHistory,
74 Self::Prune,
75 ];
76
77 pub const fn as_str(&self) -> &str {
79 match self {
80 #[expect(deprecated)]
81 Self::StaticFile => "StaticFile",
82 #[expect(deprecated)]
83 Self::MerkleChangeSets => "MerkleChangeSets",
84 Self::Era => "Era",
85 Self::Headers => "Headers",
86 Self::Bodies => "Bodies",
87 Self::SenderRecovery => "SenderRecovery",
88 Self::Execution => "Execution",
89 Self::PruneSenderRecovery => "PruneSenderRecovery",
90 Self::MerkleUnwind => "MerkleUnwind",
91 Self::AccountHashing => "AccountHashing",
92 Self::StorageHashing => "StorageHashing",
93 Self::MerkleExecute => "MerkleExecute",
94 Self::TransactionLookup => "TransactionLookup",
95 Self::IndexAccountHistory => "IndexAccountHistory",
96 Self::IndexStorageHistory => "IndexStorageHistory",
97 Self::Prune => "Prune",
98 Self::Finish => "Finish",
99 Self::Other(s) => s,
100 }
101 }
102
103 pub const fn is_downloading_stage(&self) -> bool {
105 matches!(self, Self::Era | Self::Headers | Self::Bodies)
106 }
107
108 pub const fn is_tx_lookup(&self) -> bool {
110 matches!(self, Self::TransactionLookup)
111 }
112
113 pub const fn is_finish(&self) -> bool {
115 matches!(self, Self::Finish)
116 }
117
118 pub fn get_pre_encoded(&self) -> Option<&Vec<u8>> {
121 #[cfg(not(feature = "std"))]
122 {
123 None
124 }
125 #[cfg(feature = "std")]
126 ENCODED_STAGE_IDS
127 .get_or_init(|| {
128 let mut map = HashMap::with_capacity(Self::ALL.len());
129 for stage_id in Self::ALL {
130 map.insert(stage_id, stage_id.to_string().into_bytes());
131 }
132 map
133 })
134 .get(self)
135 }
136}
137
138impl core::fmt::Display for StageId {
139 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
140 write!(f, "{}", self.as_str())
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[test]
149 fn stage_id_as_string() {
150 assert_eq!(StageId::Era.to_string(), "Era");
151 assert_eq!(StageId::Headers.to_string(), "Headers");
152 assert_eq!(StageId::Bodies.to_string(), "Bodies");
153 assert_eq!(StageId::SenderRecovery.to_string(), "SenderRecovery");
154 assert_eq!(StageId::Execution.to_string(), "Execution");
155 assert_eq!(StageId::MerkleUnwind.to_string(), "MerkleUnwind");
156 assert_eq!(StageId::AccountHashing.to_string(), "AccountHashing");
157 assert_eq!(StageId::StorageHashing.to_string(), "StorageHashing");
158 assert_eq!(StageId::MerkleExecute.to_string(), "MerkleExecute");
159 assert_eq!(StageId::IndexAccountHistory.to_string(), "IndexAccountHistory");
160 assert_eq!(StageId::IndexStorageHistory.to_string(), "IndexStorageHistory");
161 assert_eq!(StageId::TransactionLookup.to_string(), "TransactionLookup");
162 assert_eq!(StageId::Finish.to_string(), "Finish");
163
164 assert_eq!(StageId::Other("Foo").to_string(), "Foo");
165 }
166
167 #[test]
168 fn is_downloading_stage() {
169 assert!(StageId::Headers.is_downloading_stage());
170 assert!(StageId::Bodies.is_downloading_stage());
171 assert!(StageId::Era.is_downloading_stage());
172
173 assert!(!StageId::Execution.is_downloading_stage());
174 }
175}