abstract_std/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 PrimaryKey<'_> 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> {
92 self.0.key()
93 }
94}
95
96impl Prefixer<'_> for AssetEntry {
97 fn prefix(&self) -> Vec<Key> {
98 self.0.prefix()
99 }
100}
101
102impl KeyDeserialize for AssetEntry {
103 type Output = AssetEntry;
104 const KEY_ELEMS: u16 = 1;
105
106 #[inline(always)]
107 fn from_vec(value: Vec<u8>) -> StdResult<Self::Output> {
108 Ok(AssetEntry(String::from_vec(value)?))
109 }
110}
111
112impl KeyDeserialize for &AssetEntry {
113 type Output = AssetEntry;
114 const KEY_ELEMS: u16 = AssetEntry::KEY_ELEMS;
115
116 #[inline(always)]
117 fn from_vec(value: Vec<u8>) -> StdResult<Self::Output> {
118 Ok(AssetEntry(String::from_vec(value)?))
119 }
120}
121
122#[cfg(test)]
123mod test {
124 #![allow(clippy::needless_borrows_for_generic_args)]
125 use rstest::rstest;
126
127 use super::*;
128
129 #[coverage_helper::test]
130 fn test_asset_entry() {
131 let mut entry = AssetEntry::new("CRAB");
132 assert_eq!(entry.as_str(), "crab");
133 entry.format();
134 assert_eq!(entry.as_str(), "crab");
135 }
136
137 #[coverage_helper::test]
138 fn test_src_chain() -> AbstractResult<()> {
139 let entry = AssetEntry::new("CRAB");
141 assert_eq!(
142 entry.src_chain(),
143 Err(AbstractError::EntryFormattingError {
144 actual: "crab".to_string(),
145 expected: "src_chain>asset_name".to_string(),
146 })
147 );
148 let entry = AssetEntry::new("osmosis>crab");
149 assert_eq!(entry.src_chain(), Ok("osmosis".to_string()));
150 let entry = AssetEntry::new("osmosis>juno>crab");
151 assert_eq!(entry.src_chain(), Ok("osmosis".to_string()));
152
153 Ok(())
154 }
155
156 #[rstest]
157 #[case("CRAB")]
158 #[case("")]
159 #[case(">")]
160 #[case("juno>")]
161 fn test_src_chain_error(#[case] input: &str) {
162 let entry = AssetEntry::new(input);
163
164 assert_eq!(
165 entry.src_chain(),
166 Err(AbstractError::EntryFormattingError {
167 actual: input.to_ascii_lowercase(),
168 expected: "src_chain>asset_name".to_string(),
169 })
170 );
171 }
172
173 #[coverage_helper::test]
174 fn test_from_string() {
175 let entry = AssetEntry::from("CRAB".to_string());
176 assert_eq!(entry.as_str(), "crab");
177 }
178
179 #[coverage_helper::test]
180 fn test_from_str() {
181 let entry = AssetEntry::from("CRAB");
182 assert_eq!(entry.as_str(), "crab");
183 }
184
185 #[coverage_helper::test]
186 fn test_from_ref_string() {
187 let entry = AssetEntry::from(&"CRAB".to_string());
188 assert_eq!(entry.as_str(), "crab");
189 }
190
191 #[coverage_helper::test]
192 fn test_to_string() {
193 let entry = AssetEntry::new("CRAB");
194 assert_eq!(entry.to_string(), "crab".to_string());
195 }
196
197 #[coverage_helper::test]
198 fn string_key_works() {
199 let k = &AssetEntry::new("CRAB");
200 let path = k.key();
201 assert_eq!(1, path.len());
202 assert_eq!(b"crab", path[0].as_ref());
203
204 let joined = k.joined_key();
205 assert_eq!(joined, b"crab")
206 }
207}