/update-informer

Update informer for CLI applications written in Rust 🦀

Primary LanguageRustMIT LicenseMIT

Update-informer

CI Version Docs.rs Codecov Downloads

update-informer

Update informer for CLI applications written in Rust 🦀

It checks for a new version on Crates.io, GitHub, Npm and PyPI 🚀

Benefits

Idea

The idea is actually not new. This feature has long been present in the GitHub CLI application and npm.
There is also a popular JavaScript library.

Usage

Add update-informer to Cargo.toml:

[dependencies]
update-informer = "0.6"

By default, update-informer can only check on Crates.io and uses ureq as a default HTTP client. To enable support for other registries or change the HTTP client, use features:

[dependencies]
update-informer = { version = "0.6", default_features = false, features = ["github", "reqwest"] }

Available features:

Name Type Default?
crates Registry Yes
github Registry No
npm Registry No
pypi Registry No
ureq HTTP client Yes
reqwest HTTP client No

Checking for a new version

To check for a new version, use the UpdateInformer::check_version function.
This function takes the project name and current version as well as registry:

use update_informer::{registry, Check};

let name = env!("CARGO_PKG_NAME");
let version = env!("CARGO_PKG_VERSION");
let informer = update_informer::new(registry::Crates, name, version);

if let Some(version) = informer.check_version().ok().flatten()  {
    println!("New version is available: {}", version);
}

More examples you can find [here].

Interval

Note that the first check will start only after the interval has expired. By default, the interval is 24 hours, but you can change it:

use std::time::Duration;
use update_informer::{registry, Check};

const EVERY_HOUR: Duration = Duration::from_secs(60 * 60);

let informer = update_informer::new(registry::Crates, "crate_name", "0.1.0").interval(EVERY_HOUR);
let _ = informer.check_version(); // The check will start only after an hour

Caching

By default, update-informer creates a file in the cache directory to avoid spam requests to the registry API.

In order not to cache requests, use a zero interval:

use std::time::Duration;
use update_informer::{registry, Check};

let informer = update_informer::new(registry::Crates, "crate_name", "0.1.0").interval(Duration::ZERO);
let _ = informer.check_version();

Request timeout

You can also change the request timeout. By default, it is 5 seconds:

use std::time::Duration;
use update_informer::{registry, Check};

const THIRTY_SECONDS: Duration = Duration::from_secs(30);

let informer = update_informer::new(registry::Crates, "crate_name", "0.1.0").timeout(THIRTY_SECONDS);
let _ = informer.check_version();

Implementing your own registry

You can implement your own registry to check updates. For example:

use update_informer::{http_client::{HttpClient, SendRequest}, registry, Check, Package, Registry, Result};

#[derive(serde::Deserialize)]
struct Response {
    version: String,
}

struct YourOwnRegistry;
impl Registry for YourOwnRegistry {
    const NAME: &'static str = "your_own_registry";

    fn get_latest_version<T: SendRequest>(http_client: HttpClient<T>, pkg: &Package) -> Result<Option<String>> {
        let url = "https://turbo.build/api/binaries/version";
        let resp = http_client.get::<Response>(&url)?;

        Ok(Some(resp.version))
    }
}

let informer = update_informer::new(YourOwnRegistry, "package_name", "0.1.0");
informer.check_version();

Using your own HTTP client

You can use your own HTTP client to check updates. For example:

use isahc::ReadResponseExt;
use std::time::Duration;
use serde::de::DeserializeOwned;
use update_informer::{http_client::SendRequest, registry, Check};

struct YourOwnHttpClient;

impl SendRequest for YourOwnHttpClient {
    fn get<T: DeserializeOwned>(
        url: &str,
        _timeout: Duration,
        _headers: Option<(&str, &str)>,
    ) -> update_informer::Result<T> {
        let json = isahc::get(url)?.json()?;
        Ok(json)
    }
}

let informer = update_informer::new(registry::Crates, "crate_name", "0.1.0").http_client(YourOwnHttpClient);
let _ = informer.check_version();

Tests

In order not to check for updates in tests, you can use the FakeUpdateInformer::check_version function, which returns the desired version:

use update_informer::{registry, Check};

let name = "crate_name";
let version = "0.1.0";

#[cfg(not(test))]
let informer = update_informer::new(registry::Crates, name, version);

#[cfg(test)]
let informer = update_informer::fake(registry::Crates, name, version, "1.0.0");

if let Some(version) = informer.check_version().ok().flatten() {
    println!("New version is available: {}", version);
}

Integration tests

To use the FakeUpdateInformer::check_version function in integration tests, you must first add the feature flag to Cargo.toml:

[features]
stub_check_version = []

Then use this feature flag in your code and integration tests:

use update_informer::{registry, Check};

let name = "crate_name";
let version = "0.1.0";

#[cfg(not(feature = "stub_check_version"))]
let informer = update_informer::new(registry::Crates, name, version);

#[cfg(feature = "stub_check_version")]
let informer = update_informer::fake(registry::Crates, name, version, "1.0.0");

let _ = informer.check_version();

Users

MSRV

Minimum Supported Rust Version: 1.56.1

Sponsors

update-informer is created & supported by Evrone

License

MIT