feat(phase-9.75g-0): Implement BID-FFI Day 4 - FileBox plugin (50% complete)

-  FileBox plugin design with open/read/write/close API
-  FileBoxRegistry for handle management
-  FileMode enum for file access modes
-  FileBoxPlugin implementation with BidHandle integration
-  Complete test suite for FileBox operations (1/1 passing)
- 🎯 Everything is Box philosophy applied to file operations!

Remaining tasks:
-  FileBox host integration (Nyash側からの呼び出し)
-  Plugin loading mechanism (動的ロード実装)

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Moe Charm
2025-08-17 21:09:32 +09:00
parent ea6cc1fe9e
commit 939c621b47
4 changed files with 224 additions and 6 deletions

View File

@ -33,25 +33,35 @@
- ✅ プラグインライフサイクル管理
-**テスト7/7合格** 🎉
### 🎯 **Day 3 進行中** (2025-08-17)
### **Day 3 完了** (2025-08-17)
**目標**: 既存Box統合StringBox/IntegerBox/FutureBoxブリッジ
**実装完了** (83%達成!):
**実装完了** (100%達成!):
- ✅ BID Box Bridge設計: 既存Box型とBIDハンドルの相互変換インターフェース
- ✅ StringBox BIDブリッジ: Handle/TLV変換実装
- ✅ IntegerBox BIDブリッジ: Handle/TLV変換実装
- ✅ FutureBox BIDブリッジ: 非同期Box型の統合完了
- ✅ BoxRegistry: Box型とハンドルの管理システム
- ✅ 統合テスト: StringBox/IntegerBoxラウンドトリップテスト(3/3合格!)
- ✅ 統合テスト: 全Box型ラウンドトリップテスト(4/4合格!)
-**Everything is Box理論の威力実証** 🎉
### 🎯 **Day 4 進行中!** (2025-08-17)
**目標**: FileBoxプラグイン実装open/read/write/close
**実装完了** (50%達成!):
- ✅ FileBoxプラグイン設計: open/read/write/close API設計
- ✅ FileBoxプラグイン実装: ハンドル管理・ファイル操作実装
- ✅ FileBoxテスト作成: ファイル操作テスト1/1合格
**残タスク**:
- ⏳ FutureBox BIDブリッジ実装非同期Box型の統合
- ⏳ FileBoxホスト統合: Nyash側からの呼び出し
- ⏳ プラグインロード機構: 動的ロード実装
### 🎯 今週の実装計画ChatGPT最終案準拠
- **Day 1**: ✅ BID-1基盤実装TLV仕様、Handle構造体、エンコード/デコード)
- **Day 2**: ✅ メタデータAPI実装init/abi/shutdown、HostVtable、レジストリ
- **Day 3**: 既存Box統合StringBox/IntegerBox/FutureBoxブリッジ**83%完了!**
- **Day 4**: FileBoxプラグイン実装open/read/write/close
- **Day 3**: 既存Box統合StringBox/IntegerBox/FutureBoxブリッジ**100%完了!**
- **Day 4**: FileBoxプラグイン実装open/read/write/close**50%進行中!**
- **Day 5**: 統合テスト・最適化(メモリリーク検証、性能測定)
- **Day 6-7**: ドキュメント・CI・仕上げ

View File

@ -7,6 +7,7 @@ pub mod error;
pub mod metadata;
pub mod plugin_api;
pub mod bridge;
pub mod plugins;
pub use types::*;
pub use tlv::*;

View File

@ -0,0 +1,199 @@
//! FileBox Plugin - BID-FFI File Operations
//!
//! Provides file I/O operations through the BID-FFI plugin interface.
//! Everything is Box philosophy applied to file operations!
use crate::bid::{BidHandle, BoxTypeId};
use crate::bid::{NyashPluginInfo, NyashMethodInfo, NyashHostVtable};
use std::collections::HashMap;
use std::fs::{File, OpenOptions};
use std::io::{Read, Write, Seek, SeekFrom};
use std::os::raw::{c_char, c_void};
use std::sync::{Arc, Mutex};
use std::ffi::{CStr, CString};
/// FileBox handle management
pub struct FileBoxRegistry {
files: HashMap<u32, Arc<Mutex<FileBoxState>>>,
next_handle: u32,
}
/// State of an open file
struct FileBoxState {
file: File,
path: String,
mode: FileMode,
}
#[derive(Debug, Clone, Copy)]
enum FileMode {
Read,
Write,
Append,
ReadWrite,
}
impl FileBoxRegistry {
pub fn new() -> Self {
Self {
files: HashMap::new(),
next_handle: 1,
}
}
pub fn open(&mut self, path: &str, mode: FileMode) -> Result<BidHandle, std::io::Error> {
let file = match mode {
FileMode::Read => OpenOptions::new().read(true).open(path)?,
FileMode::Write => OpenOptions::new().write(true).create(true).truncate(true).open(path)?,
FileMode::Append => OpenOptions::new().append(true).create(true).open(path)?,
FileMode::ReadWrite => OpenOptions::new().read(true).write(true).create(true).open(path)?,
};
let handle_id = self.next_handle;
self.next_handle += 1;
let state = FileBoxState {
file,
path: path.to_string(),
mode,
};
self.files.insert(handle_id, Arc::new(Mutex::new(state)));
Ok(BidHandle::new(BoxTypeId::FileBox as u32, handle_id))
}
pub fn read(&self, handle: BidHandle, size: usize) -> Result<Vec<u8>, std::io::Error> {
let handle_id = handle.instance_id;
let file_state = self.files.get(&handle_id)
.ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, "Invalid file handle"))?;
let mut state = file_state.lock().unwrap();
let mut buffer = vec![0u8; size];
let bytes_read = state.file.read(&mut buffer)?;
buffer.truncate(bytes_read);
Ok(buffer)
}
pub fn write(&self, handle: BidHandle, data: &[u8]) -> Result<usize, std::io::Error> {
let handle_id = handle.instance_id;
let file_state = self.files.get(&handle_id)
.ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, "Invalid file handle"))?;
let mut state = file_state.lock().unwrap();
state.file.write(data)
}
pub fn close(&mut self, handle: BidHandle) -> Result<(), std::io::Error> {
let handle_id = handle.instance_id;
self.files.remove(&handle_id)
.ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, "Invalid file handle"))?;
Ok(())
}
}
/// Global registry instance
static mut FILEBOX_REGISTRY: Option<Arc<Mutex<FileBoxRegistry>>> = None;
/// Get or create the global registry
fn get_registry() -> Arc<Mutex<FileBoxRegistry>> {
unsafe {
if FILEBOX_REGISTRY.is_none() {
FILEBOX_REGISTRY = Some(Arc::new(Mutex::new(FileBoxRegistry::new())));
}
FILEBOX_REGISTRY.as_ref().unwrap().clone()
}
}
/// FileBox plugin interface for Nyash
pub struct FileBoxPlugin {
registry: Arc<Mutex<FileBoxRegistry>>,
}
impl FileBoxPlugin {
pub fn new() -> Self {
Self {
registry: get_registry(),
}
}
/// Open a file and return its handle
pub fn open(&self, path: &str, mode: &str) -> Result<BidHandle, String> {
let file_mode = match mode {
"r" => FileMode::Read,
"w" => FileMode::Write,
"a" => FileMode::Append,
"rw" | "r+" => FileMode::ReadWrite,
_ => return Err(format!("Invalid file mode: {}", mode)),
};
let mut registry = self.registry.lock().unwrap();
registry.open(path, file_mode)
.map_err(|e| format!("Failed to open file: {}", e))
}
/// Read data from file
pub fn read(&self, handle: BidHandle, size: usize) -> Result<Vec<u8>, String> {
let registry = self.registry.lock().unwrap();
registry.read(handle, size)
.map_err(|e| format!("Failed to read file: {}", e))
}
/// Write data to file
pub fn write(&self, handle: BidHandle, data: &[u8]) -> Result<usize, String> {
let registry = self.registry.lock().unwrap();
registry.write(handle, data)
.map_err(|e| format!("Failed to write file: {}", e))
}
/// Close file
pub fn close(&self, handle: BidHandle) -> Result<(), String> {
let mut registry = self.registry.lock().unwrap();
registry.close(handle)
.map_err(|e| format!("Failed to close file: {}", e))
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
#[test]
fn test_filebox_plugin() {
let plugin = FileBoxPlugin::new();
// Create a test file
let test_path = "test_filebox_plugin.txt";
let test_content = "Hello, FileBox Plugin!";
fs::write(test_path, test_content).unwrap();
// Test open
let handle = plugin.open(test_path, "r").unwrap();
assert_eq!(handle.type_id, BoxTypeId::FileBox as u32);
// Test read
let data = plugin.read(handle, 100).unwrap();
assert_eq!(String::from_utf8(data).unwrap(), test_content);
// Test close
plugin.close(handle).unwrap();
// Test write mode
let write_handle = plugin.open(test_path, "w").unwrap();
let new_content = b"New content!";
let written = plugin.write(write_handle, new_content).unwrap();
assert_eq!(written, new_content.len());
plugin.close(write_handle).unwrap();
// Verify new content
let read_handle = plugin.open(test_path, "r").unwrap();
let data = plugin.read(read_handle, 100).unwrap();
assert_eq!(&data[..], new_content);
plugin.close(read_handle).unwrap();
// Cleanup
fs::remove_file(test_path).unwrap();
}
}

8
src/bid/plugins/mod.rs Normal file
View File

@ -0,0 +1,8 @@
//! BID-FFI Plugins
//!
//! Collection of built-in and loadable plugins for Nyash.
pub mod filebox;
// Re-export plugin types
pub use filebox::FileBoxPlugin;