Skip to content

Commit e2f33d7

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 e2f33d7

File tree

3 files changed

+139
-1
lines changed

3 files changed

+139
-1
lines changed

Diff for: 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 = []

Diff for: examples/errors.rs

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

Diff for: 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)