use crate::{UpdateConfig, UpdateError, UpdateResult};
use log::{info, error};
use self_update::backends::github;
use semver;
pub struct AutoUpdater {
config: UpdateConfig,
}
impl AutoUpdater {
pub fn new(config: UpdateConfig) -> Self {
Self { config }
}
pub fn with_default_config() -> Self {
Self::new(UpdateConfig::default())
}
pub async fn check_for_updates(&self) -> UpdateResult<bool> {
info!("检查更新中...");
match self.check_github_updates().await {
Ok(has_update) => Ok(has_update),
Err(e) => {
error!("GitHub 检查更新失败: {}", e);
Err(e)
}
}
}
pub async fn update_with_fallback(&self) -> UpdateResult<()> {
info!("开始更新应用程序...");
match self.update_from_github().await {
Ok(_) => {
info!("从 GitHub 更新成功");
Ok(())
}
Err(e) => {
error!("GitHub 更新失败: {}", e);
Err(e)
}
}
}
async fn check_github_updates(&self) -> UpdateResult<bool> {
info!("正在检查 GitHub 更新...");
let target = self_update::get_target().map_err(UpdateError::from)?;
let releases = github::ReleaseList::configure()
.repo_owner(&self.config.github_owner)
.repo_name(&self.config.github_repo)
.with_target(&target)
.build()
.map_err(UpdateError::from)?
.fetch()
.map_err(UpdateError::from)?;
if let Some(latest_release) = releases.first() {
let current_version = &self.config.current_version;
let release_version = latest_release.tag.trim_start_matches('v');
let current_clean = current_version.trim_start_matches('v');
info!("当前版本: {}, 最新版本: {}", current_version, latest_release.tag);
Ok(release_version != current_clean)
} else {
error!("未找到任何发布版本");
Err(UpdateError::GitHub("未找到任何发布版本".to_string()))
}
}
async fn update_from_github(&self) -> UpdateResult<()> {
info!("正在从 GitHub 下载更新...");
let target = self_update::get_target().map_err(UpdateError::from)?;
let update = github::Update::configure()
.map_err(UpdateError::from)?
.repo_owner(&self.config.github_owner)
.repo_name(&self.config.github_repo)
.target(&target)
.bin_name(&self.config.bin_name)
.current_version(&self.config.current_version)
.show_download_progress(false) .no_confirm(true) .build()
.map_err(UpdateError::from)?;
let status = update.update()
.map_err(UpdateError::from)?;
match status.updated() {
true => {
info!("更新成功,新版本: {}", status.version());
Ok(())
}
false => {
info!("已是最新版本");
Ok(())
}
}
}
pub fn current_version(&self) -> &str {
&self.config.current_version
}
pub fn set_config(&mut self, config: UpdateConfig) {
self.config = config;
}
pub fn config(&self) -> &UpdateConfig {
&self.config
}
pub fn get_download_links(&self) -> (String, String) {
self.config.download_links()
}
pub fn get_latest_release_info(&self) -> UpdateResult<Option<(String, String)>> {
info!("获取最新发布版本信息...");
let target = self_update::get_target().map_err(UpdateError::from)?;
let releases = github::ReleaseList::configure()
.repo_owner(&self.config.github_owner)
.repo_name(&self.config.github_repo)
.with_target(&target)
.build()
.map_err(UpdateError::from)?
.fetch()
.map_err(UpdateError::from)?;
if let Some(latest_release) = releases.first() {
Ok(Some((latest_release.tag.clone(), latest_release.name.clone())))
} else {
Ok(None)
}
}
pub fn needs_update(&self) -> UpdateResult<bool> {
info!("检查是否需要更新...");
let target = self_update::get_target().map_err(UpdateError::from)?;
let releases = github::ReleaseList::configure()
.repo_owner(&self.config.github_owner)
.repo_name(&self.config.github_repo)
.with_target(&target)
.build()
.map_err(UpdateError::from)?
.fetch()
.map_err(UpdateError::from)?;
if let Some(latest_release) = releases.first() {
let current_version = self.config.current_version.trim_start_matches('v');
let latest_version = latest_release.tag.trim_start_matches('v');
match (semver::Version::parse(current_version), semver::Version::parse(latest_version)) {
(Ok(current), Ok(latest)) => {
info!("当前版本: v{}, 最新版本: v{}", current, latest);
Ok(latest > current)
}
_ => {
info!("使用字符串比较: 当前版本: {}, 最新版本: {}", current_version, latest_version);
Ok(latest_version != current_version)
}
}
} else {
error!("未找到任何发布版本");
Err(UpdateError::GitHub("未找到任何发布版本".to_string()))
}
}
pub fn sync_update(&self) -> UpdateResult<()> {
info!("开始同步更新应用程序...");
let target = self_update::get_target().map_err(UpdateError::from)?;
let update = github::Update::configure()
.map_err(UpdateError::from)?
.repo_owner(&self.config.github_owner)
.repo_name(&self.config.github_repo)
.target(&target)
.bin_name(&self.config.bin_name)
.current_version(&self.config.current_version)
.show_download_progress(false) .no_confirm(true) .build()
.map_err(UpdateError::from)?;
let status = update.update()
.map_err(UpdateError::from)?;
match status.updated() {
true => {
info!("更新成功,新版本: {}", status.version());
Ok(())
}
false => {
info!("已是最新版本");
Ok(())
}
}
}
pub fn sync_check_for_updates(&self) -> UpdateResult<bool> {
info!("同步检查更新中...");
let target = self_update::get_target().map_err(UpdateError::from)?;
let releases = github::ReleaseList::configure()
.repo_owner(&self.config.github_owner)
.repo_name(&self.config.github_repo)
.with_target(&target)
.build()
.map_err(UpdateError::from)?
.fetch()
.map_err(UpdateError::from)?;
if let Some(latest_release) = releases.first() {
let current_version = &self.config.current_version;
let release_version = latest_release.tag.trim_start_matches('v');
let current_clean = current_version.trim_start_matches('v');
info!("当前版本: {}, 最新版本: {}", current_version, latest_release.tag);
Ok(release_version != current_clean)
} else {
error!("未找到任何发布版本");
Err(UpdateError::GitHub("未找到任何发布版本".to_string()))
}
}
}
impl Clone for AutoUpdater {
fn clone(&self) -> Self {
Self {
config: self.config.clone(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_auto_updater_creation() {
let updater = AutoUpdater::with_default_config();
assert_eq!(updater.current_version(), std::env!("CARGO_PKG_VERSION"));
}
#[test]
fn test_get_download_links() {
let updater = AutoUpdater::with_default_config();
let (github_url, gitee_url) = updater.get_download_links();
assert_eq!(github_url, "https://github.com/burncloud/burncloud/releases");
assert_eq!(gitee_url, "https://gitee.com/burncloud/burncloud/releases");
}
#[test]
fn test_update_config_customization() {
let mut config = UpdateConfig::default();
config.current_version = "1.0.0".to_string();
let mut updater = AutoUpdater::new(config);
assert_eq!(updater.current_version(), "1.0.0");
let new_config = UpdateConfig {
current_version: "2.0.0".to_string(),
..UpdateConfig::default()
};
updater.set_config(new_config);
assert_eq!(updater.current_version(), "2.0.0");
}
#[test]
fn test_clone() {
let updater = AutoUpdater::with_default_config();
let cloned = updater.clone();
assert_eq!(updater.current_version(), cloned.current_version());
assert_eq!(updater.config().github_owner, cloned.config().github_owner);
}
}