dRAT3
July 28, 2021, 10:59am
#1
I have a struct:
pub struct IsContractResponse {
pub address: String,
pub is_contract: bool,
pub count: u8,
}
And I need to return it whenever a reqwest ::Error occurs in the following function:
pub async fn is_contract(
container: IsContractResponse,
json_rpc_api: String,
) -> Result<IsContractResponse, reqwest::Error> {
let new_eth_request = EthRequest {
jsonrpc: "2.0".to_string(),
method: "eth_getCode".to_string(),
params: [
Params::String(container.address.to_string()),
Params::String("latest".to_string()),
],
id: 1,
};
let new_eth_response: EthBlockCtResponse = reqwest::Client::new()
.post(&json_rpc_api)
.json(&new_eth_request)
.send()
.await?
.json()
.await?;
let mut container = container;
if new_eth_response.result.chars().count() > 2 {
container.is_contract = true;
}
Ok(container)
}
How do I implement the Error, and how do I return it?
dRAT3
July 28, 2021, 11:24am
#3
Why did you delete it?
All i want to do is return a count and an address on error so I can retry it (from a futuresunorded list). Is that so unsafe?
I deleted it because I felt it was not the correct response nor solution, really sorry for that.
If you do want to go the route of implementing the From
trait, then I would recommend having a separate struct for the error response. That way, you'd both know it's an error, maybe have error details in the struct, and also have the data needed to retry.
dRAT3
July 28, 2021, 12:22pm
#5
Now i have this:
struct ContractErrorData {
address: String,
count: u8,
}
struct ContractError {
kind: String,
message: String,
data: ContractErrorData,
}
impl From<reqwest::Error> for ContractError {
fn from(error: reqwest::Error) -> Self {
ContractError {
kind: String::from("reqwest"),
message: error.to_string(),
data: //??? How do I send data to self from is_contract()
}
}
}
Ah, based on my understanding of the question, I assumed the data you wanted along with the error was some known default.
If you want to use the data from the container
that's passed to is_contract
, you would be better off using map_err
(or something similar) to return a ContractError
on reqwest::Error
.
pub async fn is_contract(
container: IsContractResponse,
json_rpc_api: String,
) -> Result<IsContractResponse, ContractError> {
let new_eth_request = EthRequest {
jsonrpc: "2.0".to_string(),
method: "eth_getCode".to_string(),
params: [
Params::String(container.address.to_string()),
Params::String("latest".to_string()),
],
id: 1,
};
let new_eth_response: EthBlockCtResponse = reqwest::Client::new()
.post(&json_rpc_api)
.json(&new_eth_request)
.send()
.await
.map_err(|err| ContractError::new(err, &container))?
.json()
.await
.map_err(|err| ContractError::new(err, &container))?;
let mut container = container;
if new_eth_response.result.chars().count() > 2 {
container.is_contract = true;
}
Ok(container)
}
And, of course, implement ContractError::new
(or however you want to transform reqwest::Error
to ContractError
):
impl ContractError {
// However you want it, this is just an example
pub fn new(error: reqwest::Error, container: &IsContractResponse) -> Self {
ContractError {
kind: String::from("reqwest"),
message: error.to_string(),
data: ContractErrorData {
address: container.address.clone(),
count: container.count,
},
}
}
}
dRAT3
July 28, 2021, 2:45pm
#7
Ah thanks for your help, I didn't see your response. I'll study it and try to understand it. For now I ended up solving it with another function that wrap's around the request and returns a struct containing a container with the reqwest::error
and the IsContractResponse
if that makes sense:
pub struct IsContractResponse {
pub address: String,
pub is_contract: bool,
pub count: u8,
}
pub struct IsContractErr {
container: IsContractResponse,
error: reqwest::Error,
}
pub async fn is_contract(
container: IsContractResponse,
json_rpc_api: String,
) -> Result<IsContractResponse, IsContractErr> {
let mut container = container;
container.count += 1;
let res = is_contract_reqwest(&container.address, json_rpc_api).await;
if res.is_err() {
let error = IsContractErr {
container: container,
error: res.err().unwrap(),
};
return Err(error);
}
if res.unwrap().result.chars().count() > 2 {
container.is_contract = true;
}
Ok(container)
}
pub async fn is_contract_reqwest(
address: &str,
json_rpc_api: String,
) -> Result<EthBlockCtResponse, reqwest::Error> {
let new_eth_request = EthRequest {
jsonrpc: "2.0".to_string(),
method: "eth_getCode".to_string(),
params: [
Params::String(address.to_string()),
Params::String("latest".to_string()),
],
id: 1,
};
let new_eth_response: EthBlockCtResponse = reqwest::Client::new()
.post(&json_rpc_api)
.json(&new_eth_request)
.send()
.await?
.json()
.await?;
Ok(new_eth_response)
}
Is this a valid/idiomatic way to solve the problem or will I run into unforeseen errors later?
I would say that extracting is_contract_reqwest
to its own function is pretty good, especially if in the future you need to use it elsewhere.
Also, if you use a match
you can avoid using those unwrap
s.
pub async fn is_contract(
container: IsContractResponse,
json_rpc_api: String,
) -> Result<IsContractResponse, IsContractErr> {
let mut container = container;
container.count += 1;
let res = match is_contract_reqwest(&container.address, json_rpc_api).await {
Ok(res) => res,
Err(err) => return Err(IsContractErr {
container: container,
error: err,
}),
};
if res.result.chars().count() > 2 {
container.is_contract = true;
}
Ok(container)
}
1 Like
system
closed
October 26, 2021, 3:02pm
#9
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.