asimov_module_cli/registry/
pypi.rs1use super::{ModuleMetadata, ModuleType, http::http_client};
4use known_types_pypi::PackageMetadata;
5use reqwest::Error;
6
7pub async fn fetch_current_modules() -> Result<String, Error> {
9 fetch_modules("25.0.0.dev0").await }
11
12pub async fn fetch_modules(version: &str) -> Result<String, Error> {
14 let url = format!("https://pypi.org/pypi/asimov-modules/{}/json", version);
15 http_client().get(&url).send().await?.text().await
16}
17
18pub fn extract_module_names(json_str: impl AsRef<str>) -> serde_json::Result<Vec<ModuleMetadata>> {
22 let package: PackageMetadata = serde_json::from_str(json_str.as_ref())?;
23
24 let Some(dependencies) = package.info.requires_dist else {
26 return Ok(Vec::new()); };
28
29 let module_names = dependencies
31 .into_iter()
32 .filter_map(|dep| {
33 let dep_name = dep
35 .split(|c| c == ' ' || c == '<' || c == '>' || c == ';' || c == '[')
36 .next()?;
37
38 if dep_name == "asimov-module" {
40 return None;
41 }
42
43 if dep_name.starts_with("asimov-") && dep_name.ends_with("-module") {
45 let mod_name = dep_name.strip_prefix("asimov-")?.strip_suffix("-module")?;
47
48 Some(ModuleMetadata {
49 name: mod_name.to_string(),
50 version: package.info.version.clone(),
51 r#type: ModuleType::Python,
52 url: format!("https://pypi.org/project/{}/", dep_name),
53 })
54 } else {
55 None
56 }
57 })
58 .collect();
59
60 Ok(module_names)
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66
67 #[test]
68 fn test_extract_module_names() {
69 let json = r#"{
70 "info": {
71 "name": "asimov-modules",
72 "version": "25.0.0.dev0",
73 "requires_dist": [
74 "asimov-mlx-module",
75 "asimov-gpu-module>=1.0.0",
76 "asimov-cpu-module; python_version >= '3.13'",
77 "numpy>=1.20.0",
78 "other-package"
79 ]
80 }
81 }"#;
82
83 let result: Vec<String> = extract_module_names(json)
84 .unwrap()
85 .iter()
86 .map(|m| m.name.clone())
87 .collect();
88 assert_eq!(result, vec!["mlx", "gpu", "cpu"]);
89 }
90
91 #[test]
92 fn test_no_dependencies() {
93 let json = r#"{
94 "info": {
95 "name": "asimov-modules",
96 "version": "25.0.0.dev0"
97 }
98 }"#;
99
100 let result = extract_module_names(json).unwrap();
101 assert!(result.is_empty());
102 }
103
104 #[test]
105 fn test_empty_dependencies() {
106 let json = r#"{
107 "info": {
108 "name": "asimov-modules",
109 "version": "25.0.0.dev0",
110 "requires_dist": []
111 }
112 }"#;
113
114 let result: Vec<String> = extract_module_names(json)
115 .unwrap()
116 .iter()
117 .map(|m| m.name.clone())
118 .collect();
119 assert!(result.is_empty());
120 }
121}