chatsounds/
fetching.rs

1use std::path::{Component, Path};
2
3use serde::Deserialize;
4
5use crate::{cache::download, error::Result, types::Chatsound, Chatsounds, Error};
6
7#[derive(Deserialize)]
8pub struct GitHubApiFileEntry {
9    pub path: String,
10    pub r#type: String,
11    pub size: Option<usize>,
12}
13
14#[derive(Deserialize)]
15pub struct GitHubApiTrees {
16    pub tree: Vec<GitHubApiFileEntry>,
17}
18
19pub type GitHubMsgpackEntries = Vec<Vec<String>>;
20
21impl Chatsounds {
22    pub async fn fetch_github_api(&self, repo: &str, _repo_path: &str) -> Result<GitHubApiTrees> {
23        let api_url = format!("https://api.github.com/repos/{repo}/git/trees/HEAD?recursive=1");
24
25        #[cfg(feature = "fs")]
26        let cache = &self.cache_path;
27        #[cfg(feature = "memory")]
28        let cache = self.fs_memory.clone();
29
30        let bytes = download(&api_url, cache, false).await?;
31
32        let trees: GitHubApiTrees =
33            serde_json::from_slice(&bytes).map_err(|err| Error::Json { err, url: api_url })?;
34
35        Ok(trees)
36    }
37
38    pub fn load_github_api(
39        &mut self,
40        repo: &str,
41        repo_path: &str,
42        mut trees: GitHubApiTrees,
43    ) -> Result<()> {
44        for entry in &mut trees.tree {
45            if entry.r#type != "blob" || !entry.path.starts_with(repo_path) {
46                continue;
47            }
48
49            // e26/stop.ogg or e26/nestetrismusic/1.ogg
50            let sound_path = entry.path.split_off(repo_path.len() + 1);
51
52            let path = Path::new(&sound_path);
53            if let Some(Component::Normal(s)) = path.components().nth(1) {
54                if let Some(sentence) = Path::new(&s).file_stem() {
55                    let sentence = sentence.to_string_lossy().to_string();
56                    let vec = self.map_store.entry(sentence).or_default();
57                    let chatsound = Chatsound {
58                        repo: repo.to_string(),
59                        repo_path: repo_path.to_string(),
60                        sound_path,
61                    };
62
63                    let url = chatsound.get_url();
64                    match vec.binary_search_by(|c| c.get_url().cmp(&url)) {
65                        Ok(_pos) => {
66                            // already exists, don't add again
67                        }
68                        Err(pos) => {
69                            vec.insert(pos, chatsound);
70                        }
71                    }
72                }
73            }
74        }
75
76        Ok(())
77    }
78
79    pub async fn fetch_github_msgpack(
80        &self,
81        repo: &str,
82        repo_path: &str,
83    ) -> Result<GitHubMsgpackEntries> {
84        let msgpack_url =
85            format!("https://raw.githubusercontent.com/{repo}/HEAD/{repo_path}/list.msgpack");
86
87        #[cfg(feature = "fs")]
88        let cache = &self.cache_path;
89        #[cfg(feature = "memory")]
90        let cache = self.fs_memory.clone();
91
92        let bytes = download(&msgpack_url, cache, false).await?;
93        let entries: GitHubMsgpackEntries =
94            rmp_serde::decode::from_slice(&bytes).map_err(|err| Error::Msgpack {
95                err,
96                url: msgpack_url,
97            })?;
98
99        Ok(entries)
100    }
101
102    pub fn load_github_msgpack(
103        &mut self,
104        repo: &str,
105        repo_path: &str,
106        entries: GitHubMsgpackEntries,
107    ) -> Result<()> {
108        for entry in entries {
109            // e26/stop.ogg or e26/nestetrismusic/1.ogg
110            let sentence = entry[1].clone();
111            let sound_path = entry[2].clone();
112            let vec = self.map_store.entry(sentence.to_string()).or_default();
113
114            let chatsound = Chatsound {
115                repo: repo.to_string(),
116                repo_path: repo_path.to_string(),
117                sound_path,
118            };
119
120            let url = chatsound.get_url();
121            match vec.binary_search_by(|c| c.get_url().cmp(&url)) {
122                Ok(_pos) => {
123                    // already exists, don't add again
124                }
125                Err(pos) => {
126                    vec.insert(pos, chatsound);
127                }
128            }
129        }
130
131        Ok(())
132    }
133}