dpm_core/
lib.rs

1use std::{
2    collections::HashMap,
3    fs::File,
4    io::{self, Read},
5    path::Path,
6};
7
8use anyhow::Result;
9use serde::{Deserialize, Serialize};
10use serde_json::to_writer_pretty;
11
12/// 代表套件的依賴資訊
13#[derive(Debug, Serialize, Deserialize, Clone)]
14pub struct Dependency {
15    pub name: String,
16    pub version: String,
17}
18/// 儲存套件的完整資訊
19#[derive(Debug, Serialize, Deserialize)]
20pub struct PackageInfo {
21    pub package_name: String,
22    pub file_name: String,
23    pub version: String,
24    pub description: String,
25    pub hash: String,
26    pub dependencies: Option<Vec<Dependency>>,
27}
28
29impl PackageInfo {
30    /// 建立一個新的 `PackageInfo` 實例
31    ///
32    /// # 參數
33    /// - `package_name`: 套件名稱
34    /// - `file_name`: 套件檔案名稱
35    /// - `version`: 套件版本
36    /// - `description`: 套件描述
37    /// - `hash`: 套件檔案的雜湊值
38    /// - `dependencies`: 可選的依賴列表
39    ///
40    /// # 回傳
41    /// 回傳一個新的 `PackageInfo` 結構體
42    pub fn new(
43        package_name: String,
44        file_name: String,
45        version: String,
46        description: String,
47        hash: String,
48        dependencies: Option<Vec<Dependency>>,
49    ) -> PackageInfo {
50        PackageInfo {
51            package_name,
52            file_name,
53            version,
54            description,
55            hash,
56            dependencies,
57        }
58    }
59}
60/// 用於處理 JSON 的存儲模組
61pub struct JsonStorage<T> {
62    _marker: std::marker::PhantomData<T>,
63}
64impl<T> JsonStorage<T>
65where
66    T: Serialize + for<'de> Deserialize<'de>,
67{
68    /// 從 JSON 檔案載入資料
69    ///
70    /// # 參數
71    /// - `path`: JSON 檔案的路徑
72    ///
73    /// # 回傳
74    /// 回傳載入的資料或錯誤
75    pub fn from_json(path: &Path) -> io::Result<T> {
76        let mut file_contents = String::new();
77        let mut file = File::open(path)?;
78        file.read_to_string(&mut file_contents)?;
79        let data: T = serde_json::from_str(&file_contents)?;
80        Ok(data)
81    }
82
83    /// 將資料存儲為 JSON 檔案
84    ///
85    /// # 參數
86    /// - `data`: 要儲存的資料
87    /// - `path`: 儲存檔案的路徑
88    pub fn to_json(data: &T, path: &Path) -> io::Result<()> {
89        let file = File::create(path)?;
90        to_writer_pretty(file, &data)?;
91        Ok(())
92    }
93
94    /// 從 URL 獲取並反序列化 JSON 資料(異步)
95    ///
96    /// # 參數
97    /// - `url`: JSON 資料的 URL
98    ///
99    /// # 回傳
100    /// 回傳載入的資料或錯誤
101    pub async fn from_url(url: &str) -> Result<T, Box<dyn std::error::Error>> {
102        let response = reqwest::get(url).await?.text().await?;
103        let repo_info: T = serde_json::from_str(&response)?;
104        Ok(repo_info)
105    }
106    /// 從字串反序列化 JSON 資料
107    ///
108    /// # 參數
109    /// - `file_contents`: JSON 格式的字串
110    ///
111    /// # 回傳
112    /// 回傳反序列化的資料或錯誤
113    pub fn from_str_to(file_contents: &str) -> io::Result<T> {
114        let data: T = serde_json::from_str(file_contents)?;
115        Ok(data)
116    }
117}
118
119/// 儲存庫的資訊管理模組
120#[derive(Debug, Serialize, Deserialize, Default)]
121pub struct RepoInfo {
122    /// 儲存庫內的套件映射
123    packages: HashMap<String, PackageBasicInfo>,
124}
125#[derive(Debug, Serialize, Deserialize)]
126/// 套件的基本資訊
127pub struct PackageBasicInfo {
128    /// 套件的下載 URL
129    pub url: String,
130    /// 套件的檔案名稱
131    pub file_name: String,
132    /// 套件的版本
133    pub version: String,
134    /// 套件檔案的雜湊值
135    pub hash: String,
136    /// 套件的依賴列表(可選)
137    pub dependencies: Option<Vec<Dependency>>,
138}
139impl RepoInfo {
140    /// 建立一個新的 `RepoInfo` 實例
141    pub fn new() -> Self {
142        RepoInfo {
143            packages: HashMap::new(),
144        }
145    }
146    /// 檢查是否存在指定名稱的套件
147    ///
148    /// # 參數
149    /// - `package_name`: 套件名稱
150    ///
151    /// # 回傳
152    /// 若存在回傳 `true`,否則回傳 `false`
153    pub fn has_package(&self, package_name: &str) -> bool {
154        self.packages.contains_key(package_name)
155    }
156    /// 根據名稱獲取套件
157    ///
158    /// # 參數
159    /// - `package_name`: 套件名稱
160    ///
161    /// # 回傳
162    /// 回傳套件資訊或錯誤
163    pub fn get_package(&self, package_name: &str) -> Result<&PackageBasicInfo> {
164        match self.packages.get(package_name) {
165            Some(package) => Ok(package),
166            None => Err(anyhow::anyhow!("Package '{}' not found.", package_name)),
167        }
168    }
169}
170#[cfg(feature = "server")]
171impl RepoInfo {
172    
173    
174    /// 新增一個套件到儲存庫
175    ///
176    /// # 參數
177    /// - `name`: 套件名稱
178    /// - 其他參數:套件的相關資訊
179    pub fn add_package(
180        &mut self,
181        name: String,
182        url: String,
183        file_name: String,
184        version: String,
185        hash: String,
186        dependencies: Option<Vec<Dependency>>,
187    ) {
188        let package = PackageBasicInfo {
189            url,
190            file_name,
191            version,
192            hash,
193            dependencies,
194        };
195        self.packages.insert(name, package);
196    }
197    /// 透過 `PackageBasicInfo` 新增一個套件
198    pub fn add_package_with_info(&mut self, name: String, info: PackageBasicInfo) {
199        self.packages.insert(name, info);
200    }
201    
202    /// 根據名稱移除套件
203    pub fn remove_package(&mut self, package_name: &str) -> Result<PackageBasicInfo> {
204        match self.packages.remove(package_name) {
205            Some(package) => Ok(package),
206            None => Err(anyhow::anyhow!("Package '{}' not found.", package_name)),
207        }
208    }
209    /// 更新儲存庫中的套件資訊
210    pub fn update_package(
211        &mut self,
212        package_name: &str,
213        url: Option<String>,
214        file_name: Option<String>,
215        version: Option<String>,
216        hash: Option<String>,
217        dependencies: Option<Vec<Dependency>>,
218    ) {
219        if let Some(existing_package) = self.packages.get_mut(package_name) {
220            if let Some(new_url) = url {
221                existing_package.url = new_url;
222            }
223            if let Some(new_file_name) = file_name {
224                existing_package.file_name = new_file_name;
225            }
226            if let Some(new_version) = version {
227                existing_package.version = new_version;
228            }
229            if let Some(new_hash) = hash {
230                existing_package.hash = new_hash;
231            }
232            if let Some(new_dependencies) = dependencies {
233                existing_package.dependencies = Some(new_dependencies);
234            }
235        } else {
236            self.packages.insert(
237                package_name.to_string(),
238                PackageBasicInfo {
239                    url: url.unwrap_or_default(),
240                    file_name: file_name.unwrap_or_default(),
241                    version: version.unwrap_or_default(),
242                    hash: hash.unwrap_or_default(),
243                    dependencies: None,
244                },
245            );
246        }
247    }
248}
249
250#[cfg(feature = "client")]
251impl RepoInfo{
252
253}