Skip to content

Commit

Permalink
examples: Add error output demonstration
Browse files Browse the repository at this point in the history
Add an `errors` example that creates and prints various error types from
the crate. This is useful for two main purposes:

- Helps us catch mistakes by exercising error paths.
- Helps us check that error output is useful for debugging.
- Helps us check that output for "alloc" builds is good/useful (no-std)

Run with appropriate features to verify the output, e.g.,

  `cargo run --example errors --no-default-features --features=alloc`
  • Loading branch information
tcharding committed Oct 30, 2023
1 parent ac74415 commit 61b6300
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 1 deletion.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,7 @@ alloc = []

[target.'cfg(mutate)'.dev-dependencies]
mutagen = { git = "https://github.com/llogiq/mutagen" }

[[example]]
name = "errors"
required-features = ["alloc"]
129 changes: 129 additions & 0 deletions examples/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
//! Demonstrate output from the various crate errors.

#[cfg(not(feature = "std"))]
use core::fmt;
#[cfg(feature = "std")]
use std::error::Error;

use bech32::{Fe32, Hrp};

fn main() {
crate_decode();
// crate::encode only returns `fmt::Error` errors.

crate_segwit_decode();
crate_segwit_encode();

// TODO: Do the other primitives modules.
primitives_hrp();
}

/// Demonstrates `bech32::decode` errors.
fn crate_decode() {
use bech32::decode;
let function = "bech32::decode";

// The arguments to pass to `function`.
let strings = vec!["1qqq", "hrp1abc"];

for s in strings {
let err = decode(s).unwrap_err();
println!("\n\n* Call `{}(\"{}\")` -> {:?}", function, s, err);
println!("\n------------");
print_source(&err);
println!("------------");
}
}

/// Demonstrates the `Hrp::Error` variants.
fn primitives_hrp() {
use bech32::primitives::hrp::Error::*;

println!("\n\n* All errors when parsing an invalid HRP");
let errs = vec![TooLong(99), Empty, NonAsciiChar('\u{e9}'), InvalidAsciiByte(200), MixedCase];
println!("\n------------");

let last = errs.len() - 1;
for (i, e) in errs.iter().enumerate() {
println!("Debug: {:?}\nError: {}", e.clone(), e);
if i != last {
println!();
}
}
println!("------------");
}

// TODO: Generate address strings to trigger:
// - Padding(PaddingError)
// - WitnessLength(WitnessLengthError)
/// Demonstrates `bech32::segwit::decode` errors.
fn crate_segwit_decode() {
use bech32::segwit::decode;
let function = "bech32::segwit::decode";

// The arguments to pass to `function`.
let strings = vec!["1qpppppp", "bc1qabc", "bc1", "bc1mpppppp", "bc1qppppp"];

for s in strings {
let err = decode(s).unwrap_err();
println!("\n\n* Call `{}(\"{}\")` -> {:?}", function, s, err);
println!("\n------------");
print_source(&err);
println!("------------");
}
}

/// Demonstrates `bech32::segwit::encode` errors.
fn crate_segwit_encode() {
use bech32::segwit::encode;
let function = "bech32::segwit::encode";

// The arguments to pass to `function`.
let hrp = Hrp::parse("bc").expect("a valid HRP string");

let invalid_witness_version = Fe32::M;

let valid_witness_program_segwit_v1 = [0x00, 0x01];
let invalid_witness_program_too_short = [0x00];
let invalid_witness_program_too_long = [0x00; 50];

let print = |err| {
println!("\n\n* Call `{}({}, [])` -> {:?}", function, hrp, err);
println!("\n------------");
print_source(&err);
println!("------------");
};

let err = encode(hrp, invalid_witness_version, &valid_witness_program_segwit_v1).unwrap_err();
print(err);

let err = encode(hrp, Fe32::P, &invalid_witness_program_too_short).unwrap_err();
print(err);

let err = encode(hrp, Fe32::P, &invalid_witness_program_too_long).unwrap_err();
print(err);

let err = encode(hrp, Fe32::Q, &valid_witness_program_segwit_v1).unwrap_err();
print(err);
}

/// Prints `e` in a similar fashion to the output created by `anyhow`.
#[cfg(feature = "std")]
fn print_source(mut e: &dyn Error) {
println!("Error: {}", e);

if e.source().is_some() {
let mut counter = 0;
println!("\nCaused by: ");

while e.source().is_some() {
let inner = e.source().unwrap();
println!("\t{}: {}", counter, inner);
e = e.source().unwrap();
counter += 1;
}
}
}

#[cfg(not(feature = "std"))]
fn print_source(e: &dyn fmt::Display) { println!("{}", e) }
3 changes: 2 additions & 1 deletion src/primitives/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1043,12 +1043,13 @@ mod tests {
fn $test_name() {
let res = SegwitHrpstring::new($address);
if res.is_ok() {
panic!("{} sting should not be valid: {}", $address, $reason);
panic!("{} string should not be valid: {}", $address, $reason);
}
}
)*
}
}
// TODO: We are not asserting on the error value, at least one of these is not failing for the reason stated.
check_invalid_segwit_addresses! {
invalid_segwit_address_0, "missing hrp", "1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq";
invalid_segwit_address_1, "missing data-checksum", "91111";
Expand Down

0 comments on commit 61b6300

Please sign in to comment.