1use crate::memory::{get_memory, MEMORY_ID_CONFIG, MEMORY_ID_RESOURCES, MEMORY_ID_TOOLS};
4use candid::{CandidType, Deserialize, Principal};
5use ic_stable_structures::{StableBTreeMap, StableCell, Storable};
6use serde::Serialize;
7use std::cell::RefCell;
8
9pub struct IcarusCanisterState {
11 pub config: StableCell<ServerConfig, crate::memory::Memory>,
12 pub tools: StableBTreeMap<String, ToolState, crate::memory::Memory>,
13 pub resources: StableBTreeMap<String, ResourceState, crate::memory::Memory>,
14}
15
16#[derive(Debug, Clone, Serialize, Deserialize, CandidType)]
18pub struct ServerConfig {
19 pub name: String,
20 pub version: String,
21 pub canister_id: Principal,
22 pub owner: Principal,
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize, CandidType)]
27pub struct ToolState {
28 pub name: String,
29 pub enabled: bool,
30 pub call_count: u64,
31 pub is_query: bool,
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize, CandidType)]
36pub struct ResourceState {
37 pub uri: String,
38 pub access_count: u64,
39}
40
41impl Storable for ServerConfig {
42 fn to_bytes(&self) -> std::borrow::Cow<'_, [u8]> {
43 std::borrow::Cow::Owned(candid::encode_one(self).unwrap())
44 }
45
46 fn into_bytes(self) -> Vec<u8> {
47 candid::encode_one(&self).unwrap()
48 }
49
50 fn from_bytes(bytes: std::borrow::Cow<'_, [u8]>) -> Self {
51 candid::decode_one(&bytes).unwrap()
52 }
53
54 const BOUND: ic_stable_structures::storable::Bound =
55 ic_stable_structures::storable::Bound::Bounded {
56 max_size: 1024,
57 is_fixed_size: false,
58 };
59}
60
61impl Storable for ToolState {
62 fn to_bytes(&self) -> std::borrow::Cow<'_, [u8]> {
63 std::borrow::Cow::Owned(candid::encode_one(self).unwrap())
64 }
65
66 fn into_bytes(self) -> Vec<u8> {
67 candid::encode_one(&self).unwrap()
68 }
69
70 fn from_bytes(bytes: std::borrow::Cow<'_, [u8]>) -> Self {
71 candid::decode_one(&bytes).unwrap()
72 }
73
74 const BOUND: ic_stable_structures::storable::Bound =
75 ic_stable_structures::storable::Bound::Bounded {
76 max_size: 512,
77 is_fixed_size: false,
78 };
79}
80
81impl Storable for ResourceState {
82 fn to_bytes(&self) -> std::borrow::Cow<'_, [u8]> {
83 std::borrow::Cow::Owned(candid::encode_one(self).unwrap())
84 }
85
86 fn into_bytes(self) -> Vec<u8> {
87 candid::encode_one(&self).unwrap()
88 }
89
90 fn from_bytes(bytes: std::borrow::Cow<'_, [u8]>) -> Self {
91 candid::decode_one(&bytes).unwrap()
92 }
93
94 const BOUND: ic_stable_structures::storable::Bound =
95 ic_stable_structures::storable::Bound::Bounded {
96 max_size: 512,
97 is_fixed_size: false,
98 };
99}
100
101thread_local! {
102 pub static STATE: RefCell<Option<IcarusCanisterState>> = const { RefCell::new(None) };
104}
105
106impl IcarusCanisterState {
107 pub fn init(config: ServerConfig) {
108 let state = Self {
109 config: StableCell::init(get_memory(MEMORY_ID_CONFIG), config),
110 tools: StableBTreeMap::init(get_memory(MEMORY_ID_TOOLS)),
111 resources: StableBTreeMap::init(get_memory(MEMORY_ID_RESOURCES)),
112 };
113
114 STATE.with(|s| *s.borrow_mut() = Some(state));
115 }
116
117 pub fn with<F, R>(f: F) -> R
118 where
119 F: FnOnce(&IcarusCanisterState) -> R,
120 {
121 STATE.with(|s| {
122 let state = s.borrow();
123 let state_ref = state.as_ref().expect("State not initialized");
124 f(state_ref)
125 })
126 }
127
128 pub fn get_owner(&self) -> Principal {
130 self.config.get().owner
131 }
132}
133
134pub fn assert_owner() {
140 let caller = ic_cdk::api::msg_caller();
141 IcarusCanisterState::with(|state| {
142 let owner = state.get_owner();
143 if caller != owner {
144 ic_cdk::trap(format!(
145 "Access denied: caller {} is not the owner {}",
146 caller.to_text(),
147 owner.to_text()
148 ));
149 }
150 });
151}
152
153pub fn is_owner(caller: Principal) -> bool {
155 IcarusCanisterState::with(|state| caller == state.get_owner())
156}
157
158pub fn get_owner() -> Principal {
160 IcarusCanisterState::with(|state| state.get_owner())
161}