waves_rust/model/
alias.rs

1use crate::error::{Error, Result};
2use crate::util::ByteWriter;
3use regex::Regex;
4
5const PREFIX: &str = "alias:";
6const MIN_LENGTH: u8 = 4;
7const MAX_LENGTH: u8 = 30;
8const ALPHABET: &str = "-.0-9@_a-z";
9const TYPE: u8 = 2;
10
11#[derive(Clone, Eq, PartialEq, Debug, Hash)]
12pub struct Alias {
13    bytes: Vec<u8>,
14    name: String,
15    full_name: String,
16}
17
18impl Alias {
19    pub fn new(chain_id: u8, name: &str) -> Result<Self> {
20        if Self::is_valid(chain_id, name) {
21            let bytes = ByteWriter::new()
22                .push_byte(TYPE)
23                .push_byte(chain_id)
24                .push_bytes(&mut name.to_owned().into_bytes())
25                .bytes();
26            let name = Self::replace_prefix(chain_id, name);
27            let full_name = format!("{}{}:{}", PREFIX, chain_id as char, &name);
28            return Ok(Self {
29                bytes,
30                name,
31                full_name,
32            });
33        }
34        Err(Error::InvalidAliasName {
35            min_length: MIN_LENGTH,
36            max_length: MAX_LENGTH,
37            alphabet: ALPHABET.to_owned(),
38            prefix: PREFIX.to_owned(),
39            chain_id: chain_id as char,
40        })
41    }
42
43    pub fn chain_id(full_name: String) -> u8 {
44        full_name
45            .replace(PREFIX, "")
46            .chars()
47            .next()
48            .expect("failed to get chain id from alias") as u8
49    }
50
51    pub fn bytes(&self) -> Vec<u8> {
52        self.bytes.clone()
53    }
54
55    pub fn name(&self) -> String {
56        self.name.clone()
57    }
58
59    pub fn full_name(&self) -> String {
60        self.full_name.clone()
61    }
62
63    pub fn is_valid(chain_id: u8, name: &str) -> bool {
64        let name = Self::replace_prefix(chain_id, name);
65        Regex::new(&format!(
66            r"^[{}]{{{},{}}}$",
67            ALPHABET, MIN_LENGTH, MAX_LENGTH
68        ))
69        .expect("invalid regex")
70        .is_match(&name)
71    }
72
73    fn replace_prefix(chain_id: u8, name: &str) -> String {
74        Regex::new(&format!(r"^{}{}:", PREFIX, chain_id as char))
75            .expect("invalid regex")
76            .replace(name, "")
77            .to_string()
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use crate::model::{Alias, ChainId};
84
85    #[test]
86    fn test_is_valid() {
87        let valid_alias = "alias:T:alias1662650000377".to_owned();
88        assert_eq!(true, Alias::is_valid(ChainId::TESTNET.byte(), &valid_alias));
89    }
90
91    #[test]
92    fn test_is_invalid() {
93        let invalid_alias1 = "alias1662650000377!".to_owned();
94        let invalid_alias2 = "alias 1662650000377".to_owned();
95        let invalid_alias3 = "ali".to_owned();
96        let invalid_alias4 = "alias1662650000377alias166265000037".to_owned();
97        assert_eq!(
98            false,
99            Alias::is_valid(ChainId::TESTNET.byte(), &invalid_alias1)
100        );
101        assert_eq!(
102            false,
103            Alias::is_valid(ChainId::TESTNET.byte(), &invalid_alias2)
104        );
105        assert_eq!(
106            false,
107            Alias::is_valid(ChainId::TESTNET.byte(), &invalid_alias3)
108        );
109        assert_eq!(
110            false,
111            Alias::is_valid(ChainId::TESTNET.byte(), &invalid_alias4)
112        )
113    }
114
115    #[test]
116    fn test_create_alias() {
117        let result = Alias::new(ChainId::TESTNET.byte(), "alias1662650000377");
118        match result {
119            Ok(alias) => {
120                assert_eq!(alias.name(), "alias1662650000377");
121                assert_eq!(alias.full_name(), "alias:T:alias1662650000377");
122                let mut bytes = vec![2, 84];
123                bytes.append(&mut alias.name().into_bytes());
124                assert_eq!(alias.bytes(), bytes);
125            }
126            Err(err) => {
127                println!("{:?}", err);
128                panic!("failed to create alias")
129            }
130        }
131    }
132
133    #[test]
134    fn test_chain_id_from_full_name_alias() {
135        let i = Alias::chain_id("alias:T:alias1662650000377".to_owned());
136        assert_eq!(84, i);
137    }
138}