Files
hakorune/plugins/nyash-file/src/lib.rs
Moe Charm 226618de05 feat(9.75f-1): Implement FileBox dynamic plugin foundation
- Add workspace configuration to support plugins
- Create nyash-file plugin with C ABI
- Implement nyash_file_open/read/write/exists/free functions
- Add libloading dependency with dynamic-file feature
- Successfully build libnyash_file.so (1.55s build time)

Next: Implement plugin loader in interpreter
2025-08-17 04:13:42 +09:00

196 lines
5.1 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! FileBox Dynamic Plugin
//!
//! C ABIを使用した動的ライブラリとしてFileBox機能を提供
use std::ffi::{c_char, c_void, CStr, CString};
use std::fs::{File, OpenOptions};
use std::io::{Read, Write, Seek};
use std::os::raw::c_int;
/// プラグインのマジックナンバー('NYAS'
const PLUGIN_MAGIC: u32 = 0x4E594153;
/// プラグイン情報構造体
#[repr(C)]
pub struct FileBoxPlugin {
magic: u32,
version: u32,
api_version: u32,
}
/// FileBoxのハンドル不透明ポインタ
pub struct FileBoxHandle {
file: File,
path: String,
}
/// プラグイン初期化
#[no_mangle]
pub extern "C" fn nyash_plugin_init() -> *const FileBoxPlugin {
let plugin = Box::new(FileBoxPlugin {
magic: PLUGIN_MAGIC,
version: 1,
api_version: 1,
});
Box::into_raw(plugin)
}
/// FileBoxを開く
///
/// # Safety
/// - pathは有効なUTF-8のC文字列である必要がある
/// - 返されたポインタはnyash_file_freeで解放する必要がある
#[no_mangle]
pub unsafe extern "C" fn nyash_file_open(path: *const c_char) -> *mut c_void {
if path.is_null() {
return std::ptr::null_mut();
}
let path_str = match CStr::from_ptr(path).to_str() {
Ok(s) => s,
Err(_) => return std::ptr::null_mut(),
};
match OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(path_str)
{
Ok(file) => {
let handle = Box::new(FileBoxHandle {
file,
path: path_str.to_string(),
});
Box::into_raw(handle) as *mut c_void
}
Err(_) => std::ptr::null_mut(),
}
}
/// ファイルの内容を読み取る
///
/// # Safety
/// - handleはnyash_file_openから返された有効なポインタである必要がある
/// - 返された文字列はnyash_string_freeで解放する必要がある
#[no_mangle]
pub unsafe extern "C" fn nyash_file_read(handle: *mut c_void) -> *mut c_char {
if handle.is_null() {
return std::ptr::null_mut();
}
let file_box = &mut *(handle as *mut FileBoxHandle);
let mut content = String::new();
// ファイルポインタを最初に戻す
if let Err(_) = file_box.file.seek(std::io::SeekFrom::Start(0)) {
return std::ptr::null_mut();
}
match file_box.file.read_to_string(&mut content) {
Ok(_) => {
match CString::new(content) {
Ok(c_str) => c_str.into_raw(),
Err(_) => std::ptr::null_mut(),
}
}
Err(_) => std::ptr::null_mut(),
}
}
/// ファイルに内容を書き込む
///
/// # Safety
/// - handleはnyash_file_openから返された有効なポインタである必要がある
/// - contentは有効なUTF-8のC文字列である必要がある
#[no_mangle]
pub unsafe extern "C" fn nyash_file_write(
handle: *mut c_void,
content: *const c_char
) -> c_int {
if handle.is_null() || content.is_null() {
return 0;
}
let file_box = &mut *(handle as *mut FileBoxHandle);
let content_str = match CStr::from_ptr(content).to_str() {
Ok(s) => s,
Err(_) => return 0,
};
// ファイルをクリアして最初から書き込む
if let Err(_) = file_box.file.set_len(0) {
return 0;
}
if let Err(_) = file_box.file.seek(std::io::SeekFrom::Start(0)) {
return 0;
}
match file_box.file.write_all(content_str.as_bytes()) {
Ok(_) => 1, // 成功
Err(_) => 0, // 失敗
}
}
/// ファイルが存在するかチェック
///
/// # Safety
/// - pathは有効なUTF-8のC文字列である必要がある
#[no_mangle]
pub unsafe extern "C" fn nyash_file_exists(path: *const c_char) -> c_int {
if path.is_null() {
return 0;
}
let path_str = match CStr::from_ptr(path).to_str() {
Ok(s) => s,
Err(_) => return 0,
};
if std::path::Path::new(path_str).exists() {
1
} else {
0
}
}
/// FileBoxハンドルを解放
///
/// # Safety
/// - handleはnyash_file_openから返された有効なポインタである必要がある
/// - 解放後はhandleを使用してはいけない
#[no_mangle]
pub unsafe extern "C" fn nyash_file_free(handle: *mut c_void) {
if !handle.is_null() {
drop(Box::from_raw(handle as *mut FileBoxHandle));
}
}
/// 文字列を解放nyash_file_readの戻り値用
///
/// # Safety
/// - strはnyash_file_readから返された有効なポインタである必要がある
#[no_mangle]
pub unsafe extern "C" fn nyash_string_free(str: *mut c_char) {
if !str.is_null() {
drop(CString::from_raw(str));
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::ffi::CString;
#[test]
fn test_plugin_init() {
unsafe {
let plugin = nyash_plugin_init();
assert!(!plugin.is_null());
let plugin_info = &*plugin;
assert_eq!(plugin_info.magic, PLUGIN_MAGIC);
assert_eq!(plugin_info.version, 1);
assert_eq!(plugin_info.api_version, 1);
}
}
}