use std::{
path::Path,
sync::{mpsc::Sender, Arc},
};
use crate::bundle::Manifest;
use crate::*;
mod file;
mod flow;
mod gitea;
mod github;
mod gitlab;
mod http;
pub use file::FileSource;
pub use flow::VelopackFlowSource;
pub use gitea::GiteaSource;
pub use github::GithubSource;
pub use gitlab::GitlabSource;
pub use http::HttpSource;
pub trait UpdateSource: Send + Sync {
fn get_release_feed(&self, channel: &str, app: &bundle::Manifest, staged_user_id: &str) -> Result<VelopackAssetFeed, Error>;
fn download_release_entry(&self, asset: &VelopackAsset, local_file: &Path, progress_sender: Option<Sender<i16>>) -> Result<(), Error>;
}
#[derive(Clone)]
pub struct NoneSource {}
impl UpdateSource for NoneSource {
fn get_release_feed(&self, _channel: &str, _app: &Manifest, _staged_user_id: &str) -> Result<VelopackAssetFeed, Error> {
Err(Error::NotSupported("None source does not checking release feed".to_owned()))
}
fn download_release_entry(&self, _asset: &VelopackAsset, _local_file: &Path, _progress_sender: Option<Sender<i16>>) -> Result<(), Error> {
Err(Error::NotSupported("None source does not support downloads".to_owned()))
}
}
pub struct AutoSource {
source: Arc<dyn UpdateSource>,
}
impl Clone for AutoSource {
fn clone(&self) -> Self {
AutoSource {
source: Arc::clone(&self.source),
}
}
}
impl AutoSource {
pub fn new(input: &str) -> AutoSource {
let source: Arc<dyn UpdateSource> = if Self::is_http_url(input) {
if let Ok(url) = url::Url::parse(input) {
if let Some(host) = url.host_str() {
if host.eq_ignore_ascii_case("github.com") {
Arc::new(GithubSource::new(input, None, false))
} else if host.eq_ignore_ascii_case("gitlab.com") {
Arc::new(GitlabSource::new(input, None, false))
} else if host.eq_ignore_ascii_case("gitea.com") {
Arc::new(GiteaSource::new(input, None, false))
} else {
Arc::new(HttpSource::new(input))
}
} else {
Arc::new(HttpSource::new(input))
}
} else {
Arc::new(HttpSource::new(input))
}
} else {
Arc::new(FileSource::new(input))
};
AutoSource { source }
}
fn is_http_url(url: &str) -> bool {
match url::Url::parse(url) {
Ok(url) => url.scheme().eq_ignore_ascii_case("http") || url.scheme().eq_ignore_ascii_case("https"),
_ => false,
}
}
}
impl UpdateSource for AutoSource {
fn get_release_feed(&self, channel: &str, app: &bundle::Manifest, staged_user_id: &str) -> Result<VelopackAssetFeed, Error> {
self.source.get_release_feed(channel, app, staged_user_id)
}
fn download_release_entry(&self, asset: &VelopackAsset, local_file: &Path, progress_sender: Option<Sender<i16>>) -> Result<(), Error> {
self.source.download_release_entry(asset, local_file, progress_sender)
}
}
fn get_git_release_feed<F>(channel: &str, headers: &[(&str, &str)], release_count: usize, get_asset_url: F) -> Result<VelopackAssetFeed, Error>
where
F: Fn(usize, &str) -> Result<String, Error>,
{
let releases_file_name = format!("releases.{}.json", channel);
let mut all_assets: Vec<VelopackAsset> = Vec::new();
for i in 0..release_count {
let asset_url = match get_asset_url(i, &releases_file_name) {
Ok(url) => url,
Err(e) => {
trace!("Skipping release {}: {}", i, e);
continue;
}
};
match download::download_url_as_string_with_headers(&asset_url, headers) {
Ok(json) => match serde_json::from_str::<VelopackAssetFeed>(&json) {
Ok(feed) => {
all_assets.extend(feed.Assets);
}
Err(e) => {
trace!("Failed to parse release feed from release {}: {}", i, e);
}
},
Err(e) => {
trace!("Failed to download release feed from release {}: {}", i, e);
}
}
}
Ok(VelopackAssetFeed { Assets: all_assets })
}
fn download_git_release_entry(
asset_url: &str,
headers: &[(&str, &str)],
local_file: &Path,
progress_sender: Option<Sender<i16>>,
) -> Result<(), Error> {
info!("About to download from URL '{}' to file '{:?}'", asset_url, local_file);
download::download_url_to_file_with_headers(asset_url, local_file, headers, move |p| {
if let Some(progress_sender) = &progress_sender {
let _ = progress_sender.send(p);
}
})?;
Ok(())
}