abstract_core/objects/entry/
asset_entry.rs1use std::fmt::Display;
2
3use cosmwasm_std::StdResult;
4use cw_storage_plus::{Key, KeyDeserialize, Prefixer, PrimaryKey};
5use schemars::JsonSchema;
6use serde::{Deserialize, Serialize};
7
8use crate::{constants::CHAIN_DELIMITER, AbstractError, AbstractResult};
9
10#[derive(
13 Deserialize, Serialize, Clone, Debug, PartialEq, Eq, JsonSchema, PartialOrd, Ord, Default,
14)]
15pub struct AssetEntry(pub(crate) String);
16
17impl AssetEntry {
18 pub fn new(entry: &str) -> Self {
19 Self(str::to_ascii_lowercase(entry))
20 }
21 pub fn as_str(&self) -> &str {
22 &self.0
23 }
24 pub fn format(&mut self) {
25 self.0 = self.0.to_ascii_lowercase();
26 }
27
28 pub fn src_chain(&self) -> AbstractResult<String> {
31 let mut split = self.0.splitn(2, CHAIN_DELIMITER);
32
33 match split.next() {
34 Some(src_chain) => {
35 if src_chain.is_empty() {
36 return self.entry_formatting_error();
37 }
38 let maybe_asset_name = split.next();
40 if maybe_asset_name.is_some() && maybe_asset_name != Some("") {
41 Ok(src_chain.to_string())
42 } else {
43 self.entry_formatting_error()
44 }
45 }
46 None => self.entry_formatting_error(),
47 }
48 }
49
50 fn entry_formatting_error(&self) -> AbstractResult<String> {
51 Err(AbstractError::EntryFormattingError {
52 actual: self.0.clone(),
53 expected: "src_chain>asset_name".to_string(),
54 })
55 }
56}
57
58impl From<&str> for AssetEntry {
59 fn from(entry: &str) -> Self {
60 Self::new(entry)
61 }
62}
63
64impl From<String> for AssetEntry {
65 fn from(entry: String) -> Self {
66 Self::new(&entry)
67 }
68}
69
70impl From<&String> for AssetEntry {
71 fn from(entry: &String) -> Self {
72 Self::new(entry)
73 }
74}
75
76impl Display for AssetEntry {
77 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78 write!(f, "{}", self.0)
79 }
80}
81
82impl<'a> PrimaryKey<'a> for &AssetEntry {
83 type Prefix = ();
84
85 type SubPrefix = ();
86
87 type Suffix = Self;
88
89 type SuperSuffix = Self;
90
91 fn key(&self) -> Vec<cw_storage_plus::Key> {
93 self.0.key()
94 }
95}
96
97impl<'a> Prefixer<'a> for &AssetEntry {
98 fn prefix(&self) -> Vec<Key> {
99 self.0.prefix()
100 }
101}
102
103impl KeyDeserialize for &AssetEntry {
104 type Output = AssetEntry;
105
106 #[inline(always)]
107 fn from_vec(value: Vec<u8>) -> StdResult<Self::Output> {
108 Ok(AssetEntry(String::from_vec(value)?))
109 }
110}
111
112#[cfg(test)]
113mod test {
114 use rstest::rstest;
115 use speculoos::prelude::*;
116
117 use super::*;
118
119 #[test]
120 fn test_asset_entry() {
121 let mut entry = AssetEntry::new("CRAB");
122 assert_that!(entry.as_str()).is_equal_to("crab");
123 entry.format();
124 assert_that!(entry.as_str()).is_equal_to("crab");
125 }
126
127 #[test]
128 fn test_src_chain() -> AbstractResult<()> {
129 let entry = AssetEntry::new("CRAB");
131 assert_that!(entry.src_chain())
132 .is_err()
133 .is_equal_to(AbstractError::EntryFormattingError {
134 actual: "crab".to_string(),
135 expected: "src_chain>asset_name".to_string(),
136 });
137 let entry = AssetEntry::new("osmosis>crab");
138 assert_that!(entry.src_chain())
139 .is_ok()
140 .is_equal_to("osmosis".to_string());
141 let entry = AssetEntry::new("osmosis>juno>crab");
142 assert_that!(entry.src_chain())
143 .is_ok()
144 .is_equal_to("osmosis".to_string());
145
146 Ok(())
147 }
148
149 #[rstest]
150 #[case("CRAB")]
151 #[case("")]
152 #[case(">")]
153 #[case("juno>")]
154 fn test_src_chain_error(#[case] input: &str) {
155 let entry = AssetEntry::new(input);
156
157 assert_that!(entry.src_chain())
158 .is_err()
159 .is_equal_to(AbstractError::EntryFormattingError {
160 actual: input.to_ascii_lowercase(),
161 expected: "src_chain>asset_name".to_string(),
162 });
163 }
164
165 #[test]
166 fn test_from_string() {
167 let entry = AssetEntry::from("CRAB".to_string());
168 assert_that!(entry.as_str()).is_equal_to("crab");
169 }
170
171 #[test]
172 fn test_from_str() {
173 let entry = AssetEntry::from("CRAB");
174 assert_that!(entry.as_str()).is_equal_to("crab");
175 }
176
177 #[test]
178 fn test_from_ref_string() {
179 let entry = AssetEntry::from(&"CRAB".to_string());
180 assert_that!(entry.as_str()).is_equal_to("crab");
181 }
182
183 #[test]
184 fn test_to_string() {
185 let entry = AssetEntry::new("CRAB");
186 assert_that!(entry.to_string()).is_equal_to("crab".to_string());
187 }
188
189 #[test]
190 fn string_key_works() {
191 let k = &AssetEntry::new("CRAB");
192 let path = k.key();
193 assert_eq!(1, path.len());
194 assert_eq!(b"crab", path[0].as_ref());
195
196 let joined = k.joined_key();
197 assert_eq!(joined, b"crab")
198 }
199}