use algonaut_model::algod::{Account, Application, DryrunRequest, DryrunSource};
use algonaut_model::transaction::ApiSignedTransaction;
use algonaut_transaction::SignedTransaction;
use algonaut_transaction::error::TransactionError;
#[derive(Debug, Clone, Default)]
pub struct DryrunRequestBuilder {
accounts: Vec<Account>,
apps: Vec<Application>,
sources: Vec<DryrunSource>,
txns: Vec<ApiSignedTransaction>,
round: u64,
latest_timestamp: u64,
protocol_version: String,
}
impl DryrunRequestBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn from_signed_txns(signed: &[SignedTransaction]) -> Result<Self, TransactionError> {
let mut me = Self::new();
for tx in signed {
me.txns.push(ApiSignedTransaction::try_from(tx.clone())?);
}
Ok(me)
}
pub fn accounts(mut self, accounts: Vec<Account>) -> Self {
self.accounts = accounts;
self
}
pub fn apps(mut self, apps: Vec<Application>) -> Self {
self.apps = apps;
self
}
pub fn round(mut self, round: u64) -> Self {
self.round = round;
self
}
pub fn latest_timestamp(mut self, ts: u64) -> Self {
self.latest_timestamp = ts;
self
}
pub fn protocol_version(mut self, version: impl Into<String>) -> Self {
self.protocol_version = version.into();
self
}
pub fn add_text_source(
mut self,
source: impl Into<String>,
field_name: impl Into<String>,
txn_index: u64,
app_index: u64,
) -> Self {
self.sources.push(DryrunSource {
app_index,
field_name: field_name.into(),
source: source.into(),
txn_index,
});
self
}
pub fn add_source(mut self, source: DryrunSource) -> Self {
self.sources.push(source);
self
}
pub fn add_signed_txn(mut self, tx: &SignedTransaction) -> Result<Self, TransactionError> {
self.txns.push(ApiSignedTransaction::try_from(tx.clone())?);
Ok(self)
}
pub fn build(self) -> DryrunRequest {
DryrunRequest {
accounts: self.accounts,
apps: self.apps,
latest_timestamp: self.latest_timestamp,
protocol_version: self.protocol_version,
round: self.round,
sources: self.sources,
txns: self.txns,
}
}
}
pub mod field_name {
pub const LSIG: &str = "lsig";
pub const APPROV: &str = "approv";
pub const CLEARP: &str = "clearp";
}
pub mod result {
use algonaut_model::algod::{DryrunResponse, DryrunTxnResult};
pub fn app_call_status(txn: &DryrunTxnResult) -> Option<&str> {
txn.app_call_messages
.as_ref()
.and_then(|msgs: &Vec<String>| msgs.last().map(String::as_str))
}
pub fn logic_sig_status(txn: &DryrunTxnResult) -> Option<&str> {
txn.logic_sig_messages
.as_ref()
.and_then(|msgs: &Vec<String>| msgs.last().map(String::as_str))
}
pub fn overall_status(txn: &DryrunTxnResult) -> Option<&str> {
logic_sig_status(txn).or_else(|| app_call_status(txn))
}
pub fn first_status(resp: &DryrunResponse) -> Option<&str> {
resp.txns.first().and_then(overall_status)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn builder_starts_empty() {
let req = DryrunRequestBuilder::new().build();
assert!(req.accounts.is_empty());
assert!(req.apps.is_empty());
assert!(req.sources.is_empty());
assert!(req.txns.is_empty());
assert_eq!(req.round, 0);
assert_eq!(req.protocol_version, "");
}
#[test]
fn add_source_round_trip() {
let source = "#pragma version 8\nint 1";
let req = DryrunRequestBuilder::new()
.add_text_source(source, field_name::APPROV, 0, 0)
.round(123)
.protocol_version("future")
.build();
assert_eq!(req.sources.len(), 1);
assert_eq!(req.sources[0].field_name, "approv");
assert_eq!(req.sources[0].source, source);
assert_eq!(req.round, 123);
assert_eq!(req.protocol_version, "future");
}
}