Skip to content

Commit 2077144

Browse files
Add ResultExt trait for error mapping utilities (#320)
* Add ResultExt trait for error mapping utilities * Refactor ResultExt trait for improved flexibility * Use new `ResultExt` everywhere for cleaner code
1 parent bd21d3b commit 2077144

File tree

23 files changed

+263
-289
lines changed

23 files changed

+263
-289
lines changed

rust/crates/cove-nfc/src/message.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,16 @@ type Result<T, E = Error> = std::result::Result<T, E>;
1919
impl NfcMessage {
2020
#[uniffi::constructor(default(string = None, data = None))]
2121
pub fn try_new(mut string: Option<String>, mut data: Option<Vec<u8>>) -> Result<Self> {
22-
if let Some(str) = &string {
23-
if str.is_empty() {
24-
string = None;
25-
}
22+
if let Some(str) = &string
23+
&& str.is_empty()
24+
{
25+
string = None;
2626
}
2727

28-
if let Some(d) = &data {
29-
if d.is_empty() {
30-
data = None;
31-
}
28+
if let Some(d) = &data
29+
&& d.is_empty()
30+
{
31+
data = None;
3232
}
3333

3434
match (string, data) {

rust/crates/cove-nfc/src/parser/stream.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ pub fn new(b: &[u8]) -> Stream<'_> {
77

88
pub trait StreamExt {
99
fn len(&self) -> usize;
10-
fn to_stream(&self) -> Stream;
10+
fn to_stream(&self) -> Stream<'_>;
1111
fn to_vec(self) -> Vec<u8>;
1212
fn is_empty(&self) -> bool {
1313
self.len() == 0
@@ -19,7 +19,7 @@ impl StreamExt for Stream<'_> {
1919
self.as_ref().len()
2020
}
2121

22-
fn to_stream(&self) -> Stream {
22+
fn to_stream(&self) -> Stream<'_> {
2323
*self
2424
}
2525

@@ -33,7 +33,7 @@ impl StreamExt for Vec<u8> {
3333
self.len()
3434
}
3535

36-
fn to_stream(&self) -> Stream {
36+
fn to_stream(&self) -> Stream<'_> {
3737
new(self)
3838
}
3939

rust/crates/cove-types/src/fees.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -251,10 +251,10 @@ impl FeeRateOptionsWithTotalFee {
251251
#[uniffi::method]
252252
pub fn get_fee_rate_with(&self, fee_rate: f32) -> Option<Arc<FeeRateOptionWithTotalFee>> {
253253
debug!("get_fee_rate_with: {fee_rate}");
254-
if let Some(custom) = self.custom {
255-
if custom.fee_rate.sat_per_vb() == fee_rate {
256-
return Some(custom.into());
257-
}
254+
if let Some(custom) = self.custom
255+
&& custom.fee_rate.sat_per_vb() == fee_rate
256+
{
257+
return Some(custom.into());
258258
}
259259

260260
if self.fast.fee_rate.sat_per_vb() == fee_rate {

rust/crates/cove-util/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
pub mod encryption;
22
pub mod format;
3+
pub mod result_ext;
34

45
use bitcoin::secp256k1::hashes::sha256::Hash as Sha256Hash;
56
use std::hash::{DefaultHasher, Hasher as _};
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
use std::fmt::Display;
2+
3+
pub trait ResultExt<T, InitialError> {
4+
/// Map an error to a string-based error variant
5+
///
6+
/// This allows converting `Result<T, InitialError>` to `Result<T, FinalError>` where FinalError has a variant
7+
/// that takes a String, using the Display implementation of InitialError.
8+
///
9+
/// # Example
10+
/// ```
11+
/// use cove_util::result_ext::ResultExt;
12+
///
13+
/// #[derive(Debug, thiserror::Error)]
14+
/// enum MyError {
15+
/// #[error("io error: {0}")]
16+
/// Io(String),
17+
/// }
18+
///
19+
/// fn example() -> Result<(), MyError> {
20+
/// std::fs::read_to_string("nonexistent.txt")
21+
/// .map_err_str(MyError::Io)?;
22+
/// Ok(())
23+
/// }
24+
/// ```
25+
fn map_err_str<FinalError, F>(self, f: F) -> Result<T, FinalError>
26+
where
27+
InitialError: Display,
28+
F: FnOnce(String) -> FinalError;
29+
30+
/// map an error using Into::into before passing to error constructor
31+
///
32+
/// shorthand for map_err(|e| f(e.into())) to convert errors using Into trait
33+
fn map_err_into<I, FinalError, F>(self, f: F) -> Result<T, FinalError>
34+
where
35+
InitialError: Into<I>,
36+
F: FnOnce(I) -> FinalError;
37+
}
38+
39+
impl<Type, InitialError> ResultExt<Type, InitialError> for Result<Type, InitialError> {
40+
fn map_err_str<FinalError, F>(self, f: F) -> Result<Type, FinalError>
41+
where
42+
InitialError: Display,
43+
F: FnOnce(String) -> FinalError,
44+
{
45+
self.map_err(|e| f(e.to_string()))
46+
}
47+
48+
fn map_err_into<I, FinalError, F>(self, f: F) -> Result<Type, FinalError>
49+
where
50+
InitialError: Into<I>,
51+
F: FnOnce(I) -> FinalError,
52+
{
53+
self.map_err(|e| f(e.into()))
54+
}
55+
}

rust/src/database/global_cache.rs

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ use std::sync::Arc;
33
use redb::TableDefinition;
44
use tracing::debug;
55

6+
use cove_util::result_ext::ResultExt as _;
7+
68
use crate::{
79
app::reconcile::{Update, Updater},
810
fiat::client::PriceResponse,
@@ -68,38 +70,29 @@ impl GlobalCacheTable {
6870

6971
impl GlobalCacheTable {
7072
pub fn get(&self, key: GlobalCacheKey) -> Result<Option<GlobalCacheData>, Error> {
71-
let read_txn =
72-
self.db.begin_read().map_err(|error| Error::DatabaseAccess(error.to_string()))?;
73+
let read_txn = self.db.begin_read().map_err_str(Error::DatabaseAccess)?;
7374

74-
let table =
75-
read_txn.open_table(TABLE).map_err(|error| Error::TableAccess(error.to_string()))?;
75+
let table = read_txn.open_table(TABLE).map_err_str(Error::TableAccess)?;
7676

7777
let key: &'static str = key.into();
78-
let value = table
79-
.get(key)
80-
.map_err(|error| GlobalCacheTableError::Read(error.to_string()))?
81-
.map(|value| value.value());
78+
let value =
79+
table.get(key).map_err_str(GlobalCacheTableError::Read)?.map(|value| value.value());
8280

8381
Ok(value)
8482
}
8583

8684
pub fn set(&self, key: GlobalCacheKey, value: GlobalCacheData) -> Result<(), Error> {
8785
debug!("set global cache: {key:?} -> {value:?}");
88-
let write_txn =
89-
self.db.begin_write().map_err(|error| Error::DatabaseAccess(error.to_string()))?;
86+
let write_txn = self.db.begin_write().map_err_str(Error::DatabaseAccess)?;
9087

9188
{
92-
let mut table = write_txn
93-
.open_table(TABLE)
94-
.map_err(|error| Error::TableAccess(error.to_string()))?;
89+
let mut table = write_txn.open_table(TABLE).map_err_str(Error::TableAccess)?;
9590

9691
let key: &'static str = key.into();
97-
table
98-
.insert(key, value)
99-
.map_err(|error| GlobalCacheTableError::Save(error.to_string()))?;
92+
table.insert(key, value).map_err_str(GlobalCacheTableError::Save)?;
10093
}
10194

102-
write_txn.commit().map_err(|error| Error::DatabaseAccess(error.to_string()))?;
95+
write_txn.commit().map_err_str(Error::DatabaseAccess)?;
10396

10497
Updater::send_update(Update::DatabaseUpdated);
10598

rust/src/database/global_flag.rs

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ use std::sync::Arc;
33
use redb::TableDefinition;
44
use tracing::debug;
55

6+
use cove_util::result_ext::ResultExt as _;
7+
68
use crate::app::reconcile::{Update, Updater};
79

810
use super::Error;
@@ -41,16 +43,14 @@ pub enum GlobalFlagTableError {
4143
#[uniffi::export]
4244
impl GlobalFlagTable {
4345
pub fn get(&self, key: GlobalFlagKey) -> Result<bool, Error> {
44-
let read_txn =
45-
self.db.begin_read().map_err(|error| Error::DatabaseAccess(error.to_string()))?;
46+
let read_txn = self.db.begin_read().map_err_str(Error::DatabaseAccess)?;
4647

47-
let table =
48-
read_txn.open_table(TABLE).map_err(|error| Error::TableAccess(error.to_string()))?;
48+
let table = read_txn.open_table(TABLE).map_err_str(Error::TableAccess)?;
4949

5050
let key: &'static str = key.into();
5151
let value = table
5252
.get(key)
53-
.map_err(|error| GlobalFlagTableError::Read(error.to_string()))?
53+
.map_err_str(GlobalFlagTableError::Read)?
5454
.map(|value| value.value())
5555
.unwrap_or(false);
5656

@@ -59,21 +59,16 @@ impl GlobalFlagTable {
5959

6060
pub fn set(&self, key: GlobalFlagKey, value: bool) -> Result<(), Error> {
6161
debug!("setting global flag: {key:?} to {value}");
62-
let write_txn =
63-
self.db.begin_write().map_err(|error| Error::DatabaseAccess(error.to_string()))?;
62+
let write_txn = self.db.begin_write().map_err_str(Error::DatabaseAccess)?;
6463

6564
{
66-
let mut table = write_txn
67-
.open_table(TABLE)
68-
.map_err(|error| Error::TableAccess(error.to_string()))?;
65+
let mut table = write_txn.open_table(TABLE).map_err_str(Error::TableAccess)?;
6966

7067
let key: &'static str = key.into();
71-
table
72-
.insert(key, value)
73-
.map_err(|error| GlobalFlagTableError::Save(error.to_string()))?;
68+
table.insert(key, value).map_err_str(GlobalFlagTableError::Save)?;
7469
}
7570

76-
write_txn.commit().map_err(|error| Error::DatabaseAccess(error.to_string()))?;
71+
write_txn.commit().map_err_str(Error::DatabaseAccess)?;
7772

7873
Updater::send_update(Update::DatabaseUpdated);
7974

rust/src/database/historical_price.rs

Lines changed: 17 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ pub mod record;
33

44
use std::sync::Arc;
55

6+
use cove_util::result_ext::ResultExt as _;
7+
68
use ahash::AHashMap as HashMap;
79
use network_block_height::NetworkBlockHeight;
810
use record::HistoricalPriceRecord;
@@ -48,16 +50,12 @@ impl HistoricalPriceTable {
4850
) -> Result<Option<HistoricalPriceRecord>, Error> {
4951
let key = NetworkBlockHeight::new(network, block_height);
5052

51-
let read_txn =
52-
self.db.begin_read().map_err(|error| Error::DatabaseAccess(error.to_string()))?;
53+
let read_txn = self.db.begin_read().map_err_str(Error::DatabaseAccess)?;
5354

54-
let table =
55-
read_txn.open_table(TABLE).map_err(|error| Error::TableAccess(error.to_string()))?;
55+
let table = read_txn.open_table(TABLE).map_err_str(Error::TableAccess)?;
5656

57-
let value = table
58-
.get(key)
59-
.map_err(|error| HistoricalPriceTableError::Read(error.to_string()))?
60-
.map(|value| value.value());
57+
let value =
58+
table.get(key).map_err_str(HistoricalPriceTableError::Read)?.map(|value| value.value());
6159

6260
Ok(value)
6361
}
@@ -74,20 +72,15 @@ impl HistoricalPriceTable {
7472
// convert to the more compact record format
7573
let price_record: HistoricalPriceRecord = price.into();
7674

77-
let write_txn =
78-
self.db.begin_write().map_err(|error| Error::DatabaseAccess(error.to_string()))?;
75+
let write_txn = self.db.begin_write().map_err_str(Error::DatabaseAccess)?;
7976

8077
{
81-
let mut table = write_txn
82-
.open_table(TABLE)
83-
.map_err(|error| Error::TableAccess(error.to_string()))?;
78+
let mut table = write_txn.open_table(TABLE).map_err_str(Error::TableAccess)?;
8479

85-
table
86-
.insert(key, &price_record)
87-
.map_err(|error| HistoricalPriceTableError::Save(error.to_string()))?;
80+
table.insert(key, &price_record).map_err_str(HistoricalPriceTableError::Save)?;
8881
}
8982

90-
write_txn.commit().map_err(|error| Error::DatabaseAccess(error.to_string()))?;
83+
write_txn.commit().map_err_str(Error::DatabaseAccess)?;
9184

9285
Ok(())
9386
}
@@ -99,11 +92,9 @@ impl HistoricalPriceTable {
9992
network: Network,
10093
block_heights: &[u32],
10194
) -> Result<HashMap<u32, Option<HistoricalPrice>>, Error> {
102-
let read_txn =
103-
self.db.begin_read().map_err(|error| Error::DatabaseAccess(error.to_string()))?;
95+
let read_txn = self.db.begin_read().map_err_str(Error::DatabaseAccess)?;
10496

105-
let table =
106-
read_txn.open_table(TABLE).map_err(|error| Error::TableAccess(error.to_string()))?;
97+
let table = read_txn.open_table(TABLE).map_err_str(Error::TableAccess)?;
10798

10899
let mut prices = HashMap::with_capacity(block_heights.len());
109100

@@ -112,7 +103,7 @@ impl HistoricalPriceTable {
112103
let key = NetworkBlockHeight::new(network, block_height);
113104
let value = table
114105
.get(key)
115-
.map_err(|error| HistoricalPriceTableError::Read(error.to_string()))?
106+
.map_err_str(HistoricalPriceTableError::Read)?
116107
.map(|value| value.value());
117108

118109
prices.insert(block_height, value.map(HistoricalPrice::from));
@@ -124,19 +115,16 @@ impl HistoricalPriceTable {
124115
/// DANGEROUS: Deletes all prices from the table.
125116
#[allow(dead_code)]
126117
fn clear(&self) -> Result<(), Error> {
127-
let write_txn =
128-
self.db.begin_write().map_err(|error| Error::DatabaseAccess(error.to_string()))?;
118+
let write_txn = self.db.begin_write().map_err_str(Error::DatabaseAccess)?;
129119

130120
{
131-
let mut table = write_txn
132-
.open_table(TABLE)
133-
.map_err(|error| Error::TableAccess(error.to_string()))?;
121+
let mut table = write_txn.open_table(TABLE).map_err_str(Error::TableAccess)?;
134122

135123
// delete all the records
136-
table.retain(|_, _| false).map_err(|error| Error::DatabaseAccess(error.to_string()))?;
124+
table.retain(|_, _| false).map_err_str(Error::DatabaseAccess)?;
137125
}
138126

139-
write_txn.commit().map_err(|error| Error::DatabaseAccess(error.to_string()))?;
127+
write_txn.commit().map_err_str(Error::DatabaseAccess)?;
140128
Ok(())
141129
}
142130
}

0 commit comments

Comments
 (0)