1use cosmwasm_std::Uint128;
2use schemars::JsonSchema;
3use serde::{Deserialize, Serialize};
4
5pub fn package_title_from_name(name: &str) -> String {
8 name.trim_start_matches("ownable-")
9 .split(['-', '_'])
10 .filter(|part| !part.is_empty())
11 .map(|part| {
12 let mut chars = part.chars();
13 match chars.next() {
14 Some(first) => format!("{}{}", first.to_ascii_uppercase(), chars.as_str()),
15 None => String::new(),
16 }
17 })
18 .collect::<Vec<_>>()
19 .join(" ")
20}
21
22pub fn get_random_color(hash: String) -> String {
24 let (red, green, blue) = derive_rgb_values(hash);
25 rgb_hex(red, green, blue)
26}
27
28pub fn derive_rgb_values(hash: String) -> (u8, u8, u8) {
30 let mut s = hash.trim().trim_start_matches("0x").to_string();
31 if s.len() % 2 == 1 {
32 s.insert(0, '0');
33 }
34
35 match hex::decode(&s) {
36 Ok(mut bytes) => {
37 bytes.reverse();
38 let r = *bytes.get(0).unwrap_or(&0);
39 let g = *bytes.get(1).unwrap_or(&0);
40 let b = *bytes.get(2).unwrap_or(&0);
41 (r, g, b)
42 }
43 Err(_) => (0, 0, 0),
44 }
45}
46
47pub fn rgb_hex(r: u8, g: u8, b: u8) -> String {
50 format!("#{:02X}{:02X}{:02X}", r, g, b)
51}
52
53#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug, Default)]
55pub struct Metadata {
57 pub image: Option<String>,
58 pub image_data: Option<String>,
59 pub external_url: Option<String>,
60 pub description: Option<String>,
61 pub name: Option<String>,
62 pub background_color: Option<String>,
63 pub animation_url: Option<String>,
64 pub youtube_url: Option<String>,
65}
66
67#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
69pub struct NFT {
70 pub network: String,
71 pub id: Uint128,
72 pub address: String,
73 pub lock_service: Option<String>,
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79
80 #[test]
81 fn rgb_hex_formats_correctly() {
82 assert_eq!(rgb_hex(0, 0, 0), "#000000");
83 assert_eq!(rgb_hex(255, 255, 255), "#FFFFFF");
84 assert_eq!(rgb_hex(255, 0, 0), "#FF0000");
85 assert_eq!(rgb_hex(0, 128, 255), "#0080FF");
86 }
87
88 #[test]
89 fn derive_rgb_values_reads_last_three_bytes_reversed() {
90 assert_eq!(derive_rgb_values("010203".to_string()), (3, 2, 1));
91 }
92
93 #[test]
94 fn derive_rgb_values_strips_0x_prefix() {
95 assert_eq!(
96 derive_rgb_values("0x010203".to_string()),
97 derive_rgb_values("010203".to_string())
98 );
99 }
100
101 #[test]
102 fn derive_rgb_values_pads_odd_length_input() {
103 assert_eq!(derive_rgb_values("abc".to_string()), (0xbc, 0x0a, 0));
104 }
105
106 #[test]
107 fn derive_rgb_values_returns_zeros_for_invalid_hex() {
108 assert_eq!(derive_rgb_values("xyz".to_string()), (0, 0, 0));
109 }
110
111 #[test]
112 fn derive_rgb_values_returns_zeros_for_empty_input() {
113 assert_eq!(derive_rgb_values("".to_string()), (0, 0, 0));
114 }
115
116 #[test]
117 fn derive_rgb_values_uses_last_three_bytes_of_long_input() {
118 assert_eq!(
119 derive_rgb_values("aabbccdd11223344".to_string()),
120 (0x44, 0x33, 0x22)
121 );
122 }
123
124 #[test]
125 fn get_random_color_returns_hash_prefixed_hex() {
126 let color = get_random_color("010203".to_string());
127 assert!(color.starts_with('#'));
128 assert_eq!(color.len(), 7);
129 }
130
131 #[test]
132 fn get_random_color_is_deterministic() {
133 let hash = "deadbeef".to_string();
134 assert_eq!(get_random_color(hash.clone()), get_random_color(hash));
135 }
136}