rhaki_cw_plus/
serde_value.rs1use {
2 crate::{
3 encdec::{self, base64_encode},
4 traits::IntoStdResult,
5 },
6 cosmwasm_schema::cw_serde,
7 cosmwasm_std::{CosmosMsg, StdError, StdResult},
8 serde::{de::DeserializeOwned, Deserialize, Serialize},
9 std::{collections::HashMap, fmt::Debug, hash::Hash},
10};
11
12pub use {
13 serde_cw_value::Value,
14 serde_json::{json, Value as StdValue},
15 serde_json_wasm::{from_str as sjw_from_str, to_string as sjw_to_string, to_vec as sjw_to_vec},
16};
17
18#[cw_serde]
19pub enum PathKey {
20 Index(u64),
21 Key(String),
22}
23
24pub fn value_to_string(value: &Value) -> StdResult<String> {
26 sjw_to_string(&value).into_std_result()
27}
28
29pub fn value_from_string(string: &str) -> StdResult<Value> {
31 serde_json_wasm::from_slice(
32 serde_json_wasm::to_vec(string)
33 .into_std_result()?
34 .as_slice(),
35 )
36 .into_std_result()
37}
38
39pub fn value_from_b64(b64_string: &str) -> StdResult<Value> {
41 value_from_string(&encdec::base64_decode_as_string(b64_string)?)
42}
43
44pub fn value_to_b64_string(value: &Value) -> StdResult<String> {
45 Ok(base64_encode(&value_to_string(value)?))
46}
47
48pub fn std_to_sjw_value(std_value: StdValue) -> StdResult<Value> {
50 sjw_from_str::<Value>(&std_value.to_string()).into_std_result()
51}
52
53pub fn value_to_comsos_msg(value: &Value) -> StdResult<CosmosMsg> {
55 value.clone().deserialize_into().into_std_result()
56}
57
58pub trait SerdeValue {
59 fn from_b64(encoded_b64: impl Into<String>) -> StdResult<Value>;
60 fn from_string(string: impl Into<String>) -> StdResult<Value>;
61 fn as_string(&self) -> StdResult<String>;
62 fn to_cosmos_msg(&self) -> StdResult<CosmosMsg>;
63 fn to_b64_encoded(&self) -> StdResult<String>;
64 fn get_value_by_path<C: DeserializeOwned>(&self, path_key: Vec<PathKey>) -> StdResult<C>;
65 fn get_array_index(&self, index: impl Into<usize>) -> StdResult<Value>;
66 fn get_map_value(&self, value: impl Into<Value> + Clone) -> StdResult<Value>;
67}
68
69impl SerdeValue for Value {
70 fn from_b64(encoded_b64: impl Into<String>) -> StdResult<Value> {
71 value_from_b64(&encoded_b64.into())
72 }
73
74 fn from_string(string: impl Into<String>) -> StdResult<Value> {
75 value_from_string(&string.into())
76 }
77
78 fn as_string(&self) -> StdResult<String> {
79 value_to_string(self)
80 }
81
82 fn to_cosmos_msg(&self) -> StdResult<CosmosMsg> {
83 value_to_comsos_msg(self)
84 }
85
86 fn to_b64_encoded(&self) -> StdResult<String> {
87 value_to_b64_string(self)
88 }
89
90 fn get_value_by_path<C: DeserializeOwned>(&self, path_key: Vec<PathKey>) -> StdResult<C> {
91 let mut value = self.clone();
92 for k in path_key {
93 match k {
94 PathKey::Index(index) => match value {
95 Value::Seq(val) => value = val[index as usize].clone(),
96 _ => panic!(),
97 },
98 PathKey::Key(key) => match value {
99 Value::Map(val) => value = val[&Value::from_string(key).unwrap()].clone(),
100 _ => panic!(),
101 },
102 }
103 }
104 serde_json_wasm::from_slice(
105 serde_json_wasm::to_vec(&value)
106 .into_std_result()?
107 .as_slice(),
108 )
109 .into_std_result()
110 }
112
113 fn get_array_index(&self, index: impl Into<usize>) -> StdResult<Value> {
114 if let Value::Seq(array) = self {
115 Ok(array[index.into()].clone())
116 } else {
117 Err(StdError::generic_err(format!(
118 "Value is not a Seq: {:?}",
119 self
120 )))
121 }
122 }
123
124 fn get_map_value(&self, value: impl Into<Value> + Clone) -> StdResult<Value> {
125 if let Value::Map(map) = self {
126 map.get(&value.clone().into())
127 .map(|val| val.clone())
128 .ok_or(StdError::generic_err(format!(
129 "map key not found: {:?}",
130 value.into()
131 )))
132 } else {
133 Err(StdError::generic_err(format!(
134 "Value is not a Map: {:?}",
135 self
136 )))
137 }
138 }
139}
140
141pub trait SerdeMapSerializer<V> {
142 fn into_json_ser_map(self) -> HashMap<String, V>;
143}
144
145impl<K, V> SerdeMapSerializer<V> for HashMap<K, V>
146where
147 K: Into<String> + Clone,
148 V: Clone,
149{
150 fn into_json_ser_map(self) -> HashMap<String, V> {
151 let mut map: HashMap<String, V> = HashMap::new();
152
153 for (k, v) in self {
154 map.insert(Into::<String>::into(k.clone()), v.clone());
155 }
156
157 map
158 }
159}
160
161#[allow(clippy::wrong_self_convention)]
162pub trait SerdeMapDeserialize<V, K: TryFrom<String>> {
163 fn from_json_ser_map(self) -> Result<HashMap<K, V>, K::Error>;
164}
165
166impl<K, V> SerdeMapDeserialize<V, K> for HashMap<String, V>
167where
168 K: TryFrom<String> + Eq + PartialEq + Hash + Debug,
169 V: Clone,
170{
171 fn from_json_ser_map(self) -> Result<HashMap<K, V>, K::Error> {
172 let mut map: HashMap<K, V> = HashMap::new();
173
174 for (k, v) in self {
175 let b = K::try_from(Into::<String>::into(k.clone()))?;
176 map.insert(b, v.clone());
177 }
178
179 Ok(map)
180 }
181}
182
183pub trait ToCwJson {
184 fn into_cw(&self) -> StdResult<Value>;
185}
186
187impl ToCwJson for StdValue {
188 fn into_cw(&self) -> StdResult<Value> {
189 std_to_sjw_value(self.clone())
190 }
191}
192
193pub trait DoubleDeserialize {
194 fn double_deserialize<'de, F: Deserialize<'de>, S: Deserialize<'de>>(
195 &self,
196 ) -> StdResult<DoubleValueDeserializeResult<F, S>>;
197}
198
199impl DoubleDeserialize for Value {
200 fn double_deserialize<'de, F: Deserialize<'de>, S: Deserialize<'de>>(
201 &self,
202 ) -> StdResult<DoubleValueDeserializeResult<F, S>> {
203 if let Ok(res) = self.clone().deserialize_into() {
204 return Ok(DoubleValueDeserializeResult::First(res));
205 }
206
207 if let Ok(res) = self.clone().deserialize_into() {
208 return Ok(DoubleValueDeserializeResult::Second(res));
209 }
210
211 Err(StdError::generic_err("Deserialize failed"))
212 }
213}
214
215#[cw_serde]
216pub enum DoubleValueDeserializeResult<F, S> {
217 First(F),
218 Second(S),
219}
220
221impl<'de, F, S> DoubleValueDeserializeResult<F, S>
222where
223 F: Deserialize<'de>,
224 S: Deserialize<'de>,
225{
226}
227
228#[allow(clippy::wrong_self_convention)]
229pub trait IntoSerdeJsonString: Serialize {
230 fn into_json_string(&self) -> StdResult<String> {
231 serde_json_wasm::to_string(self).into_std_result()
232 }
233}
234
235impl<T: Serialize> IntoSerdeJsonString for Option<T> {}