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:
@ -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・仕上げ
|
||||
|
||||
|
||||
@ -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::*;
|
||||
|
||||
199
src/bid/plugins/filebox/mod.rs
Normal file
199
src/bid/plugins/filebox/mod.rs
Normal 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
8
src/bid/plugins/mod.rs
Normal 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;
|
||||
Reference in New Issue
Block a user