indexable_hooks/
lib.rs

1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3use thiserror::Error;
4
5use cosmwasm_std::{Addr, CustomQuery, Deps, StdError, StdResult, Storage, SubMsg};
6use cw_storage_plus::Item;
7
8#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, JsonSchema, Debug)]
9pub struct HooksResponse {
10    pub hooks: Vec<String>,
11}
12
13#[derive(Error, Debug, PartialEq)]
14pub enum HookError {
15    #[error("{0}")]
16    Std(#[from] StdError),
17
18    #[error("Given address already registered as a hook")]
19    HookAlreadyRegistered {},
20
21    #[error("Given address not registered as a hook")]
22    HookNotRegistered {},
23}
24
25// store all hook addresses in one item. We cannot have many of them before the contract becomes unusable anyway.
26pub struct Hooks<'a>(Item<'a, Vec<Addr>>);
27
28impl<'a> Hooks<'a> {
29    pub const fn new(storage_key: &'a str) -> Self {
30        Hooks(Item::new(storage_key))
31    }
32
33    pub fn add_hook(&self, storage: &mut dyn Storage, addr: Addr) -> Result<(), HookError> {
34        let mut hooks = self.0.may_load(storage)?.unwrap_or_default();
35        if !hooks.iter().any(|h| h == &addr) {
36            hooks.push(addr);
37        } else {
38            return Err(HookError::HookAlreadyRegistered {});
39        }
40        Ok(self.0.save(storage, &hooks)?)
41    }
42
43    pub fn remove_hook(&self, storage: &mut dyn Storage, addr: Addr) -> Result<(), HookError> {
44        let mut hooks = self.0.load(storage)?;
45        if let Some(p) = hooks.iter().position(|x| x == &addr) {
46            hooks.remove(p);
47        } else {
48            return Err(HookError::HookNotRegistered {});
49        }
50        Ok(self.0.save(storage, &hooks)?)
51    }
52
53    pub fn remove_hook_by_index(
54        &self,
55        storage: &mut dyn Storage,
56        index: u64,
57    ) -> Result<(), HookError> {
58        let mut hooks = self.0.load(storage)?;
59        hooks.remove(index as usize);
60        Ok(self.0.save(storage, &hooks)?)
61    }
62
63    pub fn prepare_hooks<F: FnMut(Addr) -> StdResult<SubMsg>>(
64        &self,
65        storage: &dyn Storage,
66        prep: F,
67    ) -> StdResult<Vec<SubMsg>> {
68        self.0
69            .may_load(storage)?
70            .unwrap_or_default()
71            .into_iter()
72            .map(prep)
73            .collect()
74    }
75
76    pub fn query_hooks<Q: CustomQuery>(&self, deps: Deps<Q>) -> StdResult<HooksResponse> {
77        let hooks = self.0.may_load(deps.storage)?.unwrap_or_default();
78        let hooks = hooks.into_iter().map(String::from).collect();
79        Ok(HooksResponse { hooks })
80    }
81}