mockforge_plugin_registry/
api.rs1use crate::{
4 RegistryConfig, RegistryEntry, RegistryError, Result, SearchQuery, SearchResults, VersionEntry,
5};
6use serde::{Deserialize, Serialize};
7use std::time::Duration;
8
9pub struct RegistryClient {
11 config: RegistryConfig,
12 client: reqwest::Client,
13}
14
15impl RegistryClient {
16 pub fn new(config: RegistryConfig) -> Result<Self> {
18 let client = reqwest::Client::builder()
19 .timeout(Duration::from_secs(config.timeout))
20 .build()
21 .map_err(|e| RegistryError::Network(e.to_string()))?;
22
23 Ok(Self { config, client })
24 }
25
26 pub async fn search(&self, query: SearchQuery) -> Result<SearchResults> {
28 let url = format!("{}/api/v1/plugins/search", self.config.url);
29 let response = self
30 .client
31 .post(&url)
32 .json(&query)
33 .send()
34 .await
35 .map_err(|e| RegistryError::Network(e.to_string()))?;
36
37 if !response.status().is_success() {
38 return Err(RegistryError::Network(format!("Search failed: {}", response.status())));
39 }
40
41 let results = response.json().await.map_err(|e| RegistryError::Network(e.to_string()))?;
42
43 Ok(results)
44 }
45
46 pub async fn get_plugin(&self, name: &str) -> Result<RegistryEntry> {
48 let url = format!("{}/api/v1/plugins/{}", self.config.url, name);
49 let response = self
50 .client
51 .get(&url)
52 .send()
53 .await
54 .map_err(|e| RegistryError::Network(e.to_string()))?;
55
56 if response.status() == reqwest::StatusCode::NOT_FOUND {
57 return Err(RegistryError::PluginNotFound(name.to_string()));
58 }
59
60 if !response.status().is_success() {
61 return Err(RegistryError::Network(format!(
62 "Get plugin failed: {}",
63 response.status()
64 )));
65 }
66
67 let entry = response.json().await.map_err(|e| RegistryError::Network(e.to_string()))?;
68
69 Ok(entry)
70 }
71
72 pub async fn get_version(&self, name: &str, version: &str) -> Result<VersionEntry> {
74 let url = format!("{}/api/v1/plugins/{}/versions/{}", self.config.url, name, version);
75 let response = self
76 .client
77 .get(&url)
78 .send()
79 .await
80 .map_err(|e| RegistryError::Network(e.to_string()))?;
81
82 if !response.status().is_success() {
83 return Err(RegistryError::InvalidVersion(version.to_string()));
84 }
85
86 let entry = response.json().await.map_err(|e| RegistryError::Network(e.to_string()))?;
87
88 Ok(entry)
89 }
90
91 pub async fn publish(&self, manifest: PublishRequest) -> Result<PublishResponse> {
93 let token = self.config.token.as_ref().ok_or(RegistryError::AuthRequired)?;
94
95 let url = format!("{}/api/v1/plugins/publish", self.config.url);
96 let response = self
97 .client
98 .post(&url)
99 .bearer_auth(token)
100 .json(&manifest)
101 .send()
102 .await
103 .map_err(|e| RegistryError::Network(e.to_string()))?;
104
105 if response.status() == reqwest::StatusCode::UNAUTHORIZED {
106 return Err(RegistryError::AuthRequired);
107 }
108
109 if response.status() == reqwest::StatusCode::FORBIDDEN {
110 return Err(RegistryError::PermissionDenied);
111 }
112
113 if !response.status().is_success() {
114 return Err(RegistryError::Network(format!("Publish failed: {}", response.status())));
115 }
116
117 let result = response.json().await.map_err(|e| RegistryError::Network(e.to_string()))?;
118
119 Ok(result)
120 }
121
122 pub async fn download(&self, url: &str) -> Result<Vec<u8>> {
124 let response = self
125 .client
126 .get(url)
127 .send()
128 .await
129 .map_err(|e| RegistryError::Network(e.to_string()))?;
130
131 if !response.status().is_success() {
132 return Err(RegistryError::Network(format!("Download failed: {}", response.status())));
133 }
134
135 let bytes = response.bytes().await.map_err(|e| RegistryError::Network(e.to_string()))?;
136
137 Ok(bytes.to_vec())
138 }
139
140 pub async fn yank(&self, name: &str, version: &str) -> Result<()> {
142 let token = self.config.token.as_ref().ok_or(RegistryError::AuthRequired)?;
143
144 let url = format!("{}/api/v1/plugins/{}/versions/{}/yank", self.config.url, name, version);
145 let response = self
146 .client
147 .delete(&url)
148 .bearer_auth(token)
149 .send()
150 .await
151 .map_err(|e| RegistryError::Network(e.to_string()))?;
152
153 if !response.status().is_success() {
154 return Err(RegistryError::Network(format!("Yank failed: {}", response.status())));
155 }
156
157 Ok(())
158 }
159}
160
161#[derive(Debug, Serialize, Deserialize)]
163pub struct PublishRequest {
164 pub name: String,
165 pub version: String,
166 pub description: String,
167 pub author: crate::AuthorInfo,
168 pub license: String,
169 pub repository: Option<String>,
170 pub homepage: Option<String>,
171 pub tags: Vec<String>,
172 pub category: crate::PluginCategory,
173 pub checksum: String,
174 pub size: u64,
175 pub min_mockforge_version: Option<String>,
176}
177
178#[derive(Debug, Serialize, Deserialize)]
180pub struct PublishResponse {
181 pub success: bool,
182 pub upload_url: String,
183 pub message: String,
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189
190 #[test]
191 fn test_client_creation() {
192 let config = RegistryConfig::default();
193 let client = RegistryClient::new(config);
194 assert!(client.is_ok());
195 }
196}