- 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
196 lines
5.1 KiB
Rust
196 lines
5.1 KiB
Rust
//! 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);
|
||
}
|
||
}
|
||
} |