feat(phase-9.75g-0): Implement BID-FFI Day 5 - FileBox plugin library and transparent switching (90% complete)
Day 5 achievements: - Created independent FileBox plugin crate with C FFI exports - Integrated plugin loading into Nyash interpreter startup - Implemented transparent builtin/plugin Box switching via nyash.toml - Successfully loaded plugin library (385KB .so) at runtime - Confirmed PluginBox proxy creation for FileBox instances Architecture changes: - Added plugins/ directory with .gitignore for build artifacts - Modified runner.rs to load plugins from nyash.toml on startup - Updated objects.rs to use BoxFactoryRegistry for FileBox creation - Fixed bid module visibility between lib.rs and main.rs Remaining work (10%): - Complete PluginBox proxy method implementations (toString, etc.) - Test actual file operations through plugin interface - Finalize error handling and edge cases Build status: All tests passing, plugin loading confirmed
This commit is contained in:
173
plugins/nyash-filebox-plugin/src/filebox.rs
Normal file
173
plugins/nyash-filebox-plugin/src/filebox.rs
Normal file
@ -0,0 +1,173 @@
|
||||
//! FileBox実装
|
||||
//!
|
||||
//! ファイル操作を提供するBox実装
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write, Seek, SeekFrom};
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// FileBoxインスタンス
|
||||
pub struct FileBoxInstance {
|
||||
path: Option<PathBuf>,
|
||||
file: Option<File>,
|
||||
content: String,
|
||||
}
|
||||
|
||||
impl FileBoxInstance {
|
||||
/// 新しいFileBoxインスタンス作成
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
path: None,
|
||||
file: None,
|
||||
content: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// ファイルオープン
|
||||
pub fn open(&mut self, path: &str) -> Result<(), std::io::Error> {
|
||||
let path_buf = PathBuf::from(path);
|
||||
|
||||
// ファイルを読み書きモードでオープン
|
||||
let mut file = std::fs::OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(&path_buf)?;
|
||||
|
||||
// 既存内容を読み込み
|
||||
let mut content = String::new();
|
||||
if let Err(_) = file.read_to_string(&mut content) {
|
||||
// 読み込めない場合は空文字列
|
||||
content.clear();
|
||||
}
|
||||
|
||||
// ファイルポインタを先頭に戻す
|
||||
file.seek(SeekFrom::Start(0))?;
|
||||
|
||||
self.path = Some(path_buf);
|
||||
self.file = Some(file);
|
||||
self.content = content;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// ファイル読み取り
|
||||
pub fn read(&mut self) -> Result<String, std::io::Error> {
|
||||
if let Some(ref mut file) = self.file {
|
||||
let mut content = String::new();
|
||||
file.seek(SeekFrom::Start(0))?;
|
||||
file.read_to_string(&mut content)?;
|
||||
self.content = content.clone();
|
||||
Ok(content)
|
||||
} else {
|
||||
Ok(self.content.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// ファイル書き込み
|
||||
pub fn write(&mut self, data: &str) -> Result<(), std::io::Error> {
|
||||
self.content = data.to_string();
|
||||
|
||||
if let Some(ref mut file) = self.file {
|
||||
file.seek(SeekFrom::Start(0))?;
|
||||
file.set_len(0)?; // ファイル内容をクリア
|
||||
file.write_all(data.as_bytes())?;
|
||||
file.flush()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// ファイルパス取得
|
||||
pub fn path(&self) -> Option<&str> {
|
||||
self.path.as_ref().and_then(|p| p.to_str())
|
||||
}
|
||||
|
||||
/// 内容取得
|
||||
pub fn content(&self) -> &str {
|
||||
&self.content
|
||||
}
|
||||
}
|
||||
|
||||
/// FileBoxレジストリ
|
||||
pub struct FileBoxRegistry {
|
||||
instances: HashMap<u32, FileBoxInstance>,
|
||||
}
|
||||
|
||||
impl FileBoxRegistry {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
instances: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register(&mut self, handle: u32, instance: FileBoxInstance) {
|
||||
self.instances.insert(handle, instance);
|
||||
}
|
||||
|
||||
pub fn get(&self, handle: u32) -> Option<&FileBoxInstance> {
|
||||
self.instances.get(&handle)
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, handle: u32) -> Option<&mut FileBoxInstance> {
|
||||
self.instances.get_mut(&handle)
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, handle: u32) -> Option<FileBoxInstance> {
|
||||
self.instances.remove(&handle)
|
||||
}
|
||||
|
||||
pub fn count(&self) -> usize {
|
||||
self.instances.len()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::fs;
|
||||
|
||||
#[test]
|
||||
fn test_filebox_basic_operations() {
|
||||
let mut filebox = FileBoxInstance::new();
|
||||
|
||||
// テスト用ファイル
|
||||
let test_file = "test_plugin_file.txt";
|
||||
|
||||
// ファイルオープン
|
||||
assert!(filebox.open(test_file).is_ok());
|
||||
assert_eq!(filebox.path(), Some(test_file));
|
||||
|
||||
// 書き込み
|
||||
assert!(filebox.write("Hello from plugin!").is_ok());
|
||||
assert_eq!(filebox.content(), "Hello from plugin!");
|
||||
|
||||
// 読み込み
|
||||
let content = filebox.read().unwrap();
|
||||
assert_eq!(content, "Hello from plugin!");
|
||||
|
||||
// クリーンアップ
|
||||
let _ = fs::remove_file(test_file);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_filebox_registry() {
|
||||
let mut registry = FileBoxRegistry::new();
|
||||
|
||||
let filebox1 = FileBoxInstance::new();
|
||||
let filebox2 = FileBoxInstance::new();
|
||||
|
||||
registry.register(1, filebox1);
|
||||
registry.register(2, filebox2);
|
||||
|
||||
assert_eq!(registry.count(), 2);
|
||||
assert!(registry.get(1).is_some());
|
||||
assert!(registry.get(2).is_some());
|
||||
assert!(registry.get(3).is_none());
|
||||
|
||||
registry.remove(1);
|
||||
assert_eq!(registry.count(), 1);
|
||||
assert!(registry.get(1).is_none());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user