sol_chainsaw/
api.rs

1use std::collections::{HashMap, HashSet};
2
3use crate::{
4    errors::{ChainsawError, ChainsawResult},
5    IdlProvider,
6};
7
8#[cfg(feature = "json")]
9mod json_uses {
10    pub use crate::json::{JsonAccountsDeserializer, JsonSerializationOpts};
11    pub use std::fmt::Write;
12}
13#[cfg(feature = "json")]
14use json_uses::*;
15
16#[cfg(feature = "bson")]
17mod bson_uses {
18    pub use crate::bson::{BsonAccountsDeserializer, BsonSerializationOpts};
19    pub use bson::raw::RawBson;
20}
21#[cfg(feature = "bson")]
22use bson_uses::*;
23
24/// Setup to  deserialize accounts for a given program. The accounts are expected to have been
25/// serialized using the [borsh] format.
26///
27/// Uses deserializers defined inside [deserializer] modules under the hood in order to resolve the
28/// appropriate [borsh] deserializers for each field.
29pub struct ChainsawDeserializer<'opts> {
30    #[cfg(feature = "json")]
31    /// The deserializers for accounts of for each program
32    json_account_deserializers:
33        HashMap<String, JsonAccountsDeserializer<'opts>>,
34
35    /// The [JsonSerializationOpts] specifying how specific data types should be deserialized.
36    #[cfg(feature = "json")]
37    json_serialization_opts: &'opts JsonSerializationOpts,
38
39    #[cfg(feature = "bson")]
40    /// The deserializers for accounts of for each program
41    bson_account_deserializers:
42        HashMap<String, BsonAccountsDeserializer<'opts>>,
43    ///
44    /// The [BsonSerializationOpts] specifying how specific data types should be deserialized.
45    #[cfg(feature = "bson")]
46    bson_serialization_opts: &'opts BsonSerializationOpts,
47}
48
49impl<'opts> ChainsawDeserializer<'opts> {
50    /// Creates an instance of a [ChainsawDeserializer].
51    /// Make sure to use [ChainsawDeserializer::add_idl_json] for each program _before_ attempting
52    /// to deserialize accounts for it.
53    ///
54    /// - [serialization_opts] specifying how specific data types should be deserialized.
55    pub fn new(
56        #[cfg(feature = "json")]
57        json_serialization_opts: &'opts JsonSerializationOpts,
58        #[cfg(feature = "bson")]
59        bson_serialization_opts: &'opts BsonSerializationOpts,
60    ) -> Self {
61        Self {
62            #[cfg(feature = "json")]
63            json_account_deserializers: HashMap::new(),
64            #[cfg(feature = "json")]
65            json_serialization_opts,
66
67            #[cfg(feature = "bson")]
68            bson_account_deserializers: HashMap::new(),
69            #[cfg(feature = "bson")]
70            bson_serialization_opts,
71        }
72    }
73
74    /// Parses an [IDL] specification from the provided [idl_json] for the [program_id] and adds a
75    /// to the json/bson accounts deserializer derived from it.
76    pub fn add_idl_json(
77        &mut self,
78        program_id: String,
79        idl_json: &str,
80        provider: IdlProvider,
81    ) -> ChainsawResult<()> {
82        #[cfg(feature = "json")]
83        {
84            #[cfg(feature = "bson")]
85            let (provider, program_id) = (provider.clone(), program_id.clone());
86
87            let json_deserializer = JsonAccountsDeserializer::try_from_idl(
88                idl_json,
89                provider,
90                self.json_serialization_opts,
91            )?;
92            self.json_account_deserializers
93                .insert(program_id, json_deserializer);
94        }
95        #[cfg(feature = "bson")]
96        {
97            let bson_deserializer = BsonAccountsDeserializer::try_from_idl(
98                idl_json,
99                provider,
100                self.bson_serialization_opts,
101            )?;
102            self.bson_account_deserializers
103                .insert(program_id, bson_deserializer);
104        }
105        Ok(())
106    }
107
108    #[cfg(any(feature = "json", feature = "bson"))]
109    pub fn account_name(
110        &self,
111        program_id: &str,
112        discriminator: &crate::DiscriminatorBytes,
113    ) -> Option<&str> {
114        #[cfg(feature = "json")]
115        {
116            self.json_account_deserializers
117                .get(program_id)
118                .unwrap()
119                .account_name(discriminator)
120        }
121        #[cfg(all(feature = "bson", not(feature = "json")))]
122        {
123            self.bson_account_deserializers
124                .get(program_id)
125                .unwrap()
126                .account_name(discriminator)
127        }
128    }
129
130    /// Returns `true` if the IDL of the given [program_id] has been added to the deserializer.
131    #[cfg(any(feature = "json", feature = "bson"))]
132    pub fn has_idl(&self, program_id: &str) -> bool {
133        #[cfg(feature = "json")]
134        {
135            self.json_account_deserializers.contains_key(program_id)
136        }
137        #[cfg(all(feature = "bson", not(feature = "json")))]
138        {
139            self.bson_account_deserializers.contains_key(program_id)
140        }
141    }
142
143    /// Returns all program ids for which IDLs have been added to the deserializer.
144    #[cfg(any(feature = "json", feature = "bson"))]
145    pub fn added_idls(&self) -> HashSet<String> {
146        #[cfg(feature = "json")]
147        {
148            self.json_account_deserializers.keys().cloned().collect()
149        }
150        #[cfg(all(feature = "bson", not(feature = "json")))]
151        {
152            self.bson_account_deserializers.keys().cloned().collect()
153        }
154    }
155
156    /// Deserializes an account to a JSON string.
157    ///
158    /// In order to specify a custom [Write] writer, i.e. a socket connection to write to, use
159    /// [deserialize_account] instead.
160    ///
161    /// - [program_id] is the program id of progra that owns the account.
162    ///   make sure to add it's IDL before via [ChainsawDeserializer::add_idl_json].
163    /// - [account_data] is the raw account data as a byte array
164    #[cfg(feature = "json")]
165    pub fn deserialize_account_to_json_string(
166        &self,
167        program_id: &str,
168        account_data: &mut &[u8],
169    ) -> ChainsawResult<String> {
170        let mut f = String::new();
171        self.deserialize_account_to_json(program_id, account_data, &mut f)?;
172        Ok(f)
173    }
174
175    /// Deserializes an account and writes the resulting JSON to the provided [Write] write [f].
176    ///
177    /// - [program_id] is the program id of progra that owns the account.
178    ///   make sure to add it's IDL before via [ChainsawDeserializer::add_idl_json].
179    /// - [account_data] is the raw account data as a byte array
180    /// - [f] is the [Write] writer to write the resulting JSON to, i.e. `std::io::stdout()` or
181    /// `String::new()`
182    #[cfg(feature = "json")]
183    pub fn deserialize_account_to_json<W: Write>(
184        &self,
185        program_id: &str,
186        account_data: &mut &[u8],
187        f: &mut W,
188    ) -> ChainsawResult<()> {
189        let deserializer = self
190            .json_account_deserializers
191            .get(program_id)
192            .ok_or_else(|| {
193                ChainsawError::CannotFindAccountDeserializerForProgramId(
194                    program_id.to_string(),
195                )
196            })?;
197
198        deserializer.deserialize_account_data(account_data, f)?;
199        Ok(())
200    }
201
202    /// Deserializes an account and writes it to raw bson which it returns.
203    ///
204    /// - [program_id] is the program id of progra that owns the account.
205    ///   make sure to add it's IDL before via [ChainsawDeserializer::add_idl_json].
206    /// - [account_data] is the raw account data as a byte array
207    #[cfg(feature = "bson")]
208    pub fn deserialize_account_to_bson(
209        &self,
210        program_id: &str,
211        account_data: &mut &[u8],
212    ) -> ChainsawResult<RawBson> {
213        let deserializer = self
214            .bson_account_deserializers
215            .get(program_id)
216            .ok_or_else(|| {
217                ChainsawError::CannotFindAccountDeserializerForProgramId(
218                    program_id.to_string(),
219                )
220            })?;
221
222        deserializer.deserialize_account_data(account_data)
223    }
224}