use std::collections::HashMap;
#[derive(Debug, Default)]
pub struct ResourceTracker {
resources: HashMap<(Vec<u8>, String), bool>,
}
impl ResourceTracker {
pub fn new() -> Self {
Self::default()
}
pub fn exists(&self, address: &[u8], type_name: &str) -> bool {
self.resources
.get(&(address.to_vec(), type_name.to_string()))
.copied()
.unwrap_or(false)
}
pub fn move_to(&mut self, address: &[u8], type_name: &str) -> Result<(), ResourceError> {
let key = (address.to_vec(), type_name.to_string());
if self.resources.get(&key).copied().unwrap_or(false) {
return Err(ResourceError::AlreadyExists {
type_name: type_name.to_string(),
});
}
self.resources.insert(key, true);
Ok(())
}
pub fn move_from(&mut self, address: &[u8], type_name: &str) -> Result<(), ResourceError> {
let key = (address.to_vec(), type_name.to_string());
if !self.resources.get(&key).copied().unwrap_or(false) {
return Err(ResourceError::NotFound {
type_name: type_name.to_string(),
});
}
self.resources.insert(key, false);
Ok(())
}
pub fn borrow(&self, address: &[u8], type_name: &str) -> Result<(), ResourceError> {
if !self.exists(address, type_name) {
return Err(ResourceError::NotFound {
type_name: type_name.to_string(),
});
}
Ok(())
}
}
#[derive(Debug, Clone, thiserror::Error)]
pub enum ResourceError {
#[error("resource '{type_name}' already exists at address")]
AlreadyExists { type_name: String },
#[error("resource '{type_name}' not found at address")]
NotFound { type_name: String },
#[error("resource '{type_name}' cannot be copied")]
CannotCopy { type_name: String },
#[error("resource '{type_name}' cannot be dropped")]
CannotDrop { type_name: String },
}
pub fn generate_resource_tracker_code() -> Vec<u8> {
Vec::new()
}
pub fn signer_to_checkwitness() -> &'static str {
"System.Runtime.CheckWitness"
}
const RESOURCE_PREFIX: u8 = b'R';
const RESOURCE_SEPARATOR: u8 = b':';
pub fn global_storage_key(address: &[u8], type_name: &str) -> Vec<u8> {
let mut key = Vec::with_capacity(address.len() + type_name.len() + 2);
key.push(RESOURCE_PREFIX);
key.extend_from_slice(address);
key.push(RESOURCE_SEPARATOR);
key.extend_from_slice(type_name.as_bytes());
key
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_resource_lifecycle() {
let mut tracker = ResourceTracker::new();
let addr = b"test_address";
let type_name = "0x1::Coin::Coin";
assert!(!tracker.exists(addr, type_name));
tracker.move_to(addr, type_name).unwrap();
assert!(tracker.exists(addr, type_name));
assert!(tracker.move_to(addr, type_name).is_err());
tracker.move_from(addr, type_name).unwrap();
assert!(!tracker.exists(addr, type_name));
assert!(tracker.move_from(addr, type_name).is_err());
}
}