Skip to content

Commit

Permalink
*: migrate error handling to anyhow
Browse files Browse the repository at this point in the history
To follow error handling convention of anyhow, insert `.context` to add
detailed message of error context when returning from functions.

In case of an immediate error exit, use a `bail!` macro.

Note, main functions and unit tests stay untouched if possible.
  • Loading branch information
dongsupark committed Nov 9, 2023
1 parent 815aba4 commit 4068ce3
Show file tree
Hide file tree
Showing 10 changed files with 121 additions and 81 deletions.
23 changes: 15 additions & 8 deletions examples/full_test.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use std::error::Error;
use std::borrow::Cow;

use anyhow::{Context, Result};
use hard_xml::XmlRead;
use url::Url;

fn get_pkgs_to_download(resp: &omaha::Response) -> Result<Vec<(Url, omaha::Hash<omaha::Sha256>)>, Box<dyn Error>> {
fn get_pkgs_to_download(resp: &omaha::Response) -> Result<Vec<(Url, omaha::Hash<omaha::Sha256>)>> {
let mut to_download: Vec<(Url, omaha::Hash<_>)> = Vec::new();

for app in &resp.apps {
Expand Down Expand Up @@ -44,27 +45,33 @@ fn get_pkgs_to_download(resp: &omaha::Response) -> Result<Vec<(Url, omaha::Hash<
async fn main() -> Result<(), Box<dyn Error>> {
let client = reqwest::Client::new();

const APP_VERSION_DEFAULT: &str = "3340.0.0+nightly-20220823-2100";
const MACHINE_ID_DEFAULT: &str = "abce671d61774703ac7be60715220bfe";
const TRACK_DEFAULT: &str = "stable";

////
// request
////
let parameters = ue_rs::request::Parameters {
app_version: Cow::Borrowed("3340.0.0+nightly-20220823-2100"),
machine_id: Cow::Borrowed("abce671d61774703ac7be60715220bfe"),
app_version: Cow::Borrowed(APP_VERSION_DEFAULT),
machine_id: Cow::Borrowed(MACHINE_ID_DEFAULT),

track: Cow::Borrowed("stable"),
track: Cow::Borrowed(TRACK_DEFAULT),
};

let response_text = ue_rs::request::perform(&client, parameters).await?;
let response_text = ue_rs::request::perform(&client, parameters).await.context(format!(
"perform({APP_VERSION_DEFAULT}, {MACHINE_ID_DEFAULT}, {TRACK_DEFAULT}) failed"
))?;

println!("response:\n\t{:#?}", response_text);
println!();

////
// parse response
////
let resp = omaha::Response::from_str(&response_text)?;
let resp = omaha::Response::from_str(&response_text).context("failed to parse response")?;

let pkgs_to_dl = get_pkgs_to_download(&resp)?;
let pkgs_to_dl = get_pkgs_to_download(&resp).context("failed to get packages to download")?;

////
// download
Expand All @@ -76,7 +83,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
// std::io::BufWriter wrapping an std::fs::File is probably the right choice.
// std::io::sink() is basically just /dev/null
let data = std::io::sink();
let res = ue_rs::download_and_hash(&client, url, data).await?;
let res = ue_rs::download_and_hash(&client, url.clone(), data).await.context(format!("download_and_hash({url:?}) failed"))?;

println!("\texpected sha256: {}", expected_sha256);
println!("\tcalculated sha256: {}", res.hash);
Expand Down
16 changes: 12 additions & 4 deletions examples/request.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
use std::error::Error;
use std::borrow::Cow;

use anyhow::Context;

use ue_rs::request;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let client = reqwest::Client::new();

const APP_VERSION_DEFAULT: &str = "3340.0.0+nightly-20220823-2100";
const MACHINE_ID_DEFAULT: &str = "abce671d61774703ac7be60715220bfe";
const TRACK_DEFAULT: &str = "stable";

let parameters = request::Parameters {
app_version: Cow::Borrowed("3340.0.0+nightly-20220823-2100"),
machine_id: Cow::Borrowed("abce671d61774703ac7be60715220bfe"),
app_version: Cow::Borrowed(APP_VERSION_DEFAULT),
machine_id: Cow::Borrowed(MACHINE_ID_DEFAULT),

track: Cow::Borrowed("stable"),
track: Cow::Borrowed(TRACK_DEFAULT),
};

let response = request::perform(&client, parameters).await?;
let response = request::perform(&client, parameters).await.context(format!(
"perform({APP_VERSION_DEFAULT}, {MACHINE_ID_DEFAULT}, {TRACK_DEFAULT}) failed"
))?;

println!("response:\n\t{:#?}", response);

Expand Down
8 changes: 6 additions & 2 deletions examples/response.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::error::Error;

use anyhow::Context;
use hard_xml::XmlRead;
use omaha;

Expand Down Expand Up @@ -29,7 +30,7 @@ fn main() -> Result<(), Box<dyn Error>> {
println!("{}", RESPONSE_XML);
println!();

let resp = omaha::Response::from_str(RESPONSE_XML)?;
let resp = omaha::Response::from_str(RESPONSE_XML).context("failed to create response")?;

println!("{:#?}", resp);
println!();
Expand Down Expand Up @@ -66,7 +67,10 @@ fn main() -> Result<(), Box<dyn Error>> {
println!(" urls:");

for url in &app.update_check.urls {
println!(" {}", url.join(&pkg.name)?);
println!(
" {}",
url.join(&pkg.name).context(format!("failed to join URL with {:?}", pkg.name))?
);
}

println!();
Expand Down
8 changes: 4 additions & 4 deletions omaha/src/hash_types.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use std::fmt;
use std::str;

use anyhow::{Error as CodecError, anyhow};

#[rustfmt::skip]
use ct_codecs::{
Error as CodecError,

Base64,
Hex,

Expand Down Expand Up @@ -74,9 +74,9 @@ impl<T: HashAlgo> str::FromStr for Hash<T> {

impl<T: HashAlgo> Hash<T> {
#[inline]
fn decode<D: Decoder>(hash: &str) -> Result<Self, CodecError> {
fn decode<D: Decoder>(hash: &str) -> anyhow::Result<Self, CodecError> {
let mut digest = T::Output::default();
D::decode(digest.as_mut(), hash, None)?;
D::decode(digest.as_mut(), hash, None).map_err(|_| anyhow!("decode ({}) failed", hash))?;
Ok(Self(digest))
}

Expand Down
4 changes: 2 additions & 2 deletions omaha/src/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ use self::omaha::{Sha1, Sha256};
mod sha256_hex {
use crate as omaha;
use self::omaha::Sha256;
use ct_codecs::Error;
use anyhow::Error as CodecError;

#[inline]
pub(crate) fn from_str(s: &str) -> Result<omaha::Hash<Sha256>, Error> {
pub(crate) fn from_str(s: &str) -> Result<omaha::Hash<Sha256>, CodecError> {
<omaha::Hash<Sha256>>::from_hex(s)
}
}
Expand Down
39 changes: 24 additions & 15 deletions src/bin/download_sysext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use std::io;
#[macro_use]
extern crate log;

use anyhow::{Context, Result, bail};
use globset::{Glob, GlobSet, GlobSetBuilder};
use hard_xml::XmlRead;
use argh::FromArgs;
Expand Down Expand Up @@ -36,23 +37,29 @@ struct Package<'a> {

impl<'a> Package<'a> {
#[rustfmt::skip]
fn hash_on_disk(&mut self, path: &Path) -> Result<omaha::Hash<omaha::Sha256>, Box<dyn Error>> {
fn hash_on_disk(&mut self, path: &Path) -> Result<omaha::Hash<omaha::Sha256>> {
use sha2::{Sha256, Digest};

let mut file = File::open(path)?;
let mut file = File::open(path).context({
format!("open({}) failed", path.display())
})?;
let mut hasher = Sha256::new();

io::copy(&mut file, &mut hasher)?;
io::copy(&mut file, &mut hasher).context({
format!("copy({}) failed", path.display())
})?;

Ok(omaha::Hash::from_bytes(
hasher.finalize().into()
))
}

#[rustfmt::skip]
fn check_download(&mut self, in_dir: &Path) -> Result<(), Box<dyn Error>> {
fn check_download(&mut self, in_dir: &Path) -> Result<()> {
let path = in_dir.join(&*self.name);
let md = fs::metadata(&path)?;
let md = fs::metadata(&path).context({
format!("metadata({}) failed", path.display())
})?;

let size_on_disk = md.len() as usize;
let expected_size = self.size.bytes();
Expand All @@ -68,7 +75,9 @@ impl<'a> Package<'a> {

if size_on_disk == expected_size {
info!("{}: download complete, checking hash...", path.display());
let hash = self.hash_on_disk(&path)?;
let hash = self.hash_on_disk(&path).context({
format!("hash_on_disk({}) failed", path.display())
})?;
if self.verify_checksum(hash) {
info!("{}: good hash, will continue without re-download", path.display());
} else {
Expand All @@ -80,7 +89,7 @@ impl<'a> Package<'a> {
Ok(())
}

async fn download(&mut self, into_dir: &Path, client: &reqwest::Client) -> Result<(), Box<dyn Error>> {
async fn download(&mut self, into_dir: &Path, client: &reqwest::Client) -> Result<()> {
// FIXME: use _range_start for completing downloads
let _range_start = match self.status {
PackageStatus::ToDownload => 0,
Expand All @@ -91,9 +100,9 @@ impl<'a> Package<'a> {
info!("downloading {}...", self.url);

let path = into_dir.join(&*self.name);
let mut file = File::create(path)?;
let mut file = File::create(path.clone()).context(format!("create({}) failed", path.display()))?;

let res = ue_rs::download_and_hash(&client, self.url.clone(), &mut file).await?;
let res = ue_rs::download_and_hash(&client, self.url.clone(), &mut file).await.context(format!("download_and_hash({}) failed", self.url))?;

self.verify_checksum(res.hash);
Ok(())
Expand All @@ -113,23 +122,23 @@ impl<'a> Package<'a> {
}
}

fn verify_signature_on_disk(&mut self, from_path: &Path, pubkey_path: &str) -> Result<(), Box<dyn Error>> {
let upfile = File::open(from_path)?;
fn verify_signature_on_disk(&mut self, from_path: &Path, pubkey_path: &str) -> Result<()> {
let upfile = File::open(from_path).context(format!("open({}) failed", from_path.display()))?;

// Read update payload from file, read delta update header from the payload.
let res_data = fs::read_to_string(from_path);

let header = delta_update::read_delta_update_header(&upfile)?;
let header = delta_update::read_delta_update_header(&upfile).context(format!("read_delta_update_header({}) failed", from_path.display()))?;

// Extract signature from header.
let sigbytes = delta_update::get_signatures_bytes(&upfile, &header)?;
let sigbytes = delta_update::get_signatures_bytes(&upfile, &header).context(format!("get_signatures_bytes({}) failed", from_path.display()))?;

// Parse signature data from the signature containing data, version, special fields.
let _sigdata = match delta_update::parse_signature_data(res_data.unwrap().as_bytes(), &sigbytes, pubkey_path) {
Some(data) => data,
_ => {
self.status = PackageStatus::BadSignature;
return Err("unable to parse signature data".into());
bail!("unable to parse signature data");
}
};

Expand All @@ -142,7 +151,7 @@ impl<'a> Package<'a> {

#[rustfmt::skip]
fn get_pkgs_to_download<'a>(resp: &'a omaha::Response, glob_set: &GlobSet)
-> Result<Vec<Package<'a>>, Box<dyn Error>> {
-> Result<Vec<Package<'a>>> {
let mut to_download: Vec<_> = Vec::new();

for app in &resp.apps {
Expand Down
19 changes: 11 additions & 8 deletions src/download.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::error::Error;
use anyhow::{Context, Result};
use std::io::Write;
use std::io;

Expand All @@ -9,26 +9,29 @@ pub struct DownloadResult<W: std::io::Write> {
pub data: W,
}

pub async fn download_and_hash<U, W>(client: &reqwest::Client, url: U, mut data: W) -> Result<DownloadResult<W>, Box<dyn Error>>
pub async fn download_and_hash<U, W>(client: &reqwest::Client, url: U, mut data: W) -> Result<DownloadResult<W>>
where
U: reqwest::IntoUrl,
U: reqwest::IntoUrl + Clone,
W: io::Write,
{
let client_url = url.clone();

#[rustfmt::skip]
let mut res = client.get(url)
.send()
.await?;
.await
.context(format!("client get and send({}) failed", client_url.as_str()))?;

let mut hasher = Sha256::new();

let mut bytes_read = 0usize;
let bytes_to_read = res.content_length().unwrap_or(u64::MAX) as usize;

while let Some(chunk) = res.chunk().await? {
while let Some(chunk) = res.chunk().await.context("response chunk failed")? {
bytes_read += chunk.len();

hasher.update(&chunk);
data.write_all(&chunk)?;
data.write_all(&chunk).context("write_all chunk failed")?;

// TODO: better way to report progress?
print!(
Expand All @@ -37,10 +40,10 @@ where
bytes_to_read,
((bytes_read as f32 / bytes_to_read as f32) * 100.0f32).floor()
);
io::stdout().flush()?;
io::stdout().flush().context("stdout flush failed")?;
}

data.flush()?;
data.flush().context("data flush failed")?;
println!();

Ok(DownloadResult {
Expand Down
11 changes: 6 additions & 5 deletions src/request.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::error::Error;
use std::borrow::Cow;

use anyhow::{Context, Result};
use hard_xml::XmlWrite;
use omaha;

Expand Down Expand Up @@ -30,7 +30,7 @@ pub struct Parameters<'a> {
pub machine_id: Cow<'a, str>,
}

pub async fn perform<'a>(client: &reqwest::Client, parameters: Parameters<'a>) -> Result<String, Box<dyn Error>> {
pub async fn perform<'a>(client: &reqwest::Client, parameters: Parameters<'a>) -> Result<String> {
let req_body = {
let r = omaha::Request {
protocol_version: Cow::Borrowed(PROTOCOL_VERSION),
Expand Down Expand Up @@ -69,7 +69,7 @@ pub async fn perform<'a>(client: &reqwest::Client, parameters: Parameters<'a>) -
],
};

r.to_string()?
r.to_string().context("to_string failed")?
};

// TODO: remove
Expand All @@ -80,7 +80,8 @@ pub async fn perform<'a>(client: &reqwest::Client, parameters: Parameters<'a>) -
let resp = client.post(UPDATE_URL)
.body(req_body)
.send()
.await?;
.await
.context("client post send({UPDATE_URL}) failed")?;

Ok(resp.text().await?)
Ok(resp.text().await.context("response text failed")?)
}
Loading

0 comments on commit 4068ce3

Please sign in to comment.