normalize_package_index/index/
go.rs1use super::{IndexError, PackageIndex, PackageMeta, VersionMeta};
12
13pub struct Go;
15
16impl Go {
17 const GO_PROXY: &'static str = "https://proxy.golang.org";
19
20 const PKG_GO_DEV: &'static str = "https://pkg.go.dev";
22}
23
24impl PackageIndex for Go {
25 fn ecosystem(&self) -> &'static str {
26 "go"
27 }
28
29 fn display_name(&self) -> &'static str {
30 "Go Modules"
31 }
32
33 fn fetch(&self, name: &str) -> Result<PackageMeta, IndexError> {
34 let versions = self.fetch_versions(name)?;
36 let latest = versions
37 .first()
38 .ok_or_else(|| IndexError::NotFound(name.to_string()))?;
39
40 let repository = if name.starts_with("github.com/") {
42 Some(format!("https://{}", name))
43 } else if name.starts_with("golang.org/x/") {
44 let pkg_name = name.strip_prefix("golang.org/x/").unwrap_or(name);
46 Some(format!("https://github.com/golang/{}", pkg_name))
47 } else {
48 None
49 };
50
51 let info_url = format!("{}/{}/@v/{}.info", Self::GO_PROXY, name, latest.version);
53 let published = ureq::get(&info_url)
54 .call()
55 .ok()
56 .and_then(|r| r.into_json::<serde_json::Value>().ok())
57 .and_then(|v| v["Time"].as_str().map(String::from));
58
59 Ok(PackageMeta {
60 name: name.to_string(),
61 version: latest.version.clone(),
62 description: None, homepage: Some(format!("{}/{}", Self::PKG_GO_DEV, name)),
64 repository,
65 license: None, binaries: Vec::new(),
67 keywords: Vec::new(),
68 maintainers: Vec::new(),
69 published,
70 downloads: None, archive_url: Some(format!(
72 "{}/{}/@v/{}.zip",
73 Self::GO_PROXY,
74 name,
75 latest.version
76 )),
77 checksum: None, extra: Default::default(),
79 })
80 }
81
82 fn fetch_versions(&self, name: &str) -> Result<Vec<VersionMeta>, IndexError> {
83 let url = format!("{}/{}/@v/list", Self::GO_PROXY, name);
84 let response = ureq::get(&url).call()?.into_string()?;
85
86 let mut versions: Vec<VersionMeta> = response
87 .lines()
88 .filter(|line| !line.is_empty())
89 .map(|version| VersionMeta {
90 version: version.to_string(),
91 released: None,
92 yanked: false,
93 })
94 .collect();
95
96 versions.sort_by(|a, b| b.version.cmp(&a.version));
98
99 if versions.is_empty() {
100 return Err(IndexError::NotFound(name.to_string()));
101 }
102
103 Ok(versions)
104 }
105
106 fn search(&self, query: &str) -> Result<Vec<PackageMeta>, IndexError> {
107 Err(IndexError::Network(format!(
110 "Go module search not available via API. Visit: {}/search?q={}",
111 Self::PKG_GO_DEV,
112 query
113 )))
114 }
115}