mango-v4/lib/client/src/util.rs

147 lines
4.3 KiB
Rust

use solana_sdk::compute_budget::ComputeBudgetInstruction;
use solana_sdk::instruction::Instruction;
use anchor_lang::prelude::{AccountMeta, Pubkey};
use anyhow::Context;
/// Some Result<> types don't convert to anyhow::Result nicely. Force them through stringification.
pub trait AnyhowWrap {
type Value;
fn map_err_anyhow(self) -> anyhow::Result<Self::Value>;
}
impl<T, E: std::fmt::Debug> AnyhowWrap for Result<T, E> {
type Value = T;
fn map_err_anyhow(self) -> anyhow::Result<Self::Value> {
self.map_err(|err| anyhow::anyhow!("{:?}", err))
}
}
/// Push to an async_channel::Sender and ignore if the channel is full
pub trait AsyncChannelSendUnlessFull<T> {
/// Send a message if the channel isn't full
fn send_unless_full(&self, msg: T) -> Result<(), async_channel::SendError<T>>;
}
impl<T> AsyncChannelSendUnlessFull<T> for async_channel::Sender<T> {
fn send_unless_full(&self, msg: T) -> Result<(), async_channel::SendError<T>> {
use async_channel::*;
match self.try_send(msg) {
Ok(()) => Ok(()),
Err(TrySendError::Closed(msg)) => Err(async_channel::SendError(msg)),
Err(TrySendError::Full(_)) => Ok(()),
}
}
}
/// Like tokio::time::interval(), but with Delay as default MissedTickBehavior
///
/// The default (Burst) means that if the time between tick() calls is longer
/// than `period` there'll be a burst of catch-up ticks.
///
/// This Interval guarantees that when tick() returns, at least `period` will have
/// elapsed since the last return. That way it's more appropriate for jobs that
/// don't need to catch up.
pub fn delay_interval(period: std::time::Duration) -> tokio::time::Interval {
let mut interval = tokio::time::interval(period);
interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
interval
}
/// Convenience function used in binaries to set up the fmt tracing_subscriber,
/// with cololring enabled only if logging to a terminal and with EnvFilter.
pub fn tracing_subscriber_init() {
let format = tracing_subscriber::fmt::format().with_ansi(atty::is(atty::Stream::Stdout));
tracing_subscriber::fmt()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.event_format(format)
.init();
}
pub async fn http_error_handling<T: serde::de::DeserializeOwned>(
response: reqwest::Response,
) -> anyhow::Result<T> {
let status = response.status();
let response_text = response
.text()
.await
.context("awaiting body of http request")?;
if !status.is_success() {
anyhow::bail!("http request failed, status: {status}, body: {response_text}");
}
serde_json::from_str::<T>(&response_text)
.with_context(|| format!("response has unexpected format, body: {response_text}"))
}
pub fn to_readonly_account_meta(pubkey: Pubkey) -> AccountMeta {
AccountMeta {
pubkey,
is_writable: false,
is_signer: false,
}
}
pub fn to_writable_account_meta(pubkey: Pubkey) -> AccountMeta {
AccountMeta {
pubkey,
is_writable: true,
is_signer: false,
}
}
#[derive(Default, Clone)]
pub struct PreparedInstructions {
pub instructions: Vec<Instruction>,
pub cu: u32,
}
impl PreparedInstructions {
pub fn new() -> Self {
Self {
instructions: vec![],
cu: 0,
}
}
pub fn from_vec(instructions: Vec<Instruction>, cu: u32) -> Self {
Self { instructions, cu }
}
pub fn from_single(instruction: Instruction, cu: u32) -> Self {
Self {
instructions: vec![instruction],
cu,
}
}
pub fn push(&mut self, ix: Instruction, cu: u32) {
self.instructions.push(ix);
self.cu += cu;
}
pub fn append(&mut self, mut other: Self) {
self.instructions.append(&mut other.instructions);
self.cu += other.cu;
}
pub fn to_instructions(self) -> Vec<Instruction> {
let mut ixs = self.instructions;
ixs.insert(0, ComputeBudgetInstruction::set_compute_unit_limit(self.cu));
ixs
}
pub fn is_empty(&self) -> bool {
self.instructions.is_empty()
}
pub fn clear(&mut self) {
self.instructions.clear();
self.cu = 0;
}
pub fn len(&self) -> usize {
self.instructions.len()
}
}