abstract_core/objects/entry/
channel_entry.rs1use std::{convert::TryInto, fmt::Display};
2
3use cosmwasm_std::{StdError, StdResult};
4use cw_storage_plus::{Key, KeyDeserialize, Prefixer, PrimaryKey};
5use schemars::JsonSchema;
6use serde::{Deserialize, Serialize};
7
8use crate::{objects::chain_name::ChainName, AbstractResult};
9
10#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq, JsonSchema, PartialOrd, Ord)]
12pub struct UncheckedChannelEntry {
13 pub connected_chain: String,
14 pub protocol: String,
15}
16
17impl UncheckedChannelEntry {
18 pub fn new<T: ToString>(connected_chain: T, protocol: T) -> Self {
19 Self {
20 protocol: protocol.to_string(),
21 connected_chain: connected_chain.to_string(),
22 }
23 }
24 pub fn check(self) -> AbstractResult<ChannelEntry> {
25 let chain_name: ChainName = ChainName::from_string(self.connected_chain)?;
26 Ok(ChannelEntry {
27 connected_chain: chain_name,
28 protocol: self.protocol.to_ascii_lowercase(),
29 })
30 }
31}
32
33impl TryFrom<String> for UncheckedChannelEntry {
34 type Error = StdError;
35 fn try_from(entry: String) -> Result<Self, Self::Error> {
36 let composite: Vec<&str> = entry.split('/').collect();
37 if composite.len() != 2 {
38 return Err(StdError::generic_err(
39 "connected_chain entry should be formatted as \"connected_chain_name/protocol\".",
40 ));
41 }
42 Ok(Self::new(composite[0], composite[1]))
43 }
44}
45
46#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, JsonSchema, Eq, PartialOrd, Ord)]
49pub struct ChannelEntry {
50 pub connected_chain: ChainName,
51 pub protocol: String,
52}
53
54impl Display for ChannelEntry {
55 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56 write!(f, "{}/{}", self.connected_chain.as_str(), self.protocol)
57 }
58}
59
60impl<'a> PrimaryKey<'a> for &ChannelEntry {
61 type Prefix = String;
62
63 type SubPrefix = ();
64
65 type Suffix = String;
66
67 type SuperSuffix = Self;
68
69 fn key(&self) -> Vec<cw_storage_plus::Key> {
70 let mut keys = self.connected_chain.str_ref().key();
71 keys.extend(self.protocol.key());
72 keys
73 }
74}
75
76impl<'a> Prefixer<'a> for &ChannelEntry {
77 fn prefix(&self) -> Vec<Key> {
78 let mut res = self.connected_chain.str_ref().prefix();
79 res.extend(self.protocol.prefix());
80 res
81 }
82}
83
84impl KeyDeserialize for &ChannelEntry {
85 type Output = ChannelEntry;
86
87 #[inline(always)]
88 fn from_vec(mut value: Vec<u8>) -> StdResult<Self::Output> {
89 let mut tu = value.split_off(2);
90 let t_len = parse_length(&value)?;
91 let u = tu.split_off(t_len);
92
93 Ok(ChannelEntry {
94 connected_chain: ChainName::_from_string(String::from_vec(tu)?),
95 protocol: String::from_vec(u)?,
96 })
97 }
98}
99
100#[inline(always)]
101fn parse_length(value: &[u8]) -> StdResult<usize> {
102 Ok(u16::from_be_bytes(
103 value
104 .try_into()
105 .map_err(|_| StdError::generic_err("Could not read 2 byte length"))?,
106 )
107 .into())
108}
109
110#[cfg(test)]
115mod test {
116 use std::str::FromStr;
117
118 use cosmwasm_std::{testing::mock_dependencies, Addr, Order};
119 use cw_storage_plus::Map;
120
121 use super::*;
122
123 fn mock_key() -> ChannelEntry {
124 ChannelEntry {
125 connected_chain: ChainName::from_str("osmosis").unwrap(),
126 protocol: "ics20".to_string(),
127 }
128 }
129
130 fn mock_keys() -> (ChannelEntry, ChannelEntry, ChannelEntry) {
131 (
132 ChannelEntry {
133 connected_chain: ChainName::from_str("osmosis").unwrap(),
134 protocol: "ics20".to_string(),
135 },
136 ChannelEntry {
137 connected_chain: ChainName::from_str("osmosis").unwrap(),
138 protocol: "ics".to_string(),
139 },
140 ChannelEntry {
141 connected_chain: ChainName::from_str("cosmos").unwrap(),
142 protocol: "abstract".to_string(),
143 },
144 )
145 }
146
147 #[test]
148 fn storage_key_works() {
149 let mut deps = mock_dependencies();
150 let key = mock_key();
151 let map: Map<&ChannelEntry, u64> = Map::new("map");
152
153 map.save(deps.as_mut().storage, &key, &42069).unwrap();
154
155 assert_eq!(map.load(deps.as_ref().storage, &key).unwrap(), 42069);
156
157 let items = map
158 .range(deps.as_ref().storage, None, None, Order::Ascending)
159 .map(|item| item.unwrap())
160 .collect::<Vec<_>>();
161
162 assert_eq!(items.len(), 1);
163 assert_eq!(items[0], (key, 42069));
164 }
165
166 #[test]
167 fn composite_key_works() {
168 let mut deps = mock_dependencies();
169 let key = mock_key();
170 let map: Map<(&ChannelEntry, Addr), u64> = Map::new("map");
171
172 map.save(
173 deps.as_mut().storage,
174 (&key, Addr::unchecked("larry")),
175 &42069,
176 )
177 .unwrap();
178
179 map.save(
180 deps.as_mut().storage,
181 (&key, Addr::unchecked("jake")),
182 &69420,
183 )
184 .unwrap();
185
186 let items = map
187 .prefix(&key)
188 .range(deps.as_ref().storage, None, None, Order::Ascending)
189 .map(|item| item.unwrap())
190 .collect::<Vec<_>>();
191
192 assert_eq!(items.len(), 2);
193 assert_eq!(items[0], (Addr::unchecked("jake"), 69420));
194 assert_eq!(items[1], (Addr::unchecked("larry"), 42069));
195 }
196
197 #[test]
198 fn partial_key_works() {
199 let mut deps = mock_dependencies();
200 let (key1, key2, key3) = mock_keys();
201 let map: Map<&ChannelEntry, u64> = Map::new("map");
202
203 map.save(deps.as_mut().storage, &key1, &42069).unwrap();
204
205 map.save(deps.as_mut().storage, &key2, &69420).unwrap();
206
207 map.save(deps.as_mut().storage, &key3, &999).unwrap();
208
209 let items = map
210 .prefix("osmosis".to_string())
211 .range(deps.as_ref().storage, None, None, Order::Ascending)
212 .map(|item| item.unwrap())
213 .collect::<Vec<_>>();
214
215 assert_eq!(items.len(), 2);
216 assert_eq!(items[0], ("ics".to_string(), 69420));
217 assert_eq!(items[1], ("ics20".to_string(), 42069));
218 }
219}