Skip to content

Commit 0a03c11

Browse files
committed
examples: Add error output demonstration
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`
1 parent 8eb7954 commit 0a03c11

File tree

3 files changed

+135
-1
lines changed

3 files changed

+135
-1
lines changed

Cargo.toml

+4
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,7 @@ alloc = []
1818

1919
[target.'cfg(mutate)'.dev-dependencies]
2020
mutagen = { git = "https://github.com/llogiq/mutagen" }
21+
22+
[[example]]
23+
name = "errors"
24+
required-features = ["alloc"]

examples/errors.rs

+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
//! Demonstrate output from the various crate errors.
2+
3+
#[cfg(not(feature = "std"))]
4+
use core::fmt;
5+
#[cfg(feature = "std")]
6+
use std::error::Error;
7+
8+
use bech32::{Fe32, Hrp};
9+
10+
fn main() {
11+
crate_decode();
12+
// crate::encode only returns `fmt::Error` errors.
13+
14+
crate_segwit_decode();
15+
crate_segwit_encode();
16+
17+
// TODO: Do the other primitives modules.
18+
primitives_hrp();
19+
}
20+
21+
/// Demonstrates `bech32::decode` errors.
22+
fn crate_decode() {
23+
use bech32::decode;
24+
let function = "bech32::decode";
25+
26+
// The arguments to pass to `function`.
27+
let strings = vec!["1qqq", "hrp1abc"];
28+
29+
for s in strings {
30+
let err = decode(s).unwrap_err();
31+
println!("\n\n* Call `{}(\"{}\")` -> {:?}", function, s, err);
32+
println!("\n------------");
33+
print_source(&err);
34+
println!("------------");
35+
}
36+
}
37+
38+
/// Demonstrates the `Hrp::Error` variants.
39+
fn primitives_hrp() {
40+
use bech32::primitives::hrp::Error::*;
41+
42+
println!("\n\n* All errors when parsing an invalid HRP");
43+
let errs = vec![TooLong(99), Empty, NonAsciiChar('\u{e9}'), InvalidAsciiByte(200), MixedCase];
44+
println!("\n------------");
45+
46+
let last = errs.len() - 1;
47+
for (i, e) in errs.iter().enumerate() {
48+
println!("Debug: {:?}\nError: {}", e.clone(), e);
49+
if i != last {
50+
println!();
51+
}
52+
}
53+
println!("------------");
54+
}
55+
56+
// TODO: Generate address strings to trigger:
57+
// - Padding(PaddingError)
58+
// - WitnessLength(WitnessLengthError)
59+
/// Demonstrates `bech32::segwit::decode` errors.
60+
fn crate_segwit_decode() {
61+
use bech32::segwit::decode;
62+
let function = "bech32::segwit::decode";
63+
64+
// The arguments to pass to `function`.
65+
let strings = vec!["1qpppppp", "bc1qabc", "bc1", "bc1mpppppp", "bc1qppppp"];
66+
67+
for s in strings {
68+
let err = decode(s).unwrap_err();
69+
println!("\n\n* Call `{}(\"{}\")` -> {:?}", function, s, err);
70+
println!("\n------------");
71+
print_source(&err);
72+
println!("------------");
73+
}
74+
}
75+
76+
/// Demonstrates `bech32::segwit::encode` errors.
77+
fn crate_segwit_encode() {
78+
use bech32::segwit::encode;
79+
let function = "bech32::segwit::encode";
80+
81+
// The arguments to pass to `function`.
82+
let hrp = Hrp::parse("bc").expect("a valid HRP string");
83+
84+
let invalid_witness_version = Fe32::M;
85+
86+
let valid_witness_program_segwit_v1 = [0x00, 0x01];
87+
let invalid_witness_program_too_short = [0x00];
88+
let invalid_witness_program_too_long = [0x00; 50];
89+
90+
let print = |err| {
91+
println!("\n\n* Call `{}({}, [])` -> {:?}", function, hrp, err);
92+
println!("\n------------");
93+
print_source(&err);
94+
println!("------------");
95+
};
96+
97+
let err = encode(hrp, invalid_witness_version, &valid_witness_program_segwit_v1).unwrap_err();
98+
print(err);
99+
100+
let err = encode(hrp, Fe32::P, &invalid_witness_program_too_short).unwrap_err();
101+
print(err);
102+
103+
let err = encode(hrp, Fe32::P, &invalid_witness_program_too_long).unwrap_err();
104+
print(err);
105+
106+
let err = encode(hrp, Fe32::Q, &valid_witness_program_segwit_v1).unwrap_err();
107+
print(err);
108+
}
109+
110+
/// Prints `e` in a similar fashion to the output created by `anyhow`.
111+
#[cfg(feature = "std")]
112+
fn print_source(mut e: &dyn Error) {
113+
println!("Error: {}", e);
114+
115+
if e.source().is_some() {
116+
let mut counter = 0;
117+
println!("\nCaused by: ");
118+
119+
while e.source().is_some() {
120+
let inner = e.source().unwrap();
121+
println!("\t{}: {}", counter, inner);
122+
e = e.source().unwrap();
123+
counter += 1;
124+
}
125+
}
126+
}
127+
128+
#[cfg(not(feature = "std"))]
129+
fn print_source(e: &dyn fmt::Display) { println!("{}", e) }

src/primitives/decode.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -955,12 +955,13 @@ mod tests {
955955
fn $test_name() {
956956
let res = SegwitHrpstring::new($address);
957957
if res.is_ok() {
958-
panic!("{} sting should not be valid: {}", $address, $reason);
958+
panic!("{} string should not be valid: {}", $address, $reason);
959959
}
960960
}
961961
)*
962962
}
963963
}
964+
// TODO: We are not asserting on the error value, at least one of these is not failing for the reason stated.
964965
check_invalid_segwit_addresses! {
965966
invalid_segwit_address_0, "missing hrp", "1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq";
966967
invalid_segwit_address_1, "missing data-checksum", "91111";

0 commit comments

Comments
 (0)