spotify_cli/storage/pins/
pin.rs

1use serde::{Deserialize, Serialize};
2
3use super::resource_type::ResourceType;
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct Pin {
7    pub resource_type: ResourceType,
8    pub id: String,
9    pub alias: String,
10    pub tags: Vec<String>,
11}
12
13impl Pin {
14    pub fn new(resource_type: ResourceType, id: String, alias: String, tags: Vec<String>) -> Self {
15        Self {
16            resource_type,
17            id,
18            alias,
19            tags,
20        }
21    }
22
23    /// Extract Spotify ID from a URL or return the input if it's already an ID
24    pub fn extract_id(url_or_id: &str) -> String {
25        if url_or_id.contains("open.spotify.com") {
26            url_or_id
27                .split('/')
28                .next_back()
29                .unwrap_or(url_or_id)
30                .split('?')
31                .next()
32                .unwrap_or(url_or_id)
33                .to_string()
34        } else if url_or_id.contains(':') {
35            url_or_id
36                .split(':')
37                .next_back()
38                .unwrap_or(url_or_id)
39                .to_string()
40        } else {
41            url_or_id.to_string()
42        }
43    }
44
45    /// Get the Spotify URI for this pin
46    pub fn uri(&self) -> String {
47        format!("spotify:{}:{}", self.resource_type.as_str(), self.id)
48    }
49}
50
51#[cfg(test)]
52mod tests {
53    use super::*;
54
55    #[test]
56    fn pin_new() {
57        let pin = Pin::new(
58            ResourceType::Track,
59            "track123".to_string(),
60            "favorite".to_string(),
61            vec!["rock".to_string(), "chill".to_string()],
62        );
63        assert_eq!(pin.id, "track123");
64        assert_eq!(pin.alias, "favorite");
65        assert_eq!(pin.tags.len(), 2);
66    }
67
68    #[test]
69    fn pin_uri_track() {
70        let pin = Pin::new(
71            ResourceType::Track,
72            "abc123".to_string(),
73            "test".to_string(),
74            vec![],
75        );
76        assert_eq!(pin.uri(), "spotify:track:abc123");
77    }
78
79    #[test]
80    fn pin_uri_playlist() {
81        let pin = Pin::new(
82            ResourceType::Playlist,
83            "xyz789".to_string(),
84            "test".to_string(),
85            vec![],
86        );
87        assert_eq!(pin.uri(), "spotify:playlist:xyz789");
88    }
89
90    #[test]
91    fn pin_uri_album() {
92        let pin = Pin::new(
93            ResourceType::Album,
94            "album123".to_string(),
95            "test".to_string(),
96            vec![],
97        );
98        assert_eq!(pin.uri(), "spotify:album:album123");
99    }
100
101    #[test]
102    fn extract_id_from_plain_id() {
103        let id = Pin::extract_id("abc123");
104        assert_eq!(id, "abc123");
105    }
106
107    #[test]
108    fn extract_id_from_spotify_uri() {
109        let id = Pin::extract_id("spotify:track:abc123");
110        assert_eq!(id, "abc123");
111    }
112
113    #[test]
114    fn extract_id_from_spotify_url() {
115        let id = Pin::extract_id("https://open.spotify.com/track/abc123");
116        assert_eq!(id, "abc123");
117    }
118
119    #[test]
120    fn extract_id_from_spotify_url_with_query() {
121        let id = Pin::extract_id("https://open.spotify.com/track/abc123?si=xyz");
122        assert_eq!(id, "abc123");
123    }
124
125    #[test]
126    fn pin_serializes_to_json() {
127        let pin = Pin::new(
128            ResourceType::Track,
129            "abc123".to_string(),
130            "test_alias".to_string(),
131            vec!["tag1".to_string()],
132        );
133        let json = serde_json::to_value(&pin).unwrap();
134        assert_eq!(json["id"], "abc123");
135        assert_eq!(json["alias"], "test_alias");
136    }
137
138    #[test]
139    fn pin_deserializes_from_json() {
140        let json = serde_json::json!({
141            "resource_type": "track",
142            "id": "abc123",
143            "alias": "test_alias",
144            "tags": ["tag1", "tag2"]
145        });
146        let pin: Pin = serde_json::from_value(json).unwrap();
147        assert_eq!(pin.id, "abc123");
148        assert_eq!(pin.alias, "test_alias");
149        assert_eq!(pin.tags.len(), 2);
150    }
151}