1167 lines
37 KiB
Rust
1167 lines
37 KiB
Rust
//! Rhai wrappers for RFS client module functions
|
|
//!
|
|
//! This module provides Rhai wrappers for the functions in the RFS client module.
|
|
|
|
use crate::client::RfsClient;
|
|
use crate::types::{ClientConfig, Credentials, DownloadOptions, UploadOptions, WaitOptions};
|
|
use crate::RfsError;
|
|
use lazy_static::lazy_static;
|
|
use rhai::{Dynamic, Engine, EvalAltResult, Map};
|
|
use serde_json::Value;
|
|
use std::sync::{Arc, Mutex};
|
|
use tokio::runtime::Runtime;
|
|
|
|
// Global RFS client and runtime management
|
|
lazy_static! {
|
|
static ref RFS_CLIENT: Mutex<Option<Arc<RfsClientWrapper>>> = Mutex::new(None);
|
|
static ref RUNTIME: Mutex<Option<Runtime>> = Mutex::new(None);
|
|
}
|
|
|
|
/// Wrapper around RfsClient to make it thread-safe for global usage
|
|
struct RfsClientWrapper {
|
|
client: Mutex<RfsClient>,
|
|
}
|
|
|
|
impl RfsClientWrapper {
|
|
fn new(client: RfsClient) -> Self {
|
|
Self { client: Mutex::new(client) }
|
|
}
|
|
}
|
|
|
|
/// Register RFS module functions with the Rhai engine
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `engine` - The Rhai engine to register the functions with
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// * `Result<(), Box<EvalAltResult>>` - Ok if registration was successful, Err otherwise
|
|
pub fn register_rfs_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
|
|
// Register RFS client functions
|
|
engine.register_fn("rfs_create_client", rfs_create_client);
|
|
engine.register_fn("rfs_authenticate", rfs_authenticate);
|
|
engine.register_fn("rfs_get_system_info", rfs_get_system_info);
|
|
engine.register_fn("rfs_is_authenticated", rfs_is_authenticated);
|
|
engine.register_fn("rfs_health_check", rfs_health_check);
|
|
|
|
// Register block management functions
|
|
engine.register_fn("rfs_list_blocks", rfs_list_blocks);
|
|
engine.register_fn("rfs_upload_block", rfs_upload_block);
|
|
engine.register_fn("rfs_check_block", rfs_check_block);
|
|
engine.register_fn("rfs_get_block_downloads", rfs_get_block_downloads);
|
|
engine.register_fn("rfs_verify_blocks", rfs_verify_blocks);
|
|
engine.register_fn("rfs_get_block", rfs_get_block);
|
|
engine.register_fn("rfs_get_blocks_by_hash", rfs_get_blocks_by_hash);
|
|
engine.register_fn("rfs_get_user_blocks", rfs_get_user_blocks);
|
|
|
|
// Register file operations functions
|
|
engine.register_fn("rfs_upload_file", rfs_upload_file);
|
|
engine.register_fn("rfs_download_file", rfs_download_file);
|
|
|
|
// Register FList management functions
|
|
engine.register_fn("rfs_create_flist", rfs_create_flist);
|
|
engine.register_fn("rfs_list_flists", rfs_list_flists);
|
|
engine.register_fn("rfs_get_flist_state", rfs_get_flist_state);
|
|
engine.register_fn("rfs_preview_flist", rfs_preview_flist);
|
|
engine.register_fn("rfs_download_flist", rfs_download_flist);
|
|
engine.register_fn("rfs_wait_for_flist_creation", rfs_wait_for_flist_creation);
|
|
|
|
// Register Website functions
|
|
engine.register_fn("rfs_get_website", rfs_get_website);
|
|
|
|
// Register System and Utility functions
|
|
engine.register_fn("rfs_is_authenticated", rfs_is_authenticated);
|
|
engine.register_fn("rfs_health_check", rfs_health_check);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
// Helper function to get or create the Tokio runtime
|
|
fn get_runtime() -> Result<&'static Mutex<Option<Runtime>>, Box<EvalAltResult>> {
|
|
let mut runtime = RUNTIME.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock runtime mutex: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
if runtime.is_none() {
|
|
let rt = Runtime::new().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to create Tokio runtime: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
*runtime = Some(rt);
|
|
}
|
|
|
|
drop(runtime);
|
|
Ok(&RUNTIME)
|
|
}
|
|
|
|
// Helper function to get the RFS client
|
|
fn get_rfs_client() -> Result<Arc<RfsClientWrapper>, Box<EvalAltResult>> {
|
|
let client_guard = RFS_CLIENT.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock client mutex: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
match client_guard.as_ref() {
|
|
Some(client) => Ok(Arc::clone(client)),
|
|
None => Err(Box::new(EvalAltResult::ErrorRuntime(
|
|
"RFS client not initialized. Call rfs_create_client first.".into(),
|
|
rhai::Position::NONE,
|
|
))),
|
|
}
|
|
}
|
|
|
|
// Helper function to convert serde_json::Value to rhai::Dynamic
|
|
fn to_dynamic(value: Value) -> Dynamic {
|
|
match value {
|
|
Value::Null => Dynamic::UNIT,
|
|
Value::Bool(b) => Dynamic::from(b),
|
|
Value::Number(n) => {
|
|
if let Some(i) = n.as_i64() {
|
|
Dynamic::from(i)
|
|
} else if let Some(f) = n.as_f64() {
|
|
Dynamic::from(f)
|
|
} else {
|
|
Dynamic::from(n.to_string())
|
|
}
|
|
}
|
|
Value::String(s) => Dynamic::from(s),
|
|
Value::Array(arr) => {
|
|
let mut rhai_arr = rhai::Array::new();
|
|
for item in arr {
|
|
rhai_arr.push(to_dynamic(item));
|
|
}
|
|
Dynamic::from(rhai_arr)
|
|
}
|
|
Value::Object(map) => {
|
|
let mut rhai_map = Map::new();
|
|
for (k, v) in map {
|
|
rhai_map.insert(k.into(), to_dynamic(v));
|
|
}
|
|
Dynamic::from_map(rhai_map)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Helper function to convert JSON string to Dynamic
|
|
fn json_to_dynamic(json_str: &str) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
let value: Value = serde_json::from_str(json_str).map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to parse JSON: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
Ok(to_dynamic(value))
|
|
}
|
|
|
|
//
|
|
// RFS Client Function Wrappers
|
|
//
|
|
|
|
/// Create a new RFS client
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `base_url` - The base URL of the RFS server
|
|
/// * `username` - Username for authentication
|
|
/// * `password` - Password for authentication
|
|
/// * `timeout_seconds` - Request timeout in seconds (optional, defaults to 30)
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// * `Result<bool, Box<EvalAltResult>>` - Ok(true) if client was created successfully
|
|
pub fn rfs_create_client(
|
|
base_url: &str,
|
|
username: &str,
|
|
password: &str,
|
|
timeout_seconds: rhai::INT,
|
|
) -> Result<bool, Box<EvalAltResult>> {
|
|
let credentials = if username.is_empty() || password.is_empty() {
|
|
None
|
|
} else {
|
|
Some(Credentials {
|
|
username: username.to_string(),
|
|
password: password.to_string(),
|
|
})
|
|
};
|
|
|
|
let client_config = ClientConfig {
|
|
base_url: base_url.to_string(),
|
|
credentials,
|
|
timeout_seconds: timeout_seconds as u64,
|
|
};
|
|
|
|
let client = RfsClient::new(client_config);
|
|
let wrapper = Arc::new(RfsClientWrapper::new(client));
|
|
|
|
let mut client_guard = RFS_CLIENT.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock client mutex: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
*client_guard = Some(wrapper);
|
|
Ok(true)
|
|
}
|
|
|
|
/// Authenticate with the RFS server
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// * `Result<bool, Box<EvalAltResult>>` - Ok(true) if authentication was successful
|
|
pub fn rfs_authenticate() -> Result<bool, Box<EvalAltResult>> {
|
|
let runtime_mutex = get_runtime()?;
|
|
let runtime_guard = runtime_mutex.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock runtime mutex: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let runtime = runtime_guard.as_ref().unwrap();
|
|
let client_wrapper = get_rfs_client()?;
|
|
|
|
let mut client = client_wrapper.client.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock client: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let result = runtime.block_on(async { client.authenticate().await });
|
|
|
|
result.map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Authentication failed: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
Ok(true)
|
|
}
|
|
|
|
/// Check if the client is authenticated with the RFS server
|
|
///
|
|
/// # Returns
|
|
/// `true` if authenticated, `false` otherwise
|
|
fn rfs_is_authenticated() -> Result<bool, Box<EvalAltResult>> {
|
|
let client_wrapper = get_rfs_client()?;
|
|
let client = client_wrapper.client.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock client: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
Ok(client.is_authenticated())
|
|
}
|
|
|
|
/// Get system information from the RFS server
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// * `Result<String, Box<EvalAltResult>>` - System information as JSON string
|
|
pub fn rfs_get_system_info() -> Result<String, Box<EvalAltResult>> {
|
|
let runtime_mutex = get_runtime()?;
|
|
let runtime_guard = runtime_mutex.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock runtime mutex: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let runtime = runtime_guard.as_ref().unwrap();
|
|
let client = get_rfs_client()?;
|
|
|
|
let client_guard = client.client.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock client: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let result = runtime.block_on(async { client_guard.get_system_info().await });
|
|
|
|
result.map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("RFS error: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})
|
|
}
|
|
|
|
/// Check the health status of the RFS server
|
|
///
|
|
/// # Returns
|
|
/// The health status as a string
|
|
fn rfs_health_check() -> Result<String, Box<EvalAltResult>> {
|
|
let runtime_mutex = get_runtime()?;
|
|
let runtime_guard = runtime_mutex.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock runtime mutex: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let runtime = runtime_guard.as_ref().unwrap();
|
|
let client_wrapper = get_rfs_client()?;
|
|
let client = client_wrapper.client.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock client: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let result = runtime.block_on(async {
|
|
client.health_check().await
|
|
});
|
|
|
|
result.map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Health check failed: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})
|
|
}
|
|
|
|
// =============================================================================
|
|
// Block Management Functions
|
|
// =============================================================================
|
|
|
|
/// List all blocks with optional filtering
|
|
///
|
|
/// # Arguments
|
|
/// * `page` - Optional page number (1-based)
|
|
/// * `per_page` - Optional number of items per page
|
|
///
|
|
/// # Returns
|
|
/// JSON string containing block information
|
|
fn rfs_list_blocks(
|
|
page: Option<rhai::INT>,
|
|
per_page: Option<rhai::INT>,
|
|
) -> Result<String, Box<EvalAltResult>> {
|
|
let runtime_mutex = get_runtime()?;
|
|
let runtime_guard = runtime_mutex.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock runtime mutex: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let runtime = runtime_guard.as_ref().unwrap();
|
|
let client_wrapper = get_rfs_client()?;
|
|
let client = client_wrapper.client.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock client: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
// Create ListBlocksParams with optional page and per_page
|
|
let mut params = openapi::models::ListBlocksParams::new();
|
|
|
|
// Convert Rhai INT to i32 for the API and set the parameters
|
|
if let Some(p) = page.and_then(|p| p.try_into().ok()) {
|
|
params.page = Some(Some(p));
|
|
}
|
|
|
|
if let Some(pp) = per_page.and_then(|p| p.try_into().ok()) {
|
|
params.per_page = Some(Some(pp));
|
|
}
|
|
|
|
let result = runtime.block_on(async {
|
|
client.list_blocks(Some(params)).await
|
|
});
|
|
|
|
match result {
|
|
Ok(blocks) => {
|
|
// Convert blocks to JSON string for Rhai
|
|
serde_json::to_string(&blocks).map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to serialize blocks: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})
|
|
}
|
|
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to list blocks: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))),
|
|
}
|
|
}
|
|
|
|
/// Check if a block exists
|
|
///
|
|
/// # Arguments
|
|
/// * `hash` - The hash of the block to check
|
|
///
|
|
/// # Returns
|
|
/// `true` if the block exists, `false` otherwise
|
|
fn rfs_check_block(hash: &str) -> Result<bool, Box<EvalAltResult>> {
|
|
let runtime_mutex = get_runtime()?;
|
|
let runtime_guard = runtime_mutex.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock runtime mutex: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let runtime = runtime_guard.as_ref().unwrap();
|
|
let client_wrapper = get_rfs_client()?;
|
|
let client = client_wrapper.client.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock client: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let result = runtime.block_on(async {
|
|
client.check_block(hash).await
|
|
});
|
|
|
|
result.map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to check block: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})
|
|
}
|
|
|
|
/// Get block download statistics
|
|
///
|
|
/// # Arguments
|
|
/// * `hash` - The hash of the block
|
|
///
|
|
/// # Returns
|
|
/// JSON string containing download statistics
|
|
fn rfs_get_block_downloads(hash: &str) -> Result<String, Box<EvalAltResult>> {
|
|
let runtime_mutex = get_runtime()?;
|
|
let runtime_guard = runtime_mutex.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock runtime mutex: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let runtime = runtime_guard.as_ref().unwrap();
|
|
let client_wrapper = get_rfs_client()?;
|
|
let client = client_wrapper.client.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock client: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let result = runtime.block_on(async {
|
|
client.get_block_downloads(hash).await
|
|
});
|
|
|
|
match result {
|
|
Ok(stats) => {
|
|
// Convert stats to JSON string for Rhai
|
|
serde_json::to_string(&stats).map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to serialize block stats: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})
|
|
}
|
|
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to get block downloads: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))),
|
|
}
|
|
}
|
|
|
|
/// Verify blocks
|
|
///
|
|
/// # Arguments
|
|
/// * `hashes` - JSON array of block hashes to verify
|
|
///
|
|
/// # Returns
|
|
/// JSON string containing verification results
|
|
fn rfs_verify_blocks(hashes: &str) -> Result<String, Box<EvalAltResult>> {
|
|
// Parse the JSON array of hashes
|
|
let hashes_vec: Vec<String> = match serde_json::from_str(hashes) {
|
|
Ok(h) => h,
|
|
Err(e) => {
|
|
return Err(Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to parse hashes: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
)));
|
|
}
|
|
};
|
|
|
|
let runtime_mutex = get_runtime()?;
|
|
let runtime_guard = runtime_mutex.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock runtime mutex: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let runtime = runtime_guard.as_ref().unwrap();
|
|
let client_wrapper = get_rfs_client()?;
|
|
let client = client_wrapper.client.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock client: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
// Convert string hashes to VerifyBlock objects
|
|
// For now, we'll use the hash as both block_hash and file_hash, and use 0 as block_index
|
|
// In a real implementation, you might want to pass these as separate parameters
|
|
let verify_blocks: Vec<openapi::models::VerifyBlock> = hashes_vec
|
|
.into_iter()
|
|
.map(|block_hash| openapi::models::VerifyBlock {
|
|
block_hash: block_hash.clone(),
|
|
block_index: 0, // Default to 0 if not specified
|
|
file_hash: block_hash, // Using the same hash as file_hash for now
|
|
})
|
|
.collect();
|
|
|
|
let request = openapi::models::VerifyBlocksRequest::new(verify_blocks);
|
|
let result = runtime.block_on(async {
|
|
client.verify_blocks(request).await
|
|
});
|
|
|
|
match result {
|
|
Ok(verification) => {
|
|
// Convert verification to JSON string for Rhai
|
|
serde_json::to_string(&verification).map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to serialize verification results: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})
|
|
}
|
|
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to verify blocks: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))),
|
|
}
|
|
}
|
|
|
|
/// Get a block by hash
|
|
///
|
|
/// # Arguments
|
|
/// * `hash` - The hash of the block to retrieve
|
|
///
|
|
/// # Returns
|
|
/// The block data as a byte array
|
|
fn rfs_get_block(hash: &str) -> Result<rhai::Blob, Box<EvalAltResult>> {
|
|
let runtime_mutex = get_runtime()?;
|
|
let runtime_guard = runtime_mutex.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock runtime mutex: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let runtime = runtime_guard.as_ref().unwrap();
|
|
let client_wrapper = get_rfs_client()?;
|
|
let client = client_wrapper.client.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock client: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let result = runtime.block_on(async {
|
|
client.get_block(hash).await
|
|
});
|
|
|
|
match result {
|
|
Ok(bytes) => Ok(bytes.to_vec().into()),
|
|
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to get block: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))),
|
|
}
|
|
}
|
|
|
|
/// Get blocks by file hash or block hash
|
|
///
|
|
/// # Arguments
|
|
/// * `hash` - The file hash or block hash to look up
|
|
///
|
|
/// # Returns
|
|
/// JSON string containing block information
|
|
fn rfs_get_blocks_by_hash(hash: &str) -> Result<String, Box<EvalAltResult>> {
|
|
let runtime_mutex = get_runtime()?;
|
|
let runtime_guard = runtime_mutex.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock runtime mutex: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let runtime = runtime_guard.as_ref().unwrap();
|
|
let client_wrapper = get_rfs_client()?;
|
|
let client = client_wrapper.client.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock client: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let result = runtime.block_on(async {
|
|
client.get_blocks_by_hash(hash).await
|
|
});
|
|
|
|
match result {
|
|
Ok(blocks) => {
|
|
// Convert blocks to JSON string for Rhai
|
|
serde_json::to_string(&blocks).map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to serialize blocks: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})
|
|
}
|
|
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to get blocks by hash: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))),
|
|
}
|
|
}
|
|
|
|
/// Get blocks uploaded by the current user
|
|
///
|
|
/// # Arguments
|
|
/// * `page` - Optional page number (1-based)
|
|
/// * `per_page` - Optional number of items per page
|
|
///
|
|
/// # Returns
|
|
/// JSON string containing user's blocks information
|
|
fn rfs_get_user_blocks(
|
|
page: Option<rhai::INT>,
|
|
per_page: Option<rhai::INT>,
|
|
) -> Result<String, Box<EvalAltResult>> {
|
|
let runtime_mutex = get_runtime()?;
|
|
let runtime_guard = runtime_mutex.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock runtime mutex: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let runtime = runtime_guard.as_ref().unwrap();
|
|
let client_wrapper = get_rfs_client()?;
|
|
let client = client_wrapper.client.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock client: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
// Convert Rhai INT to i32 for the API
|
|
let page_i32 = page.and_then(|p| p.try_into().ok());
|
|
let per_page_i32 = per_page.and_then(|p| p.try_into().ok());
|
|
|
|
let result = runtime.block_on(async {
|
|
client.get_user_blocks(page_i32, per_page_i32).await
|
|
});
|
|
|
|
match result {
|
|
Ok(user_blocks) => {
|
|
// Convert user blocks to JSON string for Rhai
|
|
serde_json::to_string(&user_blocks).map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to serialize user blocks: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})
|
|
}
|
|
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to get user blocks: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))),
|
|
}
|
|
}
|
|
|
|
/// Upload a block to the RFS server
|
|
///
|
|
/// # Arguments
|
|
/// * `file_hash` - The hash of the file this block belongs to
|
|
/// * `index` - The index of the block in the file
|
|
/// * `data` - The block data as a byte array
|
|
///
|
|
/// # Returns
|
|
/// The hash of the uploaded block
|
|
fn rfs_upload_block(file_hash: &str, index: rhai::INT, data: rhai::Blob) -> Result<String, Box<EvalAltResult>> {
|
|
let runtime_mutex = get_runtime()?;
|
|
let runtime_guard = runtime_mutex.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock runtime mutex: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let runtime = runtime_guard.as_ref().unwrap();
|
|
let client_wrapper = get_rfs_client()?;
|
|
let client = client_wrapper.client.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock client: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
// Convert index to i64 for the API
|
|
let index_i64 = index as i64;
|
|
|
|
// Convert the blob to Vec<u8>
|
|
let data_vec = data.to_vec();
|
|
|
|
let result = runtime.block_on(async {
|
|
client.upload_block(file_hash, index_i64, data_vec).await
|
|
});
|
|
|
|
result.map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to upload block: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})
|
|
}
|
|
|
|
/// Upload a file to the RFS server
|
|
/// * `index` - The index of the block in the file
|
|
/// * `data` - The block data as a byte array
|
|
///
|
|
/// # Returns
|
|
/// The hash of the uploaded block
|
|
|
|
|
|
// =============================================================================
|
|
// File Operations
|
|
// =============================================================================
|
|
|
|
/// Download a file from the RFS server
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `file_id` - The ID of the file to download
|
|
/// * `output_path` - Path where the downloaded file will be saved
|
|
/// * `verify` - Whether to verify blocks during download
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// * `Result<(), Box<EvalAltResult>>` - Ok(()) if download was successful, error otherwise
|
|
fn rfs_download_file(file_id: &str, output_path: &str, verify: bool) -> Result<(), Box<EvalAltResult>> {
|
|
let runtime_mutex = get_runtime()?;
|
|
let runtime_guard = runtime_mutex.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock runtime mutex: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let runtime = runtime_guard.as_ref().unwrap();
|
|
let client_wrapper = get_rfs_client()?;
|
|
let client = client_wrapper.client.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock client: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let download_options = Some(DownloadOptions { verify });
|
|
let result = runtime.block_on(async {
|
|
client.download_file(file_id, output_path, download_options).await
|
|
});
|
|
|
|
result.map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to download file: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})
|
|
}
|
|
|
|
/// Upload a file to the RFS server
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `file_path` - Path to the file to upload
|
|
/// * `chunk_size` - Optional chunk size for large files (0 for default)
|
|
/// * `verify` - Whether to verify blocks after upload
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// * `Result<String, Box<EvalAltResult>>` - File ID of the uploaded file
|
|
pub fn rfs_upload_file(file_path: &str, chunk_size: rhai::INT, verify: bool) -> Result<String, Box<EvalAltResult>> {
|
|
let runtime_mutex = get_runtime()?;
|
|
let runtime_guard = runtime_mutex.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock runtime mutex: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let runtime = runtime_guard.as_ref().unwrap();
|
|
let client_wrapper = get_rfs_client()?;
|
|
|
|
let client = client_wrapper.client.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock client: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let upload_options = Some(UploadOptions {
|
|
chunk_size: if chunk_size > 0 { Some(chunk_size as usize) } else { None },
|
|
verify,
|
|
});
|
|
|
|
let result = runtime.block_on(async { client.upload_file(file_path, upload_options).await });
|
|
|
|
result.map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("RFS error: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})
|
|
}
|
|
|
|
// =============================================================================
|
|
// Website Functions
|
|
// =============================================================================
|
|
|
|
/// Get website content from the RFS server
|
|
///
|
|
/// # Arguments
|
|
/// * `website_id` - The ID of the website
|
|
/// * `path` - The path to the content within the website
|
|
///
|
|
/// # Returns
|
|
/// The website content as a string
|
|
fn rfs_get_website(website_id: &str, path: &str) -> Result<String, Box<EvalAltResult>> {
|
|
let runtime_mutex = get_runtime()?;
|
|
let runtime_guard = runtime_mutex.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock runtime mutex: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let runtime = runtime_guard.as_ref().unwrap();
|
|
let client_wrapper = get_rfs_client()?;
|
|
let client = client_wrapper.client.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock client: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let result = runtime.block_on(async {
|
|
let response = client.get_website(website_id, path).await?;
|
|
response.text().await.map_err(|e| RfsError::RequestError(e.into()))
|
|
});
|
|
|
|
result.map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to get website content: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})
|
|
}
|
|
|
|
// =============================================================================
|
|
// FList Management Functions
|
|
// =============================================================================
|
|
|
|
/// Create an FList from a Docker image
|
|
///
|
|
/// # Arguments
|
|
/// * `image_name` - Docker image name (e.g., "ubuntu:20.04")
|
|
/// * `server_address` - Optional server address (empty string if not needed)
|
|
/// * `identity_token` - Optional identity token (empty string if not needed)
|
|
/// * `registry_token` - Optional registry token (empty string if not needed)
|
|
///
|
|
/// # Returns
|
|
/// Job ID for tracking FList creation progress
|
|
fn rfs_create_flist(
|
|
image_name: &str,
|
|
server_address: &str,
|
|
identity_token: &str,
|
|
registry_token: &str,
|
|
) -> Result<String, Box<EvalAltResult>> {
|
|
let runtime_mutex = get_runtime()?;
|
|
let runtime_guard = runtime_mutex.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock runtime mutex: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let runtime = runtime_guard.as_ref().unwrap();
|
|
let client_wrapper = get_rfs_client()?;
|
|
|
|
let client = client_wrapper.client.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock client: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
// Build FList options
|
|
let mut options = crate::types::FlistOptions::default();
|
|
if !server_address.is_empty() {
|
|
options.server_address = Some(server_address.to_string());
|
|
}
|
|
if !identity_token.is_empty() {
|
|
options.identity_token = Some(identity_token.to_string());
|
|
}
|
|
if !registry_token.is_empty() {
|
|
options.registry_token = Some(registry_token.to_string());
|
|
}
|
|
|
|
let result = runtime.block_on(async { client.create_flist(image_name, Some(options)).await });
|
|
|
|
result.map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("FList creation failed: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})
|
|
}
|
|
|
|
/// List all available FLists
|
|
///
|
|
/// # Returns
|
|
/// JSON string containing FList information
|
|
fn rfs_list_flists() -> Result<String, Box<EvalAltResult>> {
|
|
let runtime_mutex = get_runtime()?;
|
|
let runtime_guard = runtime_mutex.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock runtime mutex: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let runtime = runtime_guard.as_ref().unwrap();
|
|
let client_wrapper = get_rfs_client()?;
|
|
|
|
let client = client_wrapper.client.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock client: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let result = runtime.block_on(async { client.list_flists().await });
|
|
|
|
match result {
|
|
Ok(flists) => {
|
|
// Convert HashMap to JSON string for Rhai
|
|
serde_json::to_string(&flists).map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to serialize FList data: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})
|
|
}
|
|
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to list FLists: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))),
|
|
}
|
|
}
|
|
|
|
/// Get FList creation state by job ID
|
|
///
|
|
/// # Arguments
|
|
/// * `job_id` - Job ID returned from create_flist
|
|
///
|
|
/// # Returns
|
|
/// JSON string containing FList state information
|
|
fn rfs_get_flist_state(job_id: &str) -> Result<String, Box<EvalAltResult>> {
|
|
let runtime_mutex = get_runtime()?;
|
|
let runtime_guard = runtime_mutex.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock runtime mutex: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let runtime = runtime_guard.as_ref().unwrap();
|
|
let client_wrapper = get_rfs_client()?;
|
|
|
|
let client = client_wrapper.client.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock client: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let result = runtime.block_on(async { client.get_flist_state(job_id).await });
|
|
|
|
match result {
|
|
Ok(state) => {
|
|
// Convert state to JSON string for Rhai
|
|
serde_json::to_string(&state).map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to serialize FList state: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})
|
|
}
|
|
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to get FList state: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))),
|
|
}
|
|
}
|
|
|
|
/// Preview an FList's contents
|
|
///
|
|
/// # Arguments
|
|
/// * `flist_path` - Path to the FList
|
|
///
|
|
/// # Returns
|
|
/// JSON string containing FList preview information
|
|
fn rfs_preview_flist(flist_path: &str) -> Result<String, Box<EvalAltResult>> {
|
|
let runtime_mutex = get_runtime()?;
|
|
let runtime_guard = runtime_mutex.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock runtime mutex: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let runtime = runtime_guard.as_ref().unwrap();
|
|
let client_wrapper = get_rfs_client()?;
|
|
|
|
let client = client_wrapper.client.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock client: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let result = runtime.block_on(async { client.preview_flist(flist_path).await });
|
|
|
|
match result {
|
|
Ok(preview) => {
|
|
// Convert preview to JSON string for Rhai
|
|
serde_json::to_string(&preview).map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to serialize FList preview: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})
|
|
}
|
|
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to preview FList: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))),
|
|
}
|
|
}
|
|
|
|
/// Download an FList file from the RFS server
|
|
///
|
|
/// # Arguments
|
|
/// * `flist_path` - Path to the FList to download (e.g., "flists/user/example.fl")
|
|
/// * `output_path` - Local path where the FList will be saved
|
|
///
|
|
/// # Returns
|
|
/// Empty string on success, error on failure
|
|
fn rfs_download_flist(flist_path: &str, output_path: &str) -> Result<String, Box<EvalAltResult>> {
|
|
let runtime_mutex = get_runtime()?;
|
|
let runtime_guard = runtime_mutex.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock runtime mutex: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let runtime = runtime_guard.as_ref().unwrap();
|
|
let client_wrapper = get_rfs_client()?;
|
|
let client = client_wrapper.client.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock client: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let result = runtime.block_on(async {
|
|
client.download_flist(flist_path, output_path).await
|
|
});
|
|
|
|
match result {
|
|
Ok(_) => Ok(String::new()),
|
|
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to download FList: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))),
|
|
}
|
|
}
|
|
|
|
/// Wait for an FList to be created
|
|
///
|
|
/// # Arguments
|
|
/// * `job_id` - The job ID returned by rfs_create_flist
|
|
/// * `timeout_seconds` - Maximum time to wait in seconds (default: 300)
|
|
/// * `poll_interval_ms` - Polling interval in milliseconds (default: 1000)
|
|
///
|
|
/// # Returns
|
|
/// JSON string containing the final FList state
|
|
fn rfs_wait_for_flist_creation(
|
|
job_id: &str,
|
|
timeout_seconds: Option<rhai::INT>,
|
|
poll_interval_ms: Option<rhai::INT>,
|
|
) -> Result<String, Box<EvalAltResult>> {
|
|
let runtime_mutex = get_runtime()?;
|
|
let runtime_guard = runtime_mutex.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock runtime mutex: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let runtime = runtime_guard.as_ref().unwrap();
|
|
let client_wrapper = get_rfs_client()?;
|
|
let client = client_wrapper.client.lock().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to lock client: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let options = WaitOptions {
|
|
timeout_seconds: timeout_seconds.unwrap_or(300) as u64,
|
|
poll_interval_ms: poll_interval_ms.unwrap_or(1000) as u64,
|
|
progress_callback: None,
|
|
};
|
|
|
|
let result = runtime.block_on(async {
|
|
client.wait_for_flist_creation(job_id, Some(options)).await
|
|
});
|
|
|
|
match result {
|
|
Ok(state) => {
|
|
// Convert state to JSON string for Rhai
|
|
serde_json::to_string(&state).map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to serialize FList state: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})
|
|
}
|
|
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("Failed to wait for FList creation: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))),
|
|
}
|
|
}
|