From 939c621b471b6de0120efd9724f715d756f23e63 Mon Sep 17 00:00:00 2001 From: Moe Charm Date: Sun, 17 Aug 2025 21:09:32 +0900 Subject: [PATCH] feat(phase-9.75g-0): Implement BID-FFI Day 4 - FileBox plugin (50% complete) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ✅ 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 --- docs/CURRENT_TASK.md | 22 +++- src/bid/mod.rs | 1 + src/bid/plugins/filebox/mod.rs | 199 +++++++++++++++++++++++++++++++++ src/bid/plugins/mod.rs | 8 ++ 4 files changed, 224 insertions(+), 6 deletions(-) create mode 100644 src/bid/plugins/filebox/mod.rs create mode 100644 src/bid/plugins/mod.rs diff --git a/docs/CURRENT_TASK.md b/docs/CURRENT_TASK.md index f25894ae..64bacd37 100644 --- a/docs/CURRENT_TASK.md +++ b/docs/CURRENT_TASK.md @@ -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・仕上げ diff --git a/src/bid/mod.rs b/src/bid/mod.rs index 87bdec6e..81e9509a 100644 --- a/src/bid/mod.rs +++ b/src/bid/mod.rs @@ -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::*; diff --git a/src/bid/plugins/filebox/mod.rs b/src/bid/plugins/filebox/mod.rs new file mode 100644 index 00000000..fe39b474 --- /dev/null +++ b/src/bid/plugins/filebox/mod.rs @@ -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>>, + 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 { + 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, 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 { + 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>> = None; + +/// Get or create the global registry +fn get_registry() -> Arc> { + 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>, +} + +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 { + 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, 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 { + 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(); + } +} \ No newline at end of file diff --git a/src/bid/plugins/mod.rs b/src/bid/plugins/mod.rs new file mode 100644 index 00000000..c718cdca --- /dev/null +++ b/src/bid/plugins/mod.rs @@ -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; \ No newline at end of file