This Rust Crate contains code generation macros and plugins to build a communication channel between Raycast's React extensions and Rust native code. Basically, it lets you import Rust code into your Raycast extension in order to:
- leverage native Windows APIs that might not be exposed to TS/JS, or
- compartmentalize your extension into client-facing code (react) and system code (Rust).
- Rustup
- Install Rust Windows msvc target via rustup:
rustup target add x86_64-pc-windows-msvc
We built a sample extension using Rust here. Check it out to quickly get a feeling how things should be laid out.
To use Rust within Raycast:
-
Create (or fork) a Raycast extension.
If you don't know how, check out this guide.
-
Create a Rust crate in the folder of your Raycast extension.
mkdir -p rust/src touch rust/Cargo.toml touch rust/src/main.rs
The crate should have a bin section:
[package] name = "my-rust-api" version = "0.1.0" edition = "2021" [[bin]] name = "my-rust-api" path = "src/main.rs"
-
Modify the
Cargo.toml
file to include the necessary macros.+ [dependencies] + raycast-rust-macros = { git = "https://github.com/raycast/extensions-rust-tools", package = "raycast-rust-macros", branch = "main" } + raycast-rust-runtime = { git = "https://github.com/raycast/extensions-rust-tools", package = "raycast-rust-runtime", branch = "main" } + serde = { version = "1.0", features = ["derive"] } + serde_json = "1.0" + tokio = { version = "1.0", features = ["full"] }
-
Import
raycast_rust_macros
in your Rust file.use raycast_rust_macros::raycast;
-
Write global Rust functions and mark them with the
#[raycast]
macro.Global functions marked with
#[raycast]
are exported to TypeScript. These functions can have any number of parameters, and one or no return type. Exported functions can also be asynchronous (async
) or throw errors (when returning aResult
).#[raycast] fn greeting(name: String) -> String { format!("Hello {name}!") }
Custom types can be received as parameters or returned by the function. You just need to be sure the type conforms to
Deserialize
(for arguments) andSerialize
(for return types).use serde::{Deserialize, Serialize}; #[raycast] fn pick_color(name: String) -> Result<Color, String> { match name.as_str() { "red" => Ok(Color { red: 1.0, green: 0.0, blue: 0.0 }), "green" => Ok(Color { red: 0.0, green: 1.0, blue: 0.0 }), "blue" => Ok(Color { red: 0.0, green: 0.0, blue: 1.0 }), _ => Err(format!("{name} is not a supported color")), } } #[derive(Serialize)] struct Color { red: f32, green: f32, blue: f32, }
-
Import the rust functions in your extension
import { pick_color } from "rust:../rust";
export default function Command() {
console.log(pick_color("red"));
}