1use crate::Result;
4use serde::{Deserialize, Serialize};
5
6#[async_trait::async_trait]
8pub trait VersionFetcher: Send + Sync {
9 fn tool_name(&self) -> &str;
11
12 async fn fetch_versions(&self, include_prerelease: bool) -> Result<Vec<VersionInfo>>;
14
15 async fn get_latest_version(&self) -> Result<Option<VersionInfo>> {
17 let versions = self.fetch_versions(false).await?;
18 Ok(versions.into_iter().next())
19 }
20
21 async fn get_latest_version_including_prerelease(&self) -> Result<Option<VersionInfo>> {
23 let versions = self.fetch_versions(true).await?;
24 Ok(versions.into_iter().next())
25 }
26
27 async fn version_exists(&self, version: &str) -> Result<bool> {
29 let versions = self.fetch_versions(true).await?;
30 Ok(versions.iter().any(|v| v.version == version))
31 }
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct VersionInfo {
37 pub version: String,
39
40 pub is_prerelease: bool,
42
43 pub release_date: Option<String>,
45
46 pub release_notes: Option<String>,
48
49 pub download_url: Option<String>,
51
52 pub checksum: Option<String>,
54
55 pub file_size: Option<u64>,
57
58 pub metadata: std::collections::HashMap<String, String>,
60}
61
62impl VersionInfo {
63 pub fn new(version: String) -> Self {
65 Self {
66 version,
67 is_prerelease: false,
68 release_date: None,
69 release_notes: None,
70 download_url: None,
71 checksum: None,
72 file_size: None,
73 metadata: std::collections::HashMap::new(),
74 }
75 }
76
77 pub fn with_prerelease(mut self, is_prerelease: bool) -> Self {
79 self.is_prerelease = is_prerelease;
80 self
81 }
82
83 pub fn with_release_date(mut self, date: String) -> Self {
85 self.release_date = Some(date);
86 self
87 }
88
89 pub fn with_download_url(mut self, url: String) -> Self {
91 self.download_url = Some(url);
92 self
93 }
94
95 pub fn with_release_notes(mut self, notes: String) -> Self {
97 self.release_notes = Some(notes);
98 self
99 }
100
101 pub fn with_metadata(mut self, key: String, value: String) -> Self {
103 self.metadata.insert(key, value);
104 self
105 }
106}
107
108pub mod version_utils {
110 use super::VersionInfo;
111
112 pub fn sort_versions_desc(mut versions: Vec<VersionInfo>) -> Vec<VersionInfo> {
114 versions.sort_by(|a, b| {
115 b.version.cmp(&a.version)
118 });
119 versions
120 }
121
122 pub fn filter_stable_only(versions: Vec<VersionInfo>) -> Vec<VersionInfo> {
124 versions.into_iter().filter(|v| !v.is_prerelease).collect()
125 }
126
127 pub fn take_latest(versions: Vec<VersionInfo>, count: usize) -> Vec<VersionInfo> {
129 versions.into_iter().take(count).collect()
130 }
131}