//! Shared transport helpers for oversized extension requests and responses. //! //! This module provides a routed invoke path that accepts JSON-encoded string //! arguments, supports request staging for large payloads, and stores oversized //! responses in memory for chunked retrieval by SQF. use arma_rs::{CallContext, Group}; use serde::Serialize; use std::collections::HashMap; use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::{LazyLock, Mutex as StdMutex}; use crate::{actor, bank, cad, garage, locker, org, v_garage, v_locker}; const CHUNK_PREFIX: &str = "FORGE_TRANSPORT_CHUNK:"; const RESPONSE_CHUNK_SIZE: usize = 12_000; const UNSUPPORTED_ROUTE_PREFIX: &str = "Unsupported transport route"; static REQUEST_STORE: LazyLock>> = LazyLock::new(|| StdMutex::new(HashMap::new())); static RESPONSE_STORE: LazyLock>>> = LazyLock::new(|| StdMutex::new(HashMap::new())); static TRANSFER_SEQUENCE: AtomicU64 = AtomicU64::new(1); #[derive(Serialize)] #[serde(rename_all = "camelCase")] struct ChunkEnvelope { transfer_id: String, chunk_count: usize, total_size: usize, } pub fn group() -> Group { Group::new() .command("invoke", invoke) .command("invoke_stored", invoke_stored) .group( "request", Group::new() .command("append", append_request_chunk) .command("clear", clear_request_chunks), ) .group( "response", Group::new() .command("get", get_response_chunk) .command("clear", clear_response_chunks), ) } fn append_request_chunk(transfer_id: String, chunk: String) -> String { let mut store = REQUEST_STORE.lock().unwrap(); store.entry(transfer_id).or_default().push_str(&chunk); "OK".to_string() } fn clear_request_chunks(transfer_id: String) -> String { REQUEST_STORE.lock().unwrap().remove(&transfer_id); "OK".to_string() } fn get_response_chunk(transfer_id: String, index: String) -> String { let chunk_index = match index.parse::() { Ok(value) => value, Err(error) => return format!("Error: Invalid response chunk index: {error}"), }; let store = RESPONSE_STORE.lock().unwrap(); let Some(chunks) = store.get(&transfer_id) else { return format!("Error: Response transfer '{transfer_id}' was not found"); }; chunks.get(chunk_index).cloned().unwrap_or_else(|| { format!( "Error: Response chunk {} was not found for '{}'", chunk_index, transfer_id ) }) } fn clear_response_chunks(transfer_id: String) -> String { RESPONSE_STORE.lock().unwrap().remove(&transfer_id); "OK".to_string() } fn invoke(call_context: CallContext, function_name: String, arguments_json: String) -> String { invoke_internal(call_context, function_name, arguments_json) } fn invoke_stored(call_context: CallContext, function_name: String, transfer_id: String) -> String { let Some(arguments_json) = REQUEST_STORE.lock().unwrap().remove(&transfer_id) else { return format!("Error: Request transfer '{transfer_id}' was not found"); }; invoke_internal(call_context, function_name, arguments_json) } fn invoke_internal( call_context: CallContext, function_name: String, arguments_json: String, ) -> String { let arguments: Vec = match parse_transport_arguments(&arguments_json) { Ok(value) => value, Err(error) => return format!("Error: Invalid transport arguments JSON: {error}"), }; let result = match route_command(call_context, &function_name, arguments) { Ok(value) => value, Err(error) => format!("Error: {error}"), }; chunk_response_if_needed(result) } fn parse_transport_arguments(arguments_json: &str) -> Result, String> { let value: serde_json::Value = serde_json::from_str(arguments_json).map_err(|error| error.to_string())?; parse_transport_argument_value(value) } fn parse_transport_argument_value(value: serde_json::Value) -> Result, String> { match value { serde_json::Value::Array(values) => Ok(values .into_iter() .map(|entry| match entry { serde_json::Value::String(string_value) => string_value, other => other.to_string(), }) .collect()), serde_json::Value::String(value) => { let trimmed = value.trim(); if trimmed.starts_with('[') || trimmed.starts_with('{') || trimmed.eq("null") { if let Ok(nested_value) = serde_json::from_str::(trimmed) { return parse_transport_argument_value(nested_value); } } Ok(vec![value]) } serde_json::Value::Null => Ok(Vec::new()), other => Err(format!("expected string or array but received {}", other)), } } fn route_command( call_context: CallContext, function_name: &str, arguments: Vec, ) -> Result { match function_name { "actor:get" => { expect_arg_count(function_name, &arguments, 1)?; Ok(actor::get_actor(call_context, arguments[0].clone())) } "actor:create" => { expect_arg_count(function_name, &arguments, 2)?; Ok(actor::create_actor( call_context, arguments[0].clone(), arguments[1].clone(), )) } "actor:update" => { expect_arg_count(function_name, &arguments, 2)?; Ok(actor::update_actor( call_context, arguments[0].clone(), arguments[1].clone(), )) } "actor:exists" => { expect_arg_count(function_name, &arguments, 1)?; Ok(actor::actor_exists(call_context, arguments[0].clone())) } "actor:delete" => { expect_arg_count(function_name, &arguments, 1)?; Ok(actor::delete_actor(call_context, arguments[0].clone())) } "actor:hot:init" => { expect_arg_count(function_name, &arguments, 1)?; Ok(actor::init_hot_actor(call_context, arguments[0].clone())) } "actor:hot:get" => { expect_arg_count(function_name, &arguments, 1)?; Ok(actor::get_hot_actor(call_context, arguments[0].clone())) } "actor:hot:override" => { expect_arg_count(function_name, &arguments, 2)?; Ok(actor::override_hot_actor( call_context, arguments[0].clone(), arguments[1].clone(), )) } "actor:hot:save" => { expect_arg_count(function_name, &arguments, 1)?; Ok(actor::save_hot_actor(call_context, arguments[0].clone())) } "actor:hot:remove" => { expect_arg_count(function_name, &arguments, 1)?; Ok(actor::remove_hot_actor(call_context, arguments[0].clone())) } "bank:get" => { expect_arg_count(function_name, &arguments, 1)?; Ok(bank::get_bank(call_context, arguments[0].clone())) } "bank:create" => { expect_arg_count(function_name, &arguments, 2)?; Ok(bank::create_bank( call_context, arguments[0].clone(), arguments[1].clone(), )) } "bank:update" => { expect_arg_count(function_name, &arguments, 2)?; Ok(bank::update_bank( call_context, arguments[0].clone(), arguments[1].clone(), )) } "bank:exists" => { expect_arg_count(function_name, &arguments, 1)?; Ok(bank::bank_exists(call_context, arguments[0].clone())) } "bank:delete" => { expect_arg_count(function_name, &arguments, 1)?; Ok(bank::delete_bank(call_context, arguments[0].clone())) } "bank:hot:init" => { expect_arg_count(function_name, &arguments, 1)?; Ok(bank::init_hot_bank(call_context, arguments[0].clone())) } "bank:hot:get" => { expect_arg_count(function_name, &arguments, 1)?; Ok(bank::get_hot_bank(call_context, arguments[0].clone())) } "bank:hot:override" => { expect_arg_count(function_name, &arguments, 2)?; Ok(bank::override_hot_bank( call_context, arguments[0].clone(), arguments[1].clone(), )) } "bank:hot:patch" => { expect_arg_count(function_name, &arguments, 2)?; Ok(bank::patch_hot_bank( call_context, arguments[0].clone(), arguments[1].clone(), )) } "bank:hot:charge_checkout" => { expect_arg_count(function_name, &arguments, 3)?; Ok(bank::charge_checkout_hot_bank( call_context, arguments[0].clone(), arguments[1].clone(), arguments[2].clone(), )) } "bank:hot:deposit" => { expect_arg_count(function_name, &arguments, 3)?; Ok(bank::deposit_hot_bank( call_context, arguments[0].clone(), arguments[1].clone(), arguments[2].clone(), )) } "bank:hot:withdraw" => { expect_arg_count(function_name, &arguments, 3)?; Ok(bank::withdraw_hot_bank( call_context, arguments[0].clone(), arguments[1].clone(), arguments[2].clone(), )) } "bank:hot:deposit_earnings" => { expect_arg_count(function_name, &arguments, 3)?; Ok(bank::deposit_earnings_hot_bank( call_context, arguments[0].clone(), arguments[1].clone(), arguments[2].clone(), )) } "bank:hot:transfer" => { expect_arg_count(function_name, &arguments, 4)?; Ok(bank::transfer_hot_bank( call_context, arguments[0].clone(), arguments[1].clone(), arguments[2].clone(), arguments[3].clone(), )) } "bank:hot:validate_pin" => { expect_arg_count(function_name, &arguments, 3)?; Ok(bank::validate_pin_hot_bank( call_context, arguments[0].clone(), arguments[1].clone(), arguments[2].clone(), )) } "bank:hot:save" => { expect_arg_count(function_name, &arguments, 1)?; Ok(bank::save_hot_bank(call_context, arguments[0].clone())) } "bank:hot:remove" => { expect_arg_count(function_name, &arguments, 1)?; Ok(bank::remove_hot_bank(call_context, arguments[0].clone())) } "org:get" => { expect_arg_count(function_name, &arguments, 1)?; Ok(org::get_org(arguments[0].clone())) } "org:create" => { expect_arg_count(function_name, &arguments, 2)?; Ok(org::create_org(arguments[0].clone(), arguments[1].clone())) } "org:update" => { expect_arg_count(function_name, &arguments, 2)?; Ok(org::update_org(arguments[0].clone(), arguments[1].clone())) } "org:exists" => { expect_arg_count(function_name, &arguments, 1)?; Ok(org::org_exists(arguments[0].clone())) } "org:delete" => { expect_arg_count(function_name, &arguments, 1)?; Ok(org::delete_org(arguments[0].clone())) } "org:hot:init" => { expect_arg_count(function_name, &arguments, 1)?; Ok(org::init_hot_org(arguments[0].clone())) } "org:hot:get" => { expect_arg_count(function_name, &arguments, 1)?; Ok(org::get_hot_org(arguments[0].clone())) } "org:hot:override" => { expect_arg_count(function_name, &arguments, 2)?; Ok(org::override_hot_org( arguments[0].clone(), arguments[1].clone(), )) } "org:hot:save" => { expect_arg_count(function_name, &arguments, 1)?; Ok(org::save_hot_org(arguments[0].clone())) } "org:hot:remove" => { expect_arg_count(function_name, &arguments, 1)?; Ok(org::remove_hot_org(arguments[0].clone())) } "org:assets:get" => { expect_arg_count(function_name, &arguments, 1)?; Ok(org::get_assets(arguments[0].clone())) } "org:assets:update" => { expect_arg_count(function_name, &arguments, 2)?; Ok(org::update_assets( arguments[0].clone(), arguments[1].clone(), )) } "org:fleet:get" => { expect_arg_count(function_name, &arguments, 1)?; Ok(org::get_fleet(arguments[0].clone())) } "org:fleet:update" => { expect_arg_count(function_name, &arguments, 2)?; Ok(org::update_fleet( arguments[0].clone(), arguments[1].clone(), )) } "org:members:get" => { expect_arg_count(function_name, &arguments, 1)?; Ok(org::get_members(arguments[0].clone())) } "org:members:add" => { expect_arg_count(function_name, &arguments, 2)?; Ok(org::add_member(arguments[0].clone(), arguments[1].clone())) } "org:members:remove" => { expect_arg_count(function_name, &arguments, 2)?; Ok(org::remove_member( arguments[0].clone(), arguments[1].clone(), )) } "garage:create" => { expect_arg_count(function_name, &arguments, 1)?; Ok(garage::create_garage(call_context, arguments[0].clone())) } "garage:get" => { expect_arg_count(function_name, &arguments, 1)?; Ok(garage::get_garage(call_context, arguments[0].clone())) } "garage:add" => { expect_arg_count(function_name, &arguments, 2)?; Ok(garage::add_vehicle( call_context, arguments[0].clone(), arguments[1].clone(), )) } "garage:update" => { expect_arg_count(function_name, &arguments, 2)?; Ok(garage::update_garage( call_context, arguments[0].clone(), arguments[1].clone(), )) } "garage:patch" => { expect_arg_count(function_name, &arguments, 2)?; Ok(garage::patch_vehicle( call_context, arguments[0].clone(), arguments[1].clone(), )) } "garage:remove" => { expect_arg_count(function_name, &arguments, 2)?; Ok(garage::remove_vehicle( call_context, arguments[0].clone(), arguments[1].clone(), )) } "garage:delete" => { expect_arg_count(function_name, &arguments, 1)?; Ok(garage::delete_garage(call_context, arguments[0].clone())) } "garage:exists" => { expect_arg_count(function_name, &arguments, 1)?; Ok(garage::garage_exists(call_context, arguments[0].clone())) } "garage:hot:init" => { expect_arg_count(function_name, &arguments, 1)?; Ok(garage::init_hot_garage(call_context, arguments[0].clone())) } "garage:hot:get" => { expect_arg_count(function_name, &arguments, 1)?; Ok(garage::get_hot_garage(call_context, arguments[0].clone())) } "garage:hot:override" => { expect_arg_count(function_name, &arguments, 2)?; Ok(garage::override_hot_garage( call_context, arguments[0].clone(), arguments[1].clone(), )) } "garage:hot:save" => { expect_arg_count(function_name, &arguments, 1)?; Ok(garage::save_hot_garage(call_context, arguments[0].clone())) } "garage:hot:remove" => { expect_arg_count(function_name, &arguments, 1)?; Ok(garage::remove_hot_garage( call_context, arguments[0].clone(), )) } "garage:hot:add" => { expect_arg_count(function_name, &arguments, 2)?; Ok(garage::add_hot_vehicle( call_context, arguments[0].clone(), arguments[1].clone(), )) } "garage:hot:remove_vehicle" => { expect_arg_count(function_name, &arguments, 2)?; Ok(garage::remove_hot_vehicle( call_context, arguments[0].clone(), arguments[1].clone(), )) } "locker:create" => { expect_arg_count(function_name, &arguments, 1)?; Ok(locker::create_locker(call_context, arguments[0].clone())) } "locker:get" => { expect_arg_count(function_name, &arguments, 1)?; Ok(locker::get_locker(call_context, arguments[0].clone())) } "locker:add" => { expect_arg_count(function_name, &arguments, 2)?; Ok(locker::add_item( call_context, arguments[0].clone(), arguments[1].clone(), )) } "locker:update" => { expect_arg_count(function_name, &arguments, 2)?; Ok(locker::update_locker( call_context, arguments[0].clone(), arguments[1].clone(), )) } "locker:patch" => { expect_arg_count(function_name, &arguments, 2)?; Ok(locker::patch_item( call_context, arguments[0].clone(), arguments[1].clone(), )) } "locker:remove" => { expect_arg_count(function_name, &arguments, 2)?; Ok(locker::remove_item( call_context, arguments[0].clone(), arguments[1].clone(), )) } "locker:delete" => { expect_arg_count(function_name, &arguments, 1)?; Ok(locker::delete_locker(call_context, arguments[0].clone())) } "locker:exists" => { expect_arg_count(function_name, &arguments, 1)?; Ok(locker::locker_exists(call_context, arguments[0].clone())) } "locker:hot:init" => { expect_arg_count(function_name, &arguments, 1)?; Ok(locker::init_hot_locker(call_context, arguments[0].clone())) } "locker:hot:get" => { expect_arg_count(function_name, &arguments, 1)?; Ok(locker::get_hot_locker(call_context, arguments[0].clone())) } "locker:hot:override" => { expect_arg_count(function_name, &arguments, 2)?; Ok(locker::override_hot_locker( call_context, arguments[0].clone(), arguments[1].clone(), )) } "locker:hot:save" => { expect_arg_count(function_name, &arguments, 1)?; Ok(locker::save_hot_locker(call_context, arguments[0].clone())) } "locker:hot:remove" => { expect_arg_count(function_name, &arguments, 1)?; Ok(locker::remove_hot_locker( call_context, arguments[0].clone(), )) } "owned:garage:create" => { expect_arg_count(function_name, &arguments, 1)?; Ok(v_garage::create_vgarage(call_context, arguments[0].clone())) } "owned:garage:fetch" => { expect_arg_count(function_name, &arguments, 1)?; Ok(v_garage::fetch_vgarage(call_context, arguments[0].clone())) } "owned:garage:get" => { expect_arg_count(function_name, &arguments, 2)?; Ok(v_garage::get_vgarage( call_context, arguments[0].clone(), arguments[1].clone(), )) } "owned:garage:add" => { expect_arg_count(function_name, &arguments, 3)?; Ok(v_garage::add_vgarage( call_context, arguments[0].clone(), arguments[1].clone(), arguments[2].clone(), )) } "owned:garage:remove" => { expect_arg_count(function_name, &arguments, 3)?; Ok(v_garage::remove_vgarage( call_context, arguments[0].clone(), arguments[1].clone(), arguments[2].clone(), )) } "owned:garage:delete" => { expect_arg_count(function_name, &arguments, 1)?; Ok(v_garage::delete_vgarage(call_context, arguments[0].clone())) } "owned:garage:exists" => { expect_arg_count(function_name, &arguments, 1)?; Ok(v_garage::vgarage_exists(call_context, arguments[0].clone())) } "owned:garage:hot:init" => { expect_arg_count(function_name, &arguments, 1)?; Ok(v_garage::init_hot_vgarage( call_context, arguments[0].clone(), )) } "owned:garage:hot:fetch" => { expect_arg_count(function_name, &arguments, 1)?; Ok(v_garage::fetch_hot_vgarage( call_context, arguments[0].clone(), )) } "owned:garage:hot:get" => { expect_arg_count(function_name, &arguments, 2)?; Ok(v_garage::get_hot_vgarage( call_context, arguments[0].clone(), arguments[1].clone(), )) } "owned:garage:hot:override" => { expect_arg_count(function_name, &arguments, 2)?; Ok(v_garage::override_hot_vgarage( call_context, arguments[0].clone(), arguments[1].clone(), )) } "owned:garage:hot:save" => { expect_arg_count(function_name, &arguments, 1)?; Ok(v_garage::save_hot_vgarage( call_context, arguments[0].clone(), )) } "owned:garage:hot:remove" => { expect_arg_count(function_name, &arguments, 1)?; Ok(v_garage::remove_hot_vgarage( call_context, arguments[0].clone(), )) } "owned:garage:hot:add" => { expect_arg_count(function_name, &arguments, 3)?; Ok(v_garage::add_hot_vgarage( call_context, arguments[0].clone(), arguments[1].clone(), arguments[2].clone(), )) } "owned:garage:hot:remove_item" => { expect_arg_count(function_name, &arguments, 3)?; Ok(v_garage::remove_hot_vgarage_item( call_context, arguments[0].clone(), arguments[1].clone(), arguments[2].clone(), )) } "owned:locker:create" => { expect_arg_count(function_name, &arguments, 1)?; Ok(v_locker::create_vlocker(call_context, arguments[0].clone())) } "owned:locker:fetch" => { expect_arg_count(function_name, &arguments, 1)?; Ok(v_locker::fetch_vlocker(call_context, arguments[0].clone())) } "owned:locker:get" => { expect_arg_count(function_name, &arguments, 2)?; Ok(v_locker::get_vlocker( call_context, arguments[0].clone(), arguments[1].clone(), )) } "owned:locker:add" => { expect_arg_count(function_name, &arguments, 3)?; Ok(v_locker::add_vlocker( call_context, arguments[0].clone(), arguments[1].clone(), arguments[2].clone(), )) } "owned:locker:remove" => { expect_arg_count(function_name, &arguments, 3)?; Ok(v_locker::remove_vlocker( call_context, arguments[0].clone(), arguments[1].clone(), arguments[2].clone(), )) } "owned:locker:delete" => { expect_arg_count(function_name, &arguments, 1)?; Ok(v_locker::delete_vlocker(call_context, arguments[0].clone())) } "owned:locker:exists" => { expect_arg_count(function_name, &arguments, 1)?; Ok(v_locker::vlocker_exists(call_context, arguments[0].clone())) } "owned:locker:hot:init" => { expect_arg_count(function_name, &arguments, 1)?; Ok(v_locker::init_hot_vlocker( call_context, arguments[0].clone(), )) } "owned:locker:hot:fetch" => { expect_arg_count(function_name, &arguments, 1)?; Ok(v_locker::fetch_hot_vlocker( call_context, arguments[0].clone(), )) } "owned:locker:hot:get" => { expect_arg_count(function_name, &arguments, 2)?; Ok(v_locker::get_hot_vlocker( call_context, arguments[0].clone(), arguments[1].clone(), )) } "owned:locker:hot:override" => { expect_arg_count(function_name, &arguments, 2)?; Ok(v_locker::override_hot_vlocker( call_context, arguments[0].clone(), arguments[1].clone(), )) } "owned:locker:hot:save" => { expect_arg_count(function_name, &arguments, 1)?; Ok(v_locker::save_hot_vlocker( call_context, arguments[0].clone(), )) } "owned:locker:hot:remove" => { expect_arg_count(function_name, &arguments, 1)?; Ok(v_locker::remove_hot_vlocker( call_context, arguments[0].clone(), )) } "cad:activity:append" => { expect_arg_count(function_name, &arguments, 1)?; Ok(cad::append_activity(arguments[0].clone())) } "cad:activity:recent" => { expect_arg_count(function_name, &arguments, 1)?; Ok(cad::recent_activity(arguments[0].clone())) } "cad:assignments:list" => { expect_arg_count(function_name, &arguments, 0)?; Ok(cad::list_assignments()) } "cad:assignments:assign" => { expect_arg_count(function_name, &arguments, 2)?; Ok(cad::assign_assignment( arguments[0].clone(), arguments[1].clone(), )) } "cad:assignments:acknowledge" => { expect_arg_count(function_name, &arguments, 2)?; Ok(cad::acknowledge_assignment( arguments[0].clone(), arguments[1].clone(), )) } "cad:assignments:decline" => { expect_arg_count(function_name, &arguments, 2)?; Ok(cad::decline_assignment( arguments[0].clone(), arguments[1].clone(), )) } "cad:assignments:upsert" => { expect_arg_count(function_name, &arguments, 2)?; Ok(cad::upsert_assignment( arguments[0].clone(), arguments[1].clone(), )) } "cad:assignments:delete" => { expect_arg_count(function_name, &arguments, 1)?; Ok(cad::delete_assignment(arguments[0].clone())) } "cad:orders:list" => { expect_arg_count(function_name, &arguments, 0)?; Ok(cad::list_orders()) } "cad:orders:create" => { expect_arg_count(function_name, &arguments, 1)?; Ok(cad::create_order(arguments[0].clone())) } "cad:orders:create_from_context" => { expect_arg_count(function_name, &arguments, 1)?; Ok(cad::create_order_from_context(arguments[0].clone())) } "cad:orders:close" => { expect_arg_count(function_name, &arguments, 1)?; Ok(cad::close_order(arguments[0].clone())) } "cad:orders:upsert" => { expect_arg_count(function_name, &arguments, 2)?; Ok(cad::upsert_order( arguments[0].clone(), arguments[1].clone(), )) } "cad:orders:delete" => { expect_arg_count(function_name, &arguments, 1)?; Ok(cad::delete_order(arguments[0].clone())) } "cad:requests:list" => { expect_arg_count(function_name, &arguments, 0)?; Ok(cad::list_requests()) } "cad:requests:submit" => { expect_arg_count(function_name, &arguments, 1)?; Ok(cad::submit_request(arguments[0].clone())) } "cad:requests:submit_from_context" => { expect_arg_count(function_name, &arguments, 1)?; Ok(cad::submit_request_from_context(arguments[0].clone())) } "cad:requests:close" => { expect_arg_count(function_name, &arguments, 1)?; Ok(cad::close_request(arguments[0].clone())) } "cad:requests:upsert" => { expect_arg_count(function_name, &arguments, 2)?; Ok(cad::upsert_request( arguments[0].clone(), arguments[1].clone(), )) } "cad:requests:delete" => { expect_arg_count(function_name, &arguments, 1)?; Ok(cad::delete_request(arguments[0].clone())) } "cad:profiles:list" => { expect_arg_count(function_name, &arguments, 0)?; Ok(cad::list_profiles()) } "cad:profiles:update_from_context" => { expect_arg_count(function_name, &arguments, 1)?; Ok(cad::update_profile_from_context(arguments[0].clone())) } "cad:profiles:upsert" => { expect_arg_count(function_name, &arguments, 2)?; Ok(cad::upsert_profile( arguments[0].clone(), arguments[1].clone(), )) } "cad:profiles:delete" => { expect_arg_count(function_name, &arguments, 1)?; Ok(cad::delete_profile(arguments[0].clone())) } "cad:groups:build" => { expect_arg_count(function_name, &arguments, 1)?; Ok(cad::build_groups(arguments[0].clone())) } "cad:view:hydrate" => { expect_arg_count(function_name, &arguments, 1)?; Ok(cad::hydrate_view(arguments[0].clone())) } _ => Err(format!( "{UNSUPPORTED_ROUTE_PREFIX} for function '{function_name}'" )), } } fn expect_arg_count( function_name: &str, arguments: &[String], expected_count: usize, ) -> Result<(), String> { if arguments.len() == expected_count { return Ok(()); } Err(format!( "Transport route '{}' expected {} arguments but received {}", function_name, expected_count, arguments.len() )) } fn chunk_response_if_needed(result: String) -> String { if result.len() <= RESPONSE_CHUNK_SIZE { return result; } let transfer_id = next_transfer_id("rsp"); let chunks = split_string_chunks(&result, RESPONSE_CHUNK_SIZE); let envelope = ChunkEnvelope { transfer_id: transfer_id.clone(), chunk_count: chunks.len(), total_size: result.len(), }; RESPONSE_STORE.lock().unwrap().insert(transfer_id, chunks); format!( "{CHUNK_PREFIX}{}", serde_json::to_string(&envelope) .unwrap_or_else(|error| format!("{{\"error\":\"{error}\"}}")) ) } fn next_transfer_id(prefix: &str) -> String { let sequence = TRANSFER_SEQUENCE.fetch_add(1, Ordering::Relaxed); format!("{prefix}_{sequence}") } fn split_string_chunks(input: &str, max_bytes: usize) -> Vec { if input.is_empty() { return vec![String::new()]; } let mut chunks = Vec::new(); let mut chunk_start = 0usize; let mut chunk_len = 0usize; for (index, character) in input.char_indices() { let char_len = character.len_utf8(); if chunk_len > 0 && chunk_len + char_len > max_bytes { chunks.push(input[chunk_start..index].to_string()); chunk_start = index; chunk_len = 0; } chunk_len += char_len; } chunks.push(input[chunk_start..].to_string()); chunks }