v0.1.0-pre.048

This commit is contained in:
2026-07-25 20:19:28 +02:00
parent 33a0762658
commit a595fd920d
10 changed files with 834 additions and 13 deletions

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/demo_backfill.rs
// version: 10
// version: 11
//! Tauri commands and UI payloads for bounded HTTP transaction backfills.
@@ -29,7 +29,7 @@ pub(crate) struct DemoBackfillRoleOption {
)]
pub(crate) struct DemoBackfillOptionsPayload {
/// Selectable endpoint roles.
pub(crate) roles: std::vec::Vec<DemoBackfillRoleOption>,
pub(crate) roles: std::vec::Vec<crate::DemoBackfillRoleOption>,
/// Preferred role when configured.
pub(crate) default_role: std::option::Option<std::string::String>,
/// Default transaction commitment.
@@ -213,7 +213,7 @@ impl std::ops::Drop for crate::DemoBackfillRunGuard<'_> {
pub(crate) fn build_role_options(
snapshots: std::vec::Vec<kb_onchain_transport::HttpPoolClientSnapshot>,
) -> std::vec::Vec<DemoBackfillRoleOption> {
) -> std::vec::Vec<crate::DemoBackfillRoleOption> {
let mut role_methods = std::collections::BTreeMap::<
std::string::String,
(
@@ -242,7 +242,7 @@ pub(crate) fn build_role_options(
{
continue;
}
output.push(DemoBackfillRoleOption {
output.push(crate::DemoBackfillRoleOption {
role,
providers: providers.into_iter().collect(),
});

View File

@@ -0,0 +1,400 @@
// file: kb-app-demo-desktop/src/demo_http.rs
// version: 9
//! HTTP JSON-RPC demo commands.
use ts_rs::TS; // rust-rules: derive-import
/// One selectable role shown by the HTTP demo.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_http/DemoHttpRoleOption.ts"
)]
pub(crate) struct DemoHttpRoleOption {
/// Endpoint role code.
pub(crate) role: std::string::String,
/// Request kinds accepted by this role.
pub(crate) request_kinds: std::vec::Vec<std::string::String>,
}
/// One selectable HTTP JSON-RPC method shown by the HTTP demo.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_http/DemoHttpMethodOption.ts"
)]
pub(crate) struct DemoHttpMethodOption {
/// JSON-RPC method name.
pub(crate) method: std::string::String,
/// Derived request kind used for endpoint routing.
pub(crate) request_kind: std::string::String,
/// Human-readable method label.
pub(crate) label: std::string::String,
/// Whether this method needs the first argument field.
pub(crate) requires_first_arg: bool,
/// Whether this method supports an optional configuration object.
pub(crate) supports_config_json: bool,
}
/// HTTP demo options derived from configuration and local method presets.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_http/DemoHttpOptionsPayload.ts"
)]
pub(crate) struct DemoHttpOptionsPayload {
/// Selectable roles.
pub(crate) roles: std::vec::Vec<crate::DemoHttpRoleOption>,
/// Selectable methods.
pub(crate) methods: std::vec::Vec<crate::DemoHttpMethodOption>,
}
/// Request payload for one HTTP JSON-RPC demo execution.
#[derive(Clone, Debug, serde::Deserialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_http/DemoHttpRequest.ts"
)]
pub(crate) struct DemoHttpRequest {
/// Required endpoint role used by the HTTP pool.
pub(crate) role: std::string::String,
/// JSON-RPC method name.
pub(crate) method: std::string::String,
/// Optional first argument string.
pub(crate) first_arg: std::option::Option<std::string::String>,
/// Optional JSON configuration string appended after the first argument.
pub(crate) config_json: std::option::Option<std::string::String>,
/// Optional raw JSON-RPC params array. When present, it overrides firstArg and configJson.
pub(crate) params_json: std::option::Option<std::string::String>,
}
/// Response payload for one HTTP JSON-RPC demo execution.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_http/DemoHttpExecutionPayload.ts"
)]
pub(crate) struct DemoHttpExecutionPayload {
/// Selected endpoint name.
pub(crate) endpoint_name: std::string::String,
/// Selected provider name.
pub(crate) provider: std::string::String,
/// Selected endpoint URL.
pub(crate) endpoint_url: std::string::String,
/// Required role used by the selection.
pub(crate) role: std::string::String,
/// JSON-RPC method name.
pub(crate) method: std::string::String,
/// Derived request kind.
pub(crate) request_kind: std::string::String,
/// Classified method family.
pub(crate) method_class: std::string::String,
/// Pretty JSON response text.
pub(crate) response_json: std::string::String,
}
pub(crate) async fn demo_http_execute_request_inner(
state: tauri::State<'_, crate::AppState>,
request: crate::DemoHttpRequest,
) -> std::result::Result<crate::DemoHttpExecutionPayload, std::string::String> {
let role = request.role.trim().to_string();
if role.is_empty() {
return std::result::Result::Err("demo HTTP role must not be empty".to_string());
}
let method = request.method.trim().to_string();
if method.is_empty() {
return std::result::Result::Err("demo HTTP method must not be empty".to_string());
}
let params_json_value = match parse_optional_params_json(request.params_json) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let config_json_value = match parse_optional_json(request.config_json) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let params = match build_demo_http_params(
&method,
request.first_arg.as_deref(),
config_json_value,
params_json_value,
) {
std::result::Result::Ok(params) => params,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let selected_client = match state.http_pool().select_client_for_role_and_method(&role, &method)
{
std::result::Result::Ok(client) => client,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let response_value =
match selected_client.execute_json_rpc_result_raw(method.clone(), params).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let response_json = match serde_json::to_string_pretty(&response_value) {
std::result::Result::Ok(text) => text,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let method_class = kb_onchain_transport::HttpClient::classify_method(&method);
return std::result::Result::Ok(crate::DemoHttpExecutionPayload {
endpoint_name: selected_client.endpoint_name().to_string(),
provider: selected_client.provider().to_string(),
endpoint_url: selected_client.endpoint_url().to_string(),
role,
request_kind: kb_onchain_transport::request_kind_from_method(&method),
method,
method_class: method_class_to_string(method_class).to_string(),
response_json,
});
}
pub(crate) fn build_http_role_options(
snapshots: std::vec::Vec<kb_onchain_transport::HttpPoolClientSnapshot>,
) -> std::vec::Vec<crate::DemoHttpRoleOption> {
let mut roles = std::collections::BTreeMap::<
std::string::String,
std::collections::BTreeSet<std::string::String>,
>::new();
for snapshot in snapshots {
for role in snapshot.roles {
if !role.enabled {
continue;
}
let role_entry = roles.entry(role.role).or_default();
for request_kind in role.request_kinds {
role_entry.insert(request_kind);
}
}
}
let mut options = std::vec::Vec::new();
for (role, request_kinds) in roles {
options.push(crate::DemoHttpRoleOption {
role,
request_kinds: request_kinds.into_iter().collect(),
});
}
return options;
}
pub(crate) fn build_http_method_options() -> std::vec::Vec<crate::DemoHttpMethodOption> {
let methods = [
("getAccountInfo", "Compte Solana", true, true),
("getBalance", "Balance SOL", true, true),
("getBlock", "Bloc par slot", true, true),
("getBlockCommitment", "Commitment dun bloc", true, false),
("getBlockHeight", "Hauteur de bloc", false, true),
("getBlockProduction", "Production de blocs", false, true),
("getBlocks", "Liste de blocs", true, true),
("getBlocksWithLimit", "Liste de blocs avec limite", true, true),
("getClusterNodes", "Nœuds du cluster", false, false),
("getEpochInfo", "Époque courante", false, true),
("getEpochSchedule", "Planning des époques", false, false),
("getFeeForMessage", "Frais pour message", true, true),
("getFirstAvailableBlock", "Premier bloc disponible", false, false),
("getGenesisHash", "Genesis hash", false, false),
("getHealth", "Santé du nœud", false, false),
("getHighestSnapshotSlot", "Dernier snapshot", false, false),
("getIdentity", "Identité du nœud", false, false),
("getInflationGovernor", "Gouverneur inflation", false, true),
("getInflationRate", "Taux dinflation", false, false),
("getInflationReward", "Récompense inflation", true, true),
("getLargestAccounts", "Plus gros comptes", false, true),
("getLatestBlockhash", "Dernier blockhash", false, true),
("getLeaderSchedule", "Planning leaders", false, true),
("getMaxRetransmitSlot", "Slot retransmis maximum", false, false),
("getMaxShredInsertSlot", "Shred insert slot maximum", false, false),
("getMinimumBalanceForRentExemption", "Rent exemption", true, true),
("getMultipleAccounts", "Comptes multiples", true, true),
("getProgramAccounts", "Comptes de programme", true, true),
("getRecentPerformanceSamples", "Samples performance", false, true),
("getRecentPrioritizationFees", "Frais de priorité récents", false, true),
("getSignaturesForAddress", "Signatures par adresse", true, true),
("getSignatureStatuses", "Statuts signatures", true, true),
("getSlot", "Slot courant", false, true),
("getSlotLeader", "Leader du slot", false, true),
("getSlotLeaders", "Leaders de slots", true, true),
("getStakeActivation", "Activation stake", true, true),
("getStakeMinimumDelegation", "Minimum delegation", false, true),
("getSupply", "Supply SOL", false, true),
("getTokenAccountBalance", "Balance token account", true, true),
("getTokenAccountsByDelegate", "Token accounts par delegate", true, true),
("getTokenAccountsByOwner", "Token accounts par owner", true, true),
("getTokenLargestAccounts", "Plus gros comptes token", true, true),
("getTokenSupply", "Supply token", true, true),
("getTransaction", "Transaction", true, true),
("getTransactionCount", "Nombre de transactions", false, true),
("getVersion", "Version du nœud", false, false),
("getVoteAccounts", "Vote accounts", false, true),
("isBlockhashValid", "Validité blockhash", true, true),
("requestAirdrop", "Airdrop devnet", true, true),
("sendTransaction", "Envoi de transaction", true, true),
("simulateTransaction", "Simulation transaction", true, true),
];
let mut options = std::vec::Vec::new();
for (method, label, requires_first_arg, supports_config_json) in methods {
options.push(crate::DemoHttpMethodOption {
method: method.to_string(),
request_kind: kb_onchain_transport::request_kind_from_method(method),
label: label.to_string(),
requires_first_arg,
supports_config_json,
});
}
return options;
}
fn parse_optional_json(
config_json: std::option::Option<std::string::String>,
) -> std::result::Result<std::option::Option<serde_json::Value>, std::string::String> {
let config_text = match config_json {
std::option::Option::Some(value) => value.trim().to_string(),
std::option::Option::None => return std::result::Result::Ok(std::option::Option::None),
};
if config_text.is_empty() {
return std::result::Result::Ok(std::option::Option::None);
}
let parse_result = serde_json::from_str::<serde_json::Value>(&config_text);
return match parse_result {
std::result::Result::Ok(value) => std::result::Result::Ok(std::option::Option::Some(value)),
std::result::Result::Err(error) => {
std::result::Result::Err(format!("invalid configJson: {error}"))
},
};
}
fn parse_optional_params_json(
params_json: std::option::Option<std::string::String>,
) -> std::result::Result<std::option::Option<std::vec::Vec<serde_json::Value>>, std::string::String>
{
let params_text = match params_json {
std::option::Option::Some(value) => value.trim().to_string(),
std::option::Option::None => return std::result::Result::Ok(std::option::Option::None),
};
if params_text.is_empty() {
return std::result::Result::Ok(std::option::Option::None);
}
let parse_result = serde_json::from_str::<serde_json::Value>(&params_text);
let value = match parse_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(format!("invalid paramsJson: {error}"));
},
};
let array = match value.as_array() {
std::option::Option::Some(array) => array.clone(),
std::option::Option::None => {
return std::result::Result::Err("paramsJson must be a JSON array".to_string());
},
};
return std::result::Result::Ok(std::option::Option::Some(array));
}
fn build_demo_http_params(
method: &str,
first_arg: std::option::Option<&str>,
config_json: std::option::Option<serde_json::Value>,
params_json: std::option::Option<std::vec::Vec<serde_json::Value>>,
) -> std::result::Result<std::vec::Vec<serde_json::Value>, std::string::String> {
if let std::option::Option::Some(params) = params_json {
return std::result::Result::Ok(params);
}
let needs_first_arg = method == "getBalance"
|| method == "getAccountInfo"
|| method == "getBlock"
|| method == "getBlockCommitment"
|| method == "getBlocks"
|| method == "getBlocksWithLimit"
|| method == "getFeeForMessage"
|| method == "getInflationReward"
|| method == "getMinimumBalanceForRentExemption"
|| method == "getMultipleAccounts"
|| method == "getProgramAccounts"
|| method == "getSignaturesForAddress"
|| method == "getSignatureStatuses"
|| method == "getSlotLeaders"
|| method == "getStakeActivation"
|| method == "getTokenAccountBalance"
|| method == "getTokenAccountsByDelegate"
|| method == "getTokenAccountsByOwner"
|| method == "getTokenLargestAccounts"
|| method == "getTokenSupply"
|| method == "getTransaction"
|| method == "isBlockhashValid"
|| method == "requestAirdrop"
|| method == "sendTransaction"
|| method == "simulateTransaction";
if needs_first_arg {
let first_arg_value = match first_arg {
std::option::Option::Some(value) => value.trim(),
std::option::Option::None => "",
};
if first_arg_value.is_empty() {
return std::result::Result::Err(format!("method '{method}' requires firstArg"));
}
let mut params = std::vec::Vec::new();
params.push(serde_json::Value::String(first_arg_value.to_string()));
if let std::option::Option::Some(config_value) = config_json {
params.push(config_value);
}
return std::result::Result::Ok(params);
}
let mut params = std::vec::Vec::new();
if let std::option::Option::Some(config_value) = config_json {
params.push(config_value);
}
return std::result::Result::Ok(params);
}
fn method_class_to_string(method_class: kb_onchain_transport::HttpMethodClass) -> &'static str {
return match method_class {
kb_onchain_transport::HttpMethodClass::GeneralRpc => "GeneralRpc",
kb_onchain_transport::HttpMethodClass::SendTransaction => "SendTransaction",
kb_onchain_transport::HttpMethodClass::HeavyRead => "HeavyRead",
};
}
#[cfg(test)]
mod tests {
#[test]
fn raw_params_override_first_argument_and_config() {
let params = super::build_demo_http_params(
"getBalance",
std::option::Option::Some("ignored"),
std::option::Option::Some(serde_json::json!({"commitment":"confirmed"})),
std::option::Option::Some(vec![serde_json::json!("exact")]),
);
assert_eq!(params, std::result::Result::Ok(vec![serde_json::json!("exact")]));
}
#[test]
fn required_first_argument_fails_closed() {
let params = super::build_demo_http_params(
"getBalance",
std::option::Option::None,
std::option::Option::None,
std::option::Option::None,
);
assert!(params.is_err());
}
#[test]
fn method_inventory_is_unique_and_classified() {
let options = crate::build_http_method_options();
let mut names = std::collections::BTreeSet::new();
for option in &options {
assert!(names.insert(option.method.as_str()));
assert_eq!(
option.request_kind,
kb_onchain_transport::request_kind_from_method(option.method.as_str())
);
}
assert!(!options.is_empty());
}
}

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/frontend_log.rs
// version: 1
// version: 2
//! Frontend logging bridge used by Tauri WebView scripts.
@@ -49,6 +49,12 @@ fn normalize_frontend_target(target: &str) -> std::string::String {
if trimmed == "kb-app-demo-desktop.frontend.splash" {
return trimmed.to_string();
}
if trimmed == "kb-app-demo-desktop.frontend.backfill" {
return trimmed.to_string();
}
if trimmed == "kb-app-demo-desktop.frontend.demo_http" {
return trimmed.to_string();
}
return "kb-app-demo-desktop.frontend".to_string();
}
@@ -69,4 +75,15 @@ mod tests {
"kb-app-demo-desktop.frontend.main"
);
}
#[test]
fn normalize_frontend_target_keeps_demo_targets() {
assert_eq!(
super::normalize_frontend_target("kb-app-demo-desktop.frontend.backfill"),
"kb-app-demo-desktop.frontend.backfill"
);
assert_eq!(
super::normalize_frontend_target("kb-app-demo-desktop.frontend.demo_http"),
"kb-app-demo-desktop.frontend.demo_http"
);
}
}

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/lib.rs
// version: 4
// version: 5
//! Tauri desktop demo application for `khadhroony-bot3`.
@@ -10,6 +10,7 @@
mod app_state;
mod constants;
mod demo_backfill;
mod demo_http;
mod frontend_log;
mod main_window;
mod splash;
@@ -40,6 +41,22 @@ pub(crate) use self::demo_backfill::build_demo_backfill_pipeline_request;
pub(crate) use self::demo_backfill::build_role_options;
/// Converts a pipeline summary to the UI-safe summary.
pub(crate) use self::demo_backfill::demo_backfill_summary_payload;
/// HTTP demo response payload.
pub(crate) use self::demo_http::DemoHttpExecutionPayload;
/// One selectable HTTP JSON-RPC method.
pub(crate) use self::demo_http::DemoHttpMethodOption;
/// HTTP demo options payload.
pub(crate) use self::demo_http::DemoHttpOptionsPayload;
/// HTTP demo request payload.
pub(crate) use self::demo_http::DemoHttpRequest;
/// One selectable HTTP endpoint role.
pub(crate) use self::demo_http::DemoHttpRoleOption;
/// Builds the HTTP method inventory.
pub(crate) use self::demo_http::build_http_method_options;
/// Builds the HTTP role inventory from pool snapshots.
pub(crate) use self::demo_http::build_http_role_options;
/// Executes one raw HTTP JSON-RPC request.
pub(crate) use self::demo_http::demo_http_execute_request_inner;
/// Frontend logging payload.
pub(crate) use self::frontend_log::FrontendLogPayload;
/// Emits one normalized frontend log event.

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/splash.rs
// version: 2
// version: 3
//! Splash-window payloads and startup sequencing helpers.
@@ -80,8 +80,8 @@ mod tests {
#[test]
fn splash_timing_is_ordered() {
assert!(crate::SPLASH_MINIMUM_MS > 0);
assert!(crate::SPLASH_FADE_MS > 0);
assert_eq!(crate::SPLASH_MINIMUM_MS, 3100);
assert_eq!(crate::SPLASH_FADE_MS, 3000);
assert!(crate::SPLASH_CLOSE_WAIT_MS >= u64::from(crate::SPLASH_FADE_MS));
}
}

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/tauri.rs
// version: 4
// version: 5
//! Tauri runtime assembly and private command wrappers.
@@ -28,6 +28,7 @@ pub fn run() -> kb_core::Result<()> {
config_path = app_state.config_path(),
active_profile = app_state.active_profile().name.as_str(),
logging_routes = app_state.logging_route_count(),
configured_profiles = app_state.app_config().profiles.len(),
"starting desktop demo application"
);
let tracing_builder = tauri_plugin_tracing::Builder::new();
@@ -40,6 +41,10 @@ pub fn run() -> kb_core::Result<()> {
demo_backfill_options,
demo_backfill_execute,
demo_backfill_cancel,
open_demo_http_window,
demo_http_list_pool_clients,
demo_http_options,
demo_http_execute_request,
]);
builder = builder.plugin(tracing_builder.build::<tauri::Wry>());
builder = builder.setup(|app| {
@@ -63,6 +68,27 @@ pub fn run() -> kb_core::Result<()> {
};
tauri::async_runtime::spawn(async move {
let started_at = tokio::time::Instant::now();
crate::emit_splash_order(
&splash_window,
"add_log",
std::option::Option::Some("Configuration chargée"),
std::option::Option::None,
std::option::Option::None,
);
crate::emit_splash_order(
&splash_window,
"add_log",
std::option::Option::Some("Sous-système de logs initialisé"),
std::option::Option::None,
std::option::Option::None,
);
crate::emit_splash_order(
&splash_window,
"add_log",
std::option::Option::Some("Pool HTTP initialisé"),
std::option::Option::None,
std::option::Option::None,
);
crate::emit_splash_order(
&splash_window,
"fadein",
@@ -73,11 +99,27 @@ pub fn run() -> kb_core::Result<()> {
crate::emit_splash_order(
&splash_window,
"add_msg",
std::option::Option::Some("Initialisation de Khadhroony Bot3..."),
std::option::Option::Some("Initialisation..."),
std::option::Option::Some("info"),
std::option::Option::None,
);
crate::emit_splash_order(
&splash_window,
"add_msg",
std::option::Option::Some("Loading complete..."),
std::option::Option::Some("success"),
std::option::Option::None,
);
crate::wait_until_minimum(started_at, crate::SPLASH_MINIMUM_MS).await;
if cfg!(debug_assertions) {
crate::emit_splash_order(
&splash_window,
"add_log",
std::option::Option::Some("Start Fade-out"),
std::option::Option::None,
std::option::Option::None,
);
}
crate::emit_splash_order(
&splash_window,
"fadeout",
@@ -202,6 +244,7 @@ fn demo_backfill_options(
};
}
#[allow(clippy::question_mark_used)]
#[tauri::command]
async fn demo_backfill_execute(
app_handle: tauri::AppHandle,
@@ -259,3 +302,61 @@ async fn demo_backfill_execute(
};
return std::result::Result::Ok(crate::demo_backfill_summary_payload(summary));
}
#[tauri::command]
fn open_demo_http_window(
app_handle: tauri::AppHandle,
) -> std::result::Result<(), std::string::String> {
let existing_window = app_handle.get_webview_window("demo_http");
if let std::option::Option::Some(window) = existing_window {
if let std::result::Result::Err(error) = window.show() {
return std::result::Result::Err(error.to_string());
}
if let std::result::Result::Err(error) = window.set_focus() {
return std::result::Result::Err(error.to_string());
}
return std::result::Result::Ok(());
}
let build_result = tauri::WebviewWindowBuilder::new(
&app_handle,
"demo_http",
tauri::WebviewUrl::App("demo_http.html".into()),
)
.title("Khadhroony Bot3 - HTTP JSON-RPC")
.inner_size(1280.0, 860.0)
.min_inner_size(960.0, 620.0)
.resizable(true)
.visible(true)
.build();
return match build_result {
std::result::Result::Ok(window) => match window.set_focus() {
std::result::Result::Ok(()) => std::result::Result::Ok(()),
std::result::Result::Err(error) => std::result::Result::Err(error.to_string()),
},
std::result::Result::Err(error) => std::result::Result::Err(error.to_string()),
};
}
#[tauri::command]
fn demo_http_list_pool_clients(
state: tauri::State<'_, crate::AppState>,
) -> std::vec::Vec<kb_onchain_transport::HttpPoolClientSnapshot> {
return state.http_pool().snapshot();
}
#[tauri::command]
fn demo_http_options(state: tauri::State<'_, crate::AppState>) -> crate::DemoHttpOptionsPayload {
return crate::DemoHttpOptionsPayload {
roles: crate::build_http_role_options(state.http_pool().snapshot()),
methods: crate::build_http_method_options(),
};
}
#[allow(clippy::question_mark_used)]
#[tauri::command]
async fn demo_http_execute_request(
state: tauri::State<'_, crate::AppState>,
request: crate::DemoHttpRequest,
) -> std::result::Result<crate::DemoHttpExecutionPayload, std::string::String> {
return crate::demo_http_execute_request_inner(state, request).await;
}