Skip to content

Commit

Permalink
feat: add http timeout
Browse files Browse the repository at this point in the history
Signed-off-by: Mikhail Grachev <work@mgrachev.com>
  • Loading branch information
mgrachev committed Feb 8, 2022
1 parent b8edbf0 commit 2cb94e1
Show file tree
Hide file tree
Showing 9 changed files with 63 additions and 19 deletions.
3 changes: 2 additions & 1 deletion examples/crates.rs
Expand Up @@ -5,7 +5,8 @@ fn main() {
let pkg_name = "update-informer";
let current_version = "0.1.0";

let informer = UpdateInformer::new(Crates, pkg_name, current_version, Duration::default());
let informer =
UpdateInformer::new(Crates, pkg_name, current_version).interval(Duration::default());

if let Ok(Some(new_version)) = informer.check_version() {
println!("A new release of {pkg_name} is available: v{current_version} -> {new_version}");
Expand Down
3 changes: 2 additions & 1 deletion examples/github.rs
Expand Up @@ -5,7 +5,8 @@ fn main() {
let pkg_name = "dotenv-linter/dotenv-linter";
let current_version = "3.1.0";

let informer = UpdateInformer::new(GitHub, pkg_name, current_version, Duration::default());
let informer =
UpdateInformer::new(GitHub, pkg_name, current_version).interval(Duration::default());

if let Ok(Some(new_version)) = informer.check_version() {
println!("A new release of {pkg_name} is available: v{current_version} -> {new_version}");
Expand Down
3 changes: 2 additions & 1 deletion examples/pypi.rs
Expand Up @@ -5,7 +5,8 @@ fn main() {
let pkg_name = "filprofiler";
let current_version = "2022.1.0";

let informer = UpdateInformer::new(PyPI, pkg_name, current_version, Duration::default());
let informer =
UpdateInformer::new(PyPI, pkg_name, current_version).interval(Duration::default());

if let Ok(Some(new_version)) = informer.check_version() {
println!("A new release of {pkg_name} is available: v{current_version} -> {new_version}");
Expand Down
26 changes: 26 additions & 0 deletions src/http_client.rs
@@ -0,0 +1,26 @@
use crate::Error;
use serde::de::DeserializeOwned;
use std::time::Duration;

pub(crate) struct HTTPClient {
request: ureq::Request,
}

impl HTTPClient {
pub(crate) fn new(url: &str, timeout: Duration) -> Self {
Self {
request: ureq::builder().timeout(timeout).build().request("GET", url),
}
}

pub(crate) fn add_header(mut self, key: &str, value: &str) -> Self {
self.request = self.request.set(key, value);
self
}

pub(crate) fn call<T: DeserializeOwned>(self) -> Result<T, Error> {
let json = self.request.call()?.into_json()?;

Ok(json)
}
}
18 changes: 15 additions & 3 deletions src/lib.rs
Expand Up @@ -114,12 +114,14 @@
//! informer.check_version();
//! ```

use crate::http_client::HTTPClient;
use crate::package::Package;
use crate::registry::Registry;
use crate::version::Version;
use crate::version_file::VersionFile;
use std::time::Duration;

mod http_client;
mod package;
mod version;
mod version_file;
Expand All @@ -145,6 +147,7 @@ pub struct UpdateInformer<R: Registry, N: AsRef<str>, V: AsRef<str>> {
name: N,
version: V,
interval: Duration,
timeout: Duration,
}

impl<R: Registry, N: AsRef<str>, V: AsRef<str>> UpdateInformer<R, N, V> {
Expand All @@ -167,14 +170,23 @@ impl<R: Registry, N: AsRef<str>, V: AsRef<str>> UpdateInformer<R, N, V> {
/// let version = env!("CARGO_PKG_VERSION");
/// let informer = UpdateInformer::new(Crates, name, version, Duration::from_secs(60 * 60 * 24));
/// ```
pub fn new(registry: R, name: N, version: V, interval: Duration) -> Self {
pub fn new(registry: R, name: N, version: V) -> Self {
Self {
_registry: registry,
name,
version,
interval,
interval: Duration::from_secs(60 * 60 * 24), // Once a day
timeout: Duration::from_secs(5),
}
}

pub fn interval(self, interval: Duration) -> Self {
Self { interval, ..self }
}

pub fn timeout(self, timeout: Duration) -> Self {
Self { timeout, ..self }
}
}

impl<R: Registry, N: AsRef<str>, V: AsRef<str>> Check for UpdateInformer<R, N, V> {
Expand Down Expand Up @@ -221,7 +233,7 @@ impl<R: Registry, N: AsRef<str>, V: AsRef<str>> Check for UpdateInformer<R, N, V
// This is needed to update mtime of the file
latest_version_file.recreate_file()?;

match R::get_latest_version(&pkg)? {
match R::get_latest_version(&pkg, self.timeout)? {
Some(v) => {
latest_version_file.write_version(&v)?;
v
Expand Down
3 changes: 2 additions & 1 deletion src/registry.rs
@@ -1,4 +1,5 @@
use crate::{Error, Package};
use std::time::Duration;

pub use crates::Crates;
pub use github::GitHub;
Expand All @@ -14,5 +15,5 @@ pub trait Registry {
/// # Arguments
///
/// * `pkg` - A `Package` struct.
fn get_latest_version(pkg: &Package) -> Result<Option<String>, Error>;
fn get_latest_version(pkg: &Package, timeout: Duration) -> Result<Option<String>, Error>;
}
7 changes: 4 additions & 3 deletions src/registry/crates.rs
@@ -1,6 +1,7 @@
use crate::registry::Registry;
use crate::{Error, Package};
use crate::{Error, HTTPClient, Package};
use serde::Deserialize;
use std::time::Duration;

#[cfg(test)]
use mockito;
Expand Down Expand Up @@ -32,10 +33,10 @@ fn get_base_url() -> String {
}

impl Registry for Crates {
fn get_latest_version(pkg: &Package) -> Result<Option<String>, Error> {
fn get_latest_version(pkg: &Package, timeout: Duration) -> Result<Option<String>, Error> {
let url = format!("{}/{}/versions", get_base_url(), pkg);

let resp: Response = ureq::get(&url).call()?.into_json()?;
let resp: Response = HTTPClient::new(&url, timeout).call()?;

if let Some(v) = resp.versions.first() {
return Ok(Some(v.num.clone()));
Expand Down
12 changes: 6 additions & 6 deletions src/registry/github.rs
@@ -1,6 +1,7 @@
use crate::registry::Registry;
use crate::{Error, Package};
use crate::{Error, HTTPClient, Package};
use serde::Deserialize;
use std::time::Duration;

#[cfg(test)]
use mockito;
Expand All @@ -27,13 +28,12 @@ fn get_base_url() -> String {
}

impl Registry for GitHub {
fn get_latest_version(pkg: &Package) -> Result<Option<String>, Error> {
fn get_latest_version(pkg: &Package, timeout: Duration) -> Result<Option<String>, Error> {
let url = format!("{}/{}/releases/latest", get_base_url(), pkg);

let resp: Response = ureq::get(&url)
.set("Accept", "application/vnd.github.v3+json")
.call()?
.into_json()?;
let resp: Response = HTTPClient::new(&url, timeout)
.add_header("Accept", "application/vnd.github.v3+json")
.call()?;

if resp.tag_name.starts_with('v') {
return Ok(Some(resp.tag_name[1..].to_string()));
Expand Down
7 changes: 4 additions & 3 deletions src/registry/pypi.rs
@@ -1,6 +1,7 @@
use crate::registry::Registry;
use crate::{Error, Package};
use crate::{Error, HTTPClient, Package};
use serde::Deserialize;
use std::time::Duration;

#[cfg(test)]
use mockito;
Expand Down Expand Up @@ -30,10 +31,10 @@ fn get_base_url() -> String {
}

impl Registry for PyPI {
fn get_latest_version(pkg: &Package) -> Result<Option<String>, Error> {
fn get_latest_version(pkg: &Package, timeout: Duration) -> Result<Option<String>, Error> {
let url = format!("{}/{}/json", get_base_url(), pkg);

let resp: Response = ureq::get(&url).call()?.into_json()?;
let resp: Response = HTTPClient::new(&url, timeout).call()?;

if !resp.info.yanked {
return Ok(Some(resp.info.version));
Expand Down

0 comments on commit 2cb94e1

Please sign in to comment.