Skip to main content

command

Attribute Macro command 

Source
#[command]
Expand description

Re-export the #[command] attribute macro from conduit-derive.

This is conduit’s equivalent of #[tauri::command]. Use it for named-parameter handlers:

use tauri_conduit::{command, handler};

#[command]
fn greet(name: String, greeting: String) -> String {
    format!("{greeting}, {name}!")
}

Attribute macro that transforms a function into a conduit command handler.

Preserves the original function and generates a hidden handler struct (__conduit_handler_{fn_name}) implementing [conduit_core::ConduitHandler]. Use handler! to obtain the handler struct for registration.

This is conduit’s 1:1 equivalent of #[tauri::command]. The macro supports:

  • Named parameters — generates a hidden args struct with #[derive(Deserialize)] and #[serde(rename_all = "camelCase")]. Rust snake_case parameters are automatically converted to camelCase in JSON, matching #[tauri::command] behavior.
  • State<T> injection — parameters whose type path ends in State are extracted from the context (which must be an AppHandle<Wry>).
  • AppHandle injection — parameters whose type path ends in AppHandle.
  • Window/WebviewWindow injection — parameters whose type path ends in Window or WebviewWindow, resolved via app_handle.get_webview_window(label).
  • Webview injection — parameters whose type path ends in Webview, resolved via app_handle.get_webview(label).
  • Result<T, E> returns — errors are converted via Display into conduit_core::Error::Handler.
  • async functions — truly async, spawned on the tokio runtime (not block_on).

§Examples

use tauri_conduit::command;

// Named parameters — frontend sends { "name": "Alice", "greeting": "Hi" }
#[command]
fn greet(name: String, greeting: String) -> String {
    format!("{greeting}, {name}!")
}

// Result return — errors become conduit_core::Error::Handler
#[command]
fn divide(a: f64, b: f64) -> Result<f64, String> {
    if b == 0.0 { Err("division by zero".into()) }
    else { Ok(a / b) }
}

// State injection + async + Result
#[command]
async fn fetch_user(state: State<'_, Db>, id: u64) -> Result<User, String> {
    state.get_user(id).await.map_err(|e| e.to_string())
}

§Error handling

When a Result-returning handler returns Err(e), the error’s Display text is sent to the frontend as a JSON error response. This matches #[tauri::command] behavior. Be careful about what information your error types expose via Display.

§Limitations

  • tauri::Wry only: Generated handlers assume tauri::Wry as the runtime backend. This is the default (and typically only) runtime in Tauri v2.
  • Multiple State<T> params: Each State<T> must use a distinct concrete type T. Tauri’s state system is keyed by TypeId, so two params with the same T will receive the same instance.
  • Name-based injection detection: State, AppHandle, Window, WebviewWindow, and Webview are identified by the last path segment of the type. Any user type with these names will be misinterpreted as a Tauri injectable type. Rename your types to avoid false matches.
  • Name-based Result detection: The return type is detected as Result by checking the last path segment. Type aliases like type MyResult<T> = Result<T, E> are NOT detected as Result returns and will be serialized directly instead of unwrapping Ok/Err.
  • Window/Webview require label: Window and Webview injection requires the frontend to send the X-Conduit-Webview header (handled automatically by the TS client). If no label is available, the handler returns an error.
  • No impl block support: The macro generates struct definitions at the call site, which is illegal inside impl blocks. Only use #[command] on free-standing functions.