ワンクリックで
add-ffi
Add a new FFI function bridging the Rust audio engine and Flutter/Dart UI. Handles all 7-8 files that need updating.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Add a new FFI function bridging the Rust audio engine and Flutter/Dart UI. Handles all 7-8 files that need updating.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
| name | add-ffi |
| description | Add a new FFI function bridging the Rust audio engine and Flutter/Dart UI. Handles all 7-8 files that need updating. |
The user will describe what engine function they need. Follow these steps in order to add it across all layers. Read each target file before editing.
engine/src/api/<domain>.rs)Add the business logic function. Return Result<T, String> for fallible operations.
pub fn my_function(param: i32) -> Result<String, String> {
with_graph(|graph| {
// business logic here
Ok("Done".to_string())
})
}
with_graph() for read-only access, with_graph_mut() for mutationget_audio_graph()? + manual lock for try_lock patternsengine/src/api/mod.rs and re-exportengine/src/ffi/<domain>.rs)Wrap the API function for C export.
String return, no string params:
#[no_mangle]
pub extern "C" fn my_function_ffi(param: i32) -> *mut c_char {
ffi_catch(std::ptr::null_mut(), || {
match api::my_function(param) {
Ok(msg) => safe_cstring(msg).into_raw(),
Err(e) => safe_cstring(format!("Error: {e}")).into_raw(),
}
})
}
With string params (use AssertUnwindSafe):
#[no_mangle]
pub extern "C" fn my_function_ffi(name: *const c_char) -> *mut c_char {
ffi_catch(std::ptr::null_mut(), AssertUnwindSafe(|| {
let name_str = unsafe {
match CStr::from_ptr(name).to_str() {
Ok(s) => s.to_string(),
Err(_) => return safe_cstring("Error: Invalid UTF-8".to_string()).into_raw(),
}
};
match api::my_function(&name_str) {
Ok(msg) => safe_cstring(msg).into_raw(),
Err(e) => safe_cstring(format!("Error: {e}")).into_raw(),
}
}))
}
Numeric return:
#[no_mangle]
pub extern "C" fn my_function_ffi() -> f64 {
ffi_catch(0.0, || {
api::my_function().unwrap_or(0.0)
})
}
engine/src/ffi/mod.rs: mod my_module;_ffi suffixui/lib/audio_engine_typedefs.dart)Add both native and Dart typedefs near related functions.
// Native (C types)
typedef _MyFunctionFfiNative = ffi.Pointer<Utf8> Function(ffi.Int32 param);
// Dart (Dart types)
typedef _MyFunctionFfi = ffi.Pointer<Utf8> Function(int param);
Type mapping:
| Rust | Dart Native (C) | Dart |
|---|---|---|
*mut c_char (return) | ffi.Pointer<Utf8> | ffi.Pointer<Utf8> |
*const c_char (param) | ffi.Pointer<ffi.Char> | ffi.Pointer<ffi.Char> |
f32 | ffi.Float | double |
f64 | ffi.Double | double |
i32 | ffi.Int32 | int |
i64 | ffi.Int64 | int |
u32 | ffi.Uint32 | int |
u64 | ffi.Uint64 | int |
bool | ffi.Bool | bool |
ui/lib/audio_engine_base.dart)Add the late final field with other fields of the same domain:
late final _MyFunctionFfi _myFunction;
Add the lookup in the constructor, near related lookups:
_myFunction = _lib
.lookup<ffi.NativeFunction<_MyFunctionFfiNative>>('my_function_ffi')
.asFunction();
CRITICAL: The string 'my_function_ffi' must match the Rust #[no_mangle] name exactly.
ui/lib/audio_engine_<domain>.dart)Add a public method in the appropriate mixin (transport, tracks, recording, or plugins).
String return:
String myFunction(int param) {
try {
final resultPtr = _myFunction(param);
final result = resultPtr.toDartString();
_freeRustString(resultPtr);
return result;
} catch (e) {
rethrow;
}
}
String param:
String myFunction(String name) {
try {
final namePtr = name.toNativeUtf8();
final resultPtr = _myFunction(namePtr.cast());
malloc.free(namePtr);
final result = resultPtr.toDartString();
_freeRustString(resultPtr);
return result;
} catch (e) {
rethrow;
}
}
print() not debugPrint() in this file_freeRustString()malloc.free()ui/lib/services/commands/audio_engine_interface.dart)Add the abstract method signature:
String myFunction(int param);
ui/lib/audio_engine_stub.dart)Add the stub implementation:
@override
String myFunction(int param) => throw UnsupportedError('stub');
ui/lib/audio_engine_web.dart)Add the web implementation. If web support isn't needed yet, add a TODO stub:
@override
String myFunction(int param) {
// TODO: implement web support
return 'Not supported on web';
}
If web IS supported, use the JS interop helpers:
@override
String myFunction(int param) {
final result = _callEngineWith('my_function', [param.toJS]);
return _jsToString(result) ?? 'Error';
}
After completing all steps, verify:
cargo build in engine/flutter analyze in ui/#[no_mangle] name in Step 2_freeRustString(), Dart strings freed via malloc.free()