1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
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) -> anyhow::Result<()>;
}

impl<T> AsyncChannelSendUnlessFull<T> for async_channel::Sender<T> {
    fn send_unless_full(&self, msg: T) -> anyhow::Result<()> {
        use async_channel::*;
        match self.try_send(msg) {
            Ok(()) => Ok(()),
            Err(TrySendError::Closed(_)) => Err(anyhow::format_err!("channel is closed")),
            Err(TrySendError::Full(_)) => Ok(()),
        }
    }
}
impl<T> AsyncChannelSendUnlessFull<T> for tokio::sync::mpsc::Sender<T> {
    fn send_unless_full(&self, msg: T) -> anyhow::Result<()> {
        use tokio::sync::mpsc::*;
        match self.try_send(msg) {
            Ok(()) => Ok(()),
            Err(error::TrySendError::Closed(_)) => Err(anyhow::format_err!("channel is closed")),
            Err(error::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()
    }
}