Untitled

mail@pastecode.io avatar
unknown
plain_text
7 months ago
4.7 kB
25
Indexable
Never
x
#[macro_use]
    extern crate serde;
    use candid::{Decode, Encode};
    use ic_cdk::api::time;
    use ic_stable_structures::memory_manager::{MemoryId, MemoryManager, VirtualMemory};
    use ic_stable_structures::{BoundedStorable, Cell, DefaultMemoryImpl, StableBTreeMap, Storable};
    use std::{borrow::Cow, cell::RefCell};
    
    type Memory = VirtualMemory<DefaultMemoryImpl>;
    type IdCell = Cell<u64, Memory>;
    
    #[derive(candid::CandidType, Clone, Serialize, Deserialize, Default)]
    struct Message {
        id: u64,
        title: String,
        body: String,
        attachment_url: String,
        created_at: u64,
        updated_at: Option<u64>,
    }
    
    // a trait that must be implemented for a struct that is stored in a stable struct
    impl Storable for Message {
        fn to_bytes(&self) -> std::borrow::Cow<[u8]> {
            Cow::Owned(Encode!(self).unwrap())
        }
    
        fn from_bytes(bytes: std::borrow::Cow<[u8]>) -> Self {
            Decode!(bytes.as_ref(), Self).unwrap()
        }
    }
    
    // another trait that must be implemented for a struct that is stored in a stable struct
    impl BoundedStorable for Message {
        const MAX_SIZE: u32 = 1024;
        const IS_FIXED_SIZE: bool = false;
    }
    
    thread_local! {
        static MEMORY_MANAGER: RefCell<MemoryManager<DefaultMemoryImpl>> = RefCell::new(
            MemoryManager::init(DefaultMemoryImpl::default())
        );
    
        static ID_COUNTER: RefCell<IdCell> = RefCell::new(
            IdCell::init(MEMORY_MANAGER.with(|m| m.borrow().get(MemoryId::new(0))), 0)
                .expect("Cannot create a counter")
        );
    
        static STORAGE: RefCell<StableBTreeMap<u64, Message, Memory>> =
            RefCell::new(StableBTreeMap::init(
                MEMORY_MANAGER.with(|m| m.borrow().get(MemoryId::new(1)))
        ));
    }
    
    #[derive(candid::CandidType, Serialize, Deserialize, Default)]
    struct MessagePayload {
        title: String,
        body: String,
        attachment_url: String,
    }
    
    #[ic_cdk::query]
    fn get_message(id: u64) -> Result<Message, Error> {
        match _get_message(&id) {
            Some(message) => Ok(message),
            None => Err(Error::NotFound {
                msg: format!("a message with id={} not found", id),
            }),
        }
    }
    
    #[ic_cdk::update]
    fn add_message(message: MessagePayload) -> Option<Message> {
        let id = ID_COUNTER
            .with(|counter| {
                let current_value = *counter.borrow().get();
                counter.borrow_mut().set(current_value + 1)
            })
            .expect("cannot increment id counter");
        let message = Message {
            id,
            title: message.title,
            body: message.body,
            attachment_url: message.attachment_url,
            created_at: time(),
            updated_at: None,
        };
        do_insert(&message);
        Some(message)
    }
    
    #[ic_cdk::update]
    fn update_message(id: u64, payload: MessagePayload) -> Result<Message, Error> {
        match STORAGE.with(|service| service.borrow().get(&id)) {
            Some(mut message) => {
                message.attachment_url = payload.attachment_url;
                message.body = payload.body;
                message.title = payload.title;
                message.updated_at = Some(time());
                do_insert(&message);
                Ok(message)
            }
            None => Err(Error::NotFound {
                msg: format!(
                    "couldn't update a message with id={}. message not found",
                    id
                ),
            }),
        }
    }
    
    // helper method to perform insert.
    fn do_insert(message: &Message) {
        STORAGE.with(|service| service.borrow_mut().insert(message.id, message.clone()));
    }
    
    #[ic_cdk::update]
    fn delete_message(id: u64) -> Result<Message, Error> {
        match STORAGE.with(|service| service.borrow_mut().remove(&id)) {
            Some(message) => Ok(message),
            None => Err(Error::NotFound {
                msg: format!(
                    "couldn't delete a message with id={}. message not found.",
                    id
                ),
            }),
        }
    }
    
    #[derive(candid::CandidType, Deserialize, Serialize)]
    enum Error {
        NotFound { msg: String },
    }
    
    // a helper method to get a message by id. used in get_message/update_message
    fn _get_message(id: &u64) -> Option<Message> {
        STORAGE.with(|service| service.borrow().get(id))
    }
    
    // need this to generate candid
    ic_cdk::export_candid!();
Leave a Comment