normalize_package_index/index/
slackware.rs1use super::{IndexError, PackageIndex, PackageMeta, VersionMeta};
26use std::collections::HashMap;
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
30pub enum SlackwareVersion {
31 Current,
33 Slack150,
35 Slack142,
37}
38
39impl SlackwareVersion {
40 fn branch(&self) -> &'static str {
42 match self {
43 Self::Current => "master",
44 Self::Slack150 => "15.0",
45 Self::Slack142 => "14.2",
46 }
47 }
48
49 pub fn name(&self) -> &'static str {
51 match self {
52 Self::Current => "current",
53 Self::Slack150 => "15.0",
54 Self::Slack142 => "14.2",
55 }
56 }
57
58 pub fn all() -> &'static [SlackwareVersion] {
60 &[Self::Current, Self::Slack150, Self::Slack142]
61 }
62
63 pub fn current() -> &'static [SlackwareVersion] {
65 &[Self::Current]
66 }
67
68 pub fn stable() -> &'static [SlackwareVersion] {
70 &[Self::Slack150, Self::Slack142]
71 }
72
73 pub fn latest_stable() -> &'static [SlackwareVersion] {
75 &[Self::Slack150]
76 }
77}
78
79const CATEGORIES: &[&str] = &[
81 "system",
82 "development",
83 "network",
84 "multimedia",
85 "desktop",
86 "misc",
87 "libraries",
88 "games",
89 "graphics",
90 "office",
91 "audio",
92 "academic",
93 "accessibility",
94 "business",
95 "gis",
96 "ham",
97 "haskell",
98 "perl",
99 "python",
100 "ruby",
101];
102
103pub struct Slackware {
105 versions: Vec<SlackwareVersion>,
106}
107
108impl Slackware {
109 const SBO_API: &'static str = "https://slackbuilds.org";
111
112 pub fn all() -> Self {
114 Self {
115 versions: SlackwareVersion::all().to_vec(),
116 }
117 }
118
119 pub fn current() -> Self {
121 Self {
122 versions: SlackwareVersion::current().to_vec(),
123 }
124 }
125
126 pub fn stable() -> Self {
128 Self {
129 versions: SlackwareVersion::stable().to_vec(),
130 }
131 }
132
133 pub fn latest_stable() -> Self {
135 Self {
136 versions: SlackwareVersion::latest_stable().to_vec(),
137 }
138 }
139
140 pub fn with_versions(versions: &[SlackwareVersion]) -> Self {
142 Self {
143 versions: versions.to_vec(),
144 }
145 }
146
147 fn fetch_from_version(
149 name: &str,
150 version: SlackwareVersion,
151 ) -> Result<PackageMeta, IndexError> {
152 for category in CATEGORIES {
154 let info_url = format!(
155 "https://raw.githubusercontent.com/SlackBuildsOrg/slackbuilds/{}/{}/{}/{}.info",
156 version.branch(),
157 category,
158 name,
159 name
160 );
161
162 if let Ok(response) = ureq::get(&info_url).call() {
163 if let Ok(body) = response.into_string() {
164 return parse_sbo_info(&body, name, category, version);
165 }
166 }
167 }
168
169 Err(IndexError::NotFound(format!(
170 "{} in {}",
171 name,
172 version.name()
173 )))
174 }
175}
176
177impl PackageIndex for Slackware {
178 fn ecosystem(&self) -> &'static str {
179 "slackware"
180 }
181
182 fn display_name(&self) -> &'static str {
183 "SlackBuilds.org"
184 }
185
186 fn fetch(&self, name: &str) -> Result<PackageMeta, IndexError> {
187 for &version in &self.versions {
189 match Self::fetch_from_version(name, version) {
190 Ok(pkg) => return Ok(pkg),
191 Err(IndexError::NotFound(_)) => continue,
192 Err(e) => return Err(e),
193 }
194 }
195
196 Err(IndexError::NotFound(name.to_string()))
197 }
198
199 fn fetch_versions(&self, name: &str) -> Result<Vec<VersionMeta>, IndexError> {
200 let mut all_versions = Vec::new();
201
202 for &version in &self.versions {
204 if let Ok(pkg) = Self::fetch_from_version(name, version) {
205 all_versions.push(VersionMeta {
206 version: format!("{} ({})", pkg.version, version.name()),
207 released: None,
208 yanked: false,
209 });
210 }
211 }
212
213 if all_versions.is_empty() {
214 return Err(IndexError::NotFound(name.to_string()));
215 }
216
217 Ok(all_versions)
218 }
219
220 fn search(&self, query: &str) -> Result<Vec<PackageMeta>, IndexError> {
221 Err(IndexError::Network(format!(
224 "SlackBuilds search not implemented via API. Use fetch() with exact package name, or visit: {}/result/?search={}",
225 Self::SBO_API,
226 query
227 )))
228 }
229}
230
231fn parse_sbo_info(
232 content: &str,
233 name: &str,
234 category: &str,
235 version: SlackwareVersion,
236) -> Result<PackageMeta, IndexError> {
237 let mut pkg_version = String::new();
238 let mut homepage = None;
239 let mut maintainer = None;
240 let mut email = None;
241
242 for line in content.lines() {
243 if let Some(val) = line.strip_prefix("VERSION=") {
244 pkg_version = val.trim_matches('"').to_string();
245 } else if let Some(val) = line.strip_prefix("HOMEPAGE=") {
246 homepage = Some(val.trim_matches('"').to_string());
247 } else if let Some(val) = line.strip_prefix("MAINTAINER=") {
248 maintainer = Some(val.trim_matches('"').to_string());
249 } else if let Some(val) = line.strip_prefix("EMAIL=") {
250 email = Some(val.trim_matches('"').to_string());
251 }
252 }
253
254 let maintainers = match (maintainer, email) {
255 (Some(m), Some(e)) => vec![format!("{} <{}>", m, e)],
256 (Some(m), None) => vec![m],
257 _ => Vec::new(),
258 };
259
260 let mut extra = HashMap::new();
261 extra.insert(
262 "source_repo".to_string(),
263 serde_json::Value::String(version.name().to_string()),
264 );
265 extra.insert(
266 "category".to_string(),
267 serde_json::Value::String(category.to_string()),
268 );
269
270 Ok(PackageMeta {
271 name: format!("{}/{}", category, name),
272 version: pkg_version,
273 description: None, homepage,
275 repository: Some(format!(
276 "https://github.com/SlackBuildsOrg/slackbuilds/tree/{}/{}/{}",
277 version.branch(),
278 category,
279 name
280 )),
281 license: None,
282 binaries: Vec::new(),
283 keywords: Vec::new(),
284 maintainers,
285 published: None,
286 downloads: None,
287 archive_url: None,
288 checksum: None,
289 extra,
290 })
291}