use std::str::FromStr;
use crate::direction::DirectionToBase;
use crate::leverage::LeverageToBase;
use crate::prelude::*;
use anyhow::Context;
use cosmwasm_std::Event;
use serde::de::DeserializeOwned;
use crate::error::{ErrorDomain, ErrorId};
pub trait CosmwasmEventExt {
fn has_attr(&self, key: &str) -> bool;
fn try_map_attr<B>(&self, key: &str, f: impl Fn(&str) -> B) -> Option<B>;
fn try_json_attr<B: DeserializeOwned>(&self, key: &str) -> anyhow::Result<Option<B>> {
match self.try_map_attr(key, |s| serde_json::from_str(s)) {
None => Ok(None),
Some(x) => Ok(Some(x?)),
}
}
fn json_attr<B: DeserializeOwned>(&self, key: &str) -> anyhow::Result<B> {
self.map_attr_result(key, |s| {
serde_json::from_str(s).map_err(anyhow::Error::from)
})
}
fn u64_attr(&self, key: &str) -> anyhow::Result<u64> {
self.map_attr_result(key, |s| s.parse().map_err(anyhow::Error::from))
}
fn try_u64_attr(&self, key: &str) -> anyhow::Result<Option<u64>> {
match self.try_map_attr(key, |s| s.parse()) {
None => Ok(None),
Some(x) => Ok(Some(x?)),
}
}
fn timestamp_attr(&self, key: &str) -> anyhow::Result<Timestamp> {
self.map_attr_result(key, |s| Timestamp::from_str(s).map_err(|x| x.into()))
}
fn try_timestamp_attr(&self, key: &str) -> anyhow::Result<Option<Timestamp>> {
self.try_map_attr(key, |s| Timestamp::from_str(s).map_err(|x| x.into()))
.transpose()
}
fn decimal_attr<T: UnsignedDecimal>(&self, key: &str) -> anyhow::Result<T> {
self.map_attr_result(key, |s| {
s.parse()
.ok()
.with_context(|| format!("decimal_attr failed on key {key} and value {s}"))
})
}
fn non_zero_attr<T: UnsignedDecimal>(&self, key: &str) -> anyhow::Result<NonZero<T>> {
self.map_attr_result(key, |s| {
s.parse()
.ok()
.with_context(|| format!("non_zero_attr failed on key {key} and value {s}"))
})
}
fn signed_attr<T: UnsignedDecimal>(&self, key: &str) -> anyhow::Result<Signed<T>> {
self.map_attr_result(key, |s| {
s.parse()
.ok()
.with_context(|| format!("signed_attr failed on key {key} and value {s}"))
})
}
fn number_attr<T: UnsignedDecimal>(&self, key: &str) -> anyhow::Result<Signed<T>> {
self.map_attr_result(key, |s| s.parse())
}
fn try_number_attr<T: UnsignedDecimal>(&self, key: &str) -> anyhow::Result<Option<Signed<T>>> {
self.try_map_attr(key, |s| {
s.parse()
.ok()
.with_context(|| format!("try_number_attr failed parse on key {key} and value {s}"))
})
.transpose()
}
fn try_decimal_attr<T: UnsignedDecimal>(&self, key: &str) -> anyhow::Result<Option<T>> {
self.try_map_attr(key, |s| {
s.parse().ok().with_context(|| {
format!("try_decimal_attr failed parse on key {key} and value {s}")
})
})
.transpose()
}
fn try_price_base_in_quote(&self, key: &str) -> anyhow::Result<Option<PriceBaseInQuote>> {
self.try_map_attr(key, |s| {
s.parse().ok().with_context(|| {
format!("try_price_base_in_quote failed parse on key {key} and value {s}")
})
})
.transpose()
}
fn string_attr(&self, key: &str) -> anyhow::Result<String> {
self.map_attr_ok(key, |s| s.to_string())
}
fn bool_attr(&self, key: &str) -> anyhow::Result<bool> {
self.string_attr(key)
.and_then(|s| s.parse::<bool>().map_err(|err| err.into()))
}
fn direction_attr(&self, key: &str) -> anyhow::Result<DirectionToBase> {
self.map_attr_result(key, |s| match s {
"long" => Ok(DirectionToBase::Long),
"short" => Ok(DirectionToBase::Short),
_ => Err(anyhow::anyhow!("Invalid direction: {s}")),
})
}
fn leverage_to_base_attr(&self, key: &str) -> anyhow::Result<LeverageToBase> {
self.map_attr_result(key, LeverageToBase::from_str)
}
fn try_leverage_to_base_attr(&self, key: &str) -> anyhow::Result<Option<LeverageToBase>> {
self.try_map_attr(key, LeverageToBase::from_str).transpose()
}
fn unchecked_addr_attr(&self, key: &str) -> anyhow::Result<Addr> {
self.map_attr_ok(key, |s| Addr::unchecked(s))
}
fn try_unchecked_addr_attr(&self, key: &str) -> anyhow::Result<Option<Addr>> {
self.try_map_attr(key, |s| Ok(Addr::unchecked(s)))
.transpose()
}
fn map_attr_ok<B>(&self, key: &str, f: impl Fn(&str) -> B) -> anyhow::Result<B> {
match self.try_map_attr(key, f) {
Some(x) => Ok(x),
None => Err(anyhow!(PerpError::new(
ErrorId::Any,
ErrorDomain::Default,
format!("no such key {key}")
))),
}
}
fn map_attr_result<B>(
&self,
key: &str,
f: impl Fn(&str) -> anyhow::Result<B>,
) -> anyhow::Result<B> {
self.map_attr_ok(key, f)?
}
}
impl CosmwasmEventExt for Event {
fn has_attr(&self, key: &str) -> bool {
self.attributes.iter().any(|a| a.key == key)
}
fn try_map_attr<B>(&self, key: &str, f: impl Fn(&str) -> B) -> Option<B> {
self.attributes.iter().find_map(|a| {
if a.key == key {
Some(f(a.value.as_str()))
} else {
None
}
})
}
}