Skip to main content

solana_primitives/builder/
data.rs

1//! Instruction data builder for encoding instruction parameters
2
3use crate::types::Pubkey;
4
5/// Builder for encoding instruction data
6pub struct InstructionDataBuilder {
7    data: Vec<u8>,
8}
9
10impl InstructionDataBuilder {
11    /// Create a new instruction data builder
12    pub fn new() -> Self {
13        Self { data: Vec::new() }
14    }
15
16    /// Add a discriminant/instruction type byte
17    pub fn instruction(mut self, discriminant: u8) -> Self {
18        self.data.push(discriminant);
19        self
20    }
21
22    /// Add raw bytes
23    pub fn bytes(mut self, bytes: &[u8]) -> Self {
24        self.data.extend_from_slice(bytes);
25        self
26    }
27
28    /// Add a u8 value
29    pub fn u8(mut self, value: u8) -> Self {
30        self.data.push(value);
31        self
32    }
33
34    /// Add a u16 value (little-endian)
35    pub fn u16(mut self, value: u16) -> Self {
36        self.data.extend_from_slice(&value.to_le_bytes());
37        self
38    }
39
40    /// Add a u32 value (little-endian)
41    pub fn u32(mut self, value: u32) -> Self {
42        self.data.extend_from_slice(&value.to_le_bytes());
43        self
44    }
45
46    /// Add a u64 value (little-endian)
47    pub fn u64(mut self, value: u64) -> Self {
48        self.data.extend_from_slice(&value.to_le_bytes());
49        self
50    }
51
52    /// Add a u128 value (little-endian)
53    pub fn u128(mut self, value: u128) -> Self {
54        self.data.extend_from_slice(&value.to_le_bytes());
55        self
56    }
57
58    /// Add a i8 value
59    pub fn i8(mut self, value: i8) -> Self {
60        self.data.push(value as u8);
61        self
62    }
63
64    /// Add a i16 value (little-endian)
65    pub fn i16(mut self, value: i16) -> Self {
66        self.data.extend_from_slice(&value.to_le_bytes());
67        self
68    }
69
70    /// Add a i32 value (little-endian)
71    pub fn i32(mut self, value: i32) -> Self {
72        self.data.extend_from_slice(&value.to_le_bytes());
73        self
74    }
75
76    /// Add a i64 value (little-endian)
77    pub fn i64(mut self, value: i64) -> Self {
78        self.data.extend_from_slice(&value.to_le_bytes());
79        self
80    }
81
82    /// Add a i128 value (little-endian)
83    pub fn i128(mut self, value: i128) -> Self {
84        self.data.extend_from_slice(&value.to_le_bytes());
85        self
86    }
87
88    /// Add a bool value
89    pub fn bool(mut self, value: bool) -> Self {
90        self.data.push(value as u8);
91        self
92    }
93
94    /// Add a pubkey (32 bytes)
95    pub fn pubkey(mut self, pubkey: &Pubkey) -> Self {
96        self.data.extend_from_slice(pubkey.as_bytes());
97        self
98    }
99
100    /// Add an optional pubkey
101    pub fn option_pubkey(mut self, pubkey: Option<&Pubkey>) -> Self {
102        match pubkey {
103            Some(pk) => {
104                self.data.push(1);
105                self.data.extend_from_slice(pk.as_bytes());
106            }
107            None => {
108                self.data.push(0);
109            }
110        }
111        self
112    }
113
114    /// Add a string (with length prefix as u32)
115    pub fn string(mut self, s: &str) -> Self {
116        let bytes = s.as_bytes();
117        self.data
118            .extend_from_slice(&(bytes.len() as u32).to_le_bytes());
119        self.data.extend_from_slice(bytes);
120        self
121    }
122
123    /// Build the final data vector
124    pub fn build(self) -> Vec<u8> {
125        self.data
126    }
127}
128
129impl Default for InstructionDataBuilder {
130    fn default() -> Self {
131        Self::new()
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use super::*;
138    use crate::types::Pubkey;
139
140    #[test]
141    fn test_instruction_data_builder() {
142        let data = InstructionDataBuilder::new()
143            .instruction(3)
144            .u64(1000000)
145            .u8(6)
146            .bool(true)
147            .build();
148
149        let expected = vec![
150            3, // instruction
151            0x40, 0x42, 0x0F, 0, 0, 0, 0, 0, // u64(1000000)
152            6, // u8(6)
153            1, // bool(true)
154        ];
155
156        assert_eq!(data, expected);
157    }
158
159    #[test]
160    fn test_instruction_data_builder_with_pubkey() {
161        let pubkey = Pubkey::new([1u8; 32]);
162
163        let data = InstructionDataBuilder::new()
164            .instruction(0)
165            .pubkey(&pubkey)
166            .build();
167
168        let mut expected = vec![0];
169        expected.extend_from_slice(&[1u8; 32]);
170
171        assert_eq!(data, expected);
172    }
173
174    #[test]
175    fn test_instruction_data_builder_with_option() {
176        let pubkey = Pubkey::new([2u8; 32]);
177
178        // Test with Some value
179        let data = InstructionDataBuilder::new()
180            .instruction(1)
181            .option_pubkey(Some(&pubkey))
182            .build();
183
184        let mut expected = vec![1, 1]; // instruction, Some flag
185        expected.extend_from_slice(&[2u8; 32]);
186        assert_eq!(data, expected);
187
188        // Test with None value
189        let data_none = InstructionDataBuilder::new()
190            .instruction(1)
191            .option_pubkey(None)
192            .build();
193
194        assert_eq!(data_none, vec![1, 0]); // instruction, None flag
195    }
196
197    #[test]
198    fn test_instruction_data_builder_with_string() {
199        let data = InstructionDataBuilder::new()
200            .instruction(5)
201            .string("hello")
202            .build();
203
204        let expected = vec![
205            5, // instruction
206            5, 0, 0, 0, // string length (u32)
207            b'h', b'e', b'l', b'l', b'o', // string bytes
208        ];
209
210        assert_eq!(data, expected);
211    }
212}