Skip to main content

update_kit/checker/sources/
mod.rs

1pub mod brew_api;
2pub mod custom_manifest;
3pub mod github;
4pub mod jsr;
5pub mod npm_registry;
6
7use crate::config::VersionSourceConfig;
8use crate::errors::UpdateKitError;
9use crate::types::AssetInfo;
10use serde::{Deserialize, Serialize};
11
12/// Information about a specific version.
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct VersionInfo {
15    pub version: String,
16    pub release_url: Option<String>,
17    pub release_notes: Option<String>,
18    pub assets: Option<Vec<AssetInfo>>,
19    pub published_at: Option<String>,
20}
21
22/// Result of fetching the latest version from a source.
23#[derive(Debug)]
24pub enum VersionSourceResult {
25    Found {
26        info: VersionInfo,
27        etag: Option<String>,
28    },
29    NotModified {
30        etag: String,
31    },
32    Error {
33        reason: String,
34        status: Option<u16>,
35    },
36}
37
38/// Options for fetching the latest version.
39#[derive(Debug, Default)]
40pub struct FetchOptions {
41    pub etag: Option<String>,
42}
43
44/// Options for fetching a list of versions.
45#[derive(Debug, Default, Clone)]
46pub struct FetchVersionsOptions {
47    pub limit: Option<usize>,
48    pub cursor: Option<String>,
49}
50
51/// Result of fetching a list of versions.
52#[derive(Debug)]
53pub enum VersionListResult {
54    Success {
55        versions: Vec<VersionInfo>,
56        next_cursor: Option<String>,
57        total_count: Option<usize>,
58    },
59    Error {
60        reason: String,
61    },
62}
63
64/// Trait for version sources that can check for updates.
65#[async_trait::async_trait]
66pub trait VersionSource: Send + Sync {
67    /// Returns the name of this source (e.g. "github", "npm").
68    fn name(&self) -> &str;
69
70    /// Fetch the latest version from this source.
71    async fn fetch_latest(&self, options: FetchOptions) -> VersionSourceResult;
72
73    /// Fetch a paginated list of versions. Not all sources support this.
74    async fn fetch_versions(
75        &self,
76        _options: FetchVersionsOptions,
77    ) -> Result<VersionListResult, UpdateKitError> {
78        Err(UpdateKitError::UnsupportedOperation(
79            "fetch_versions not supported".into(),
80        ))
81    }
82}
83
84/// Create a version source from a configuration variant.
85pub fn create_version_source(config: VersionSourceConfig) -> Box<dyn VersionSource> {
86    match config {
87        VersionSourceConfig::Github {
88            owner,
89            repo,
90            token,
91            api_base_url,
92        } => Box::new(github::GitHubReleasesSource::new(
93            owner,
94            repo,
95            token,
96            api_base_url,
97        )),
98        VersionSourceConfig::Npm {
99            package_name,
100            registry_url,
101        } => Box::new(npm_registry::NpmRegistrySource::new(
102            package_name,
103            registry_url,
104        )),
105        VersionSourceConfig::Jsr { scope, name } => {
106            Box::new(jsr::JsrSource::new(scope, name))
107        }
108        VersionSourceConfig::Brew { cask_name } => {
109            Box::new(brew_api::BrewSource::new(cask_name))
110        }
111        VersionSourceConfig::Custom { url, version_field } => {
112            Box::new(custom_manifest::CustomManifestSource::new(url, version_field))
113        }
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120
121    #[test]
122    fn test_create_github_source() {
123        let source = create_version_source(VersionSourceConfig::Github {
124            owner: "user".into(),
125            repo: "repo".into(),
126            token: None,
127            api_base_url: None,
128        });
129        assert_eq!(source.name(), "github");
130    }
131
132    #[test]
133    fn test_create_npm_source() {
134        let source = create_version_source(VersionSourceConfig::Npm {
135            package_name: "my-pkg".into(),
136            registry_url: None,
137        });
138        assert_eq!(source.name(), "npm");
139    }
140
141    #[test]
142    fn test_create_jsr_source() {
143        let source = create_version_source(VersionSourceConfig::Jsr {
144            scope: "scope".into(),
145            name: "pkg".into(),
146        });
147        assert_eq!(source.name(), "jsr");
148    }
149
150    #[test]
151    fn test_create_brew_source() {
152        let source = create_version_source(VersionSourceConfig::Brew {
153            cask_name: "my-cask".into(),
154        });
155        assert_eq!(source.name(), "brew");
156    }
157
158    #[test]
159    fn test_create_custom_source() {
160        let source = create_version_source(VersionSourceConfig::Custom {
161            url: "https://example.com/version.json".into(),
162            version_field: Some("version".into()),
163        });
164        assert_eq!(source.name(), "custom");
165    }
166}