use super::update_config_builder as builder;
use crate::github::{Error, Release, UpdateConfig, UpdateConfigBuilder, UpdateInfo};
use crate::http::AsyncHttp;
pub struct AsyncUpdateChecker {
inner: UpdateConfig,
}
impl AsyncUpdateChecker {
pub fn new(config: UpdateConfig) -> Self {
Self { inner: config }
}
pub async fn fetch_latest(&self) -> Result<Release, Error> {
let res = AsyncHttp::get(
self.inner.client_builder(),
self.inner.releases_latest_path(),
)
.await?;
let status = res.status();
let body = res.bytes().await?;
Release::parse(status, body)
}
pub async fn fetch(&self, n: usize) -> Result<Vec<Release>, Error> {
debug_assert!(n <= 100);
let res = AsyncHttp::get(
self.inner.client_builder(),
self.inner.releases_path(n as u8),
)
.await?;
let status = res.status();
let body = res.bytes().await?;
Release::parse_array(status, body)
}
pub async fn fetch_latest_prerelease(&self) -> Result<Release, Error> {
let releases = self.fetch(1).await?;
releases.into_iter().next().ok_or(Error::NO_RELEASE)
}
pub async fn check(&self) -> Result<UpdateInfo, Error> {
let release = self.fetch_latest().await?;
Ok(UpdateInfo::new(
self.inner.current_version.as_deref(),
release,
))
}
pub async fn check_prerelease(&self, prerelease: bool) -> Result<UpdateInfo, Error> {
let release = if prerelease {
self.fetch_latest_prerelease().await
} else {
self.fetch_latest().await
}?;
Ok(UpdateInfo::new(
self.inner.current_version.as_deref(),
release,
))
}
}
impl<S: builder::State> UpdateConfigBuilder<S> {
pub fn build_async(self) -> AsyncUpdateChecker
where
S: builder::IsComplete,
{
let config = self.build();
AsyncUpdateChecker::new(config)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::github::tests::{TEST_OWNER, TEST_REPO};
#[test]
fn builder_and_check() {
futures::executor::block_on(async {
let info = UpdateConfig::builder()
.owner(TEST_OWNER)
.repo(TEST_REPO)
.current_version("0.1.0")
.build_async()
.check()
.await
.unwrap();
dbg!(&info);
assert!(info.has_update());
assert!(!info.latest().assets.is_empty());
});
}
#[test]
fn fetch_latest() {
futures::executor::block_on(async {
let release = UpdateConfig::builder()
.owner(TEST_OWNER)
.repo(TEST_REPO)
.build_async()
.fetch_latest()
.await
.unwrap();
assert!(
release.tag.starts_with('v'),
"tag should start with 'v', got: {}",
release.tag
);
assert!(release.name.is_some(), "release name should be Some");
assert!(!release.name.as_ref().unwrap().is_empty());
assert!(!release.assets.is_empty(), "release should have assets");
for asset in &release.assets {
assert!(!asset.name.is_empty());
assert!(
asset.download_url.starts_with("https://"),
"download URL should be HTTPS: {}",
asset.download_url
);
}
assert!(
release.html_url.contains("Chaoses-Ib/IbEverythingExt"),
"html_url should contain the repo path"
);
assert!(release.body.is_some(), "release body should be Some");
assert!(!release.body.as_ref().unwrap().is_empty());
});
}
#[test]
fn fetch() {
futures::executor::block_on(async {
let releases = UpdateConfig::builder()
.owner(TEST_OWNER)
.repo(TEST_REPO)
.build_async()
.fetch(5)
.await
.unwrap();
assert!(!releases.is_empty(), "should have at least one release");
for release in &releases {
assert!(!release.tag.is_empty());
assert!(release.html_url.contains("Chaoses-Ib/IbEverythingExt"));
}
});
}
#[test]
fn fetch_latest_prerelease() {
futures::executor::block_on(async {
let release = UpdateConfig::builder()
.owner(TEST_OWNER)
.repo(TEST_REPO)
.build_async()
.fetch_latest_prerelease()
.await
.unwrap();
assert!(!release.tag.is_empty());
assert!(release.html_url.contains("Chaoses-Ib/IbEverythingExt"));
});
}
#[test]
fn current_version() {
futures::executor::block_on(async {
let latest = UpdateConfig::builder()
.owner(TEST_OWNER)
.repo(TEST_REPO)
.build_async()
.fetch_latest()
.await
.unwrap();
let info = UpdateConfig::builder()
.owner(TEST_OWNER)
.repo(TEST_REPO)
.current_version(&latest.tag)
.build_async()
.check()
.await
.unwrap();
assert!(
!info.has_update(),
"checking against the latest tag ({}) should not need an update",
latest.tag
);
});
}
}