feat(9.75f-1): Implement FileBox dynamic plugin foundation
- 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
This commit is contained in:
13
Cargo.toml
13
Cargo.toml
@ -16,6 +16,8 @@ cli = []
|
|||||||
gui = ["dep:egui", "dep:eframe", "dep:egui_extras", "dep:image"]
|
gui = ["dep:egui", "dep:eframe", "dep:egui_extras", "dep:image"]
|
||||||
gui-examples = ["gui"]
|
gui-examples = ["gui"]
|
||||||
all-examples = ["gui-examples"]
|
all-examples = ["gui-examples"]
|
||||||
|
# 動的ライブラリサポート
|
||||||
|
dynamic-file = ["dep:libloading"]
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "nyash_rust"
|
name = "nyash_rust"
|
||||||
@ -113,6 +115,9 @@ js-sys = "0.3"
|
|||||||
wabt = "0.10"
|
wabt = "0.10"
|
||||||
wasmtime = "35.0.0"
|
wasmtime = "35.0.0"
|
||||||
|
|
||||||
|
# 動的ライブラリロード(Phase 9.75f)
|
||||||
|
libloading = { version = "0.8", optional = true }
|
||||||
|
|
||||||
# GUI フレームワーク - only when gui feature is enabled
|
# GUI フレームワーク - only when gui feature is enabled
|
||||||
egui = { version = "0.29", optional = true }
|
egui = { version = "0.29", optional = true }
|
||||||
eframe = { version = "0.29", default-features = false, features = ["default_fonts", "glow"], optional = true }
|
eframe = { version = "0.29", default-features = false, features = ["default_fonts", "glow"], optional = true }
|
||||||
@ -169,3 +174,11 @@ panic = "abort"
|
|||||||
# 開発用設定
|
# 開発用設定
|
||||||
opt-level = 0
|
opt-level = 0
|
||||||
debug = true
|
debug = true
|
||||||
|
|
||||||
|
# Workspace configuration
|
||||||
|
[workspace]
|
||||||
|
members = [
|
||||||
|
".", # メインのnyash-rustプロジェクト
|
||||||
|
"plugins/nyash-file", # FileBoxプラグイン
|
||||||
|
]
|
||||||
|
resolver = "2"
|
||||||
|
|||||||
171
Cargo.toml.backup
Normal file
171
Cargo.toml.backup
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
[package]
|
||||||
|
name = "nyash-rust"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
authors = ["Claude Code <claude@anthropic.com>"]
|
||||||
|
description = "Everything is Box in Rust - Ultimate Memory Safe Nyash Implementation"
|
||||||
|
license = "MIT"
|
||||||
|
repository = "https://github.com/user/nyash"
|
||||||
|
keywords = ["language", "interpreter", "box", "memory-safe", "rust"]
|
||||||
|
categories = ["development-tools::parsing", "interpreters"]
|
||||||
|
|
||||||
|
# Default features - minimal CLI only
|
||||||
|
[features]
|
||||||
|
default = ["cli"]
|
||||||
|
cli = []
|
||||||
|
gui = ["dep:egui", "dep:eframe", "dep:egui_extras", "dep:image"]
|
||||||
|
gui-examples = ["gui"]
|
||||||
|
all-examples = ["gui-examples"]
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "nyash_rust"
|
||||||
|
path = "src/lib.rs"
|
||||||
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
|
# Main CLI binary - always available
|
||||||
|
[[bin]]
|
||||||
|
name = "nyash"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
# Examples for development - only available as examples, not bins
|
||||||
|
[[example]]
|
||||||
|
name = "gui_simple_notepad"
|
||||||
|
path = "examples/simple_notepad.rs"
|
||||||
|
required-features = ["gui-examples"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "gui_simple_notepad_v2"
|
||||||
|
path = "examples/simple_notepad_v2.rs"
|
||||||
|
required-features = ["gui-examples"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "gui_simple_notepad_ascii"
|
||||||
|
path = "examples/simple_notepad_ascii.rs"
|
||||||
|
required-features = ["gui-examples"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "gui_debug_notepad"
|
||||||
|
path = "examples/debug_notepad.rs"
|
||||||
|
required-features = ["gui-examples"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "gui_nyash_notepad_jp"
|
||||||
|
path = "examples/nyash_notepad_jp.rs"
|
||||||
|
required-features = ["gui-examples"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "gui_nyash_explorer"
|
||||||
|
path = "examples/nyash_explorer.rs"
|
||||||
|
required-features = ["gui-examples"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "gui_nyash_explorer_with_icons"
|
||||||
|
path = "examples/nyash_explorer_with_icons.rs"
|
||||||
|
required-features = ["gui-examples"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "gui_test_icon_extraction"
|
||||||
|
path = "examples/test_icon_extraction.rs"
|
||||||
|
required-features = ["gui-examples"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "gui_visual_node_prototype"
|
||||||
|
path = "development/egui_research/experiments/visual_node_prototype.rs"
|
||||||
|
required-features = ["gui-examples"]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
# エラーハンドリング
|
||||||
|
thiserror = "2.0"
|
||||||
|
anyhow = "1.0"
|
||||||
|
|
||||||
|
# シリアライゼーション(将来のAST永続化用)
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde_json = "1.0"
|
||||||
|
|
||||||
|
# コマンドライン(将来の CLI用)
|
||||||
|
clap = { version = "4.0", features = ["derive"] }
|
||||||
|
|
||||||
|
# 並行処理(GlobalBox用)
|
||||||
|
lazy_static = "1.5"
|
||||||
|
once_cell = "1.20"
|
||||||
|
|
||||||
|
# デバッグ・ログ
|
||||||
|
log = "0.4"
|
||||||
|
env_logger = "0.11"
|
||||||
|
|
||||||
|
# 日時処理
|
||||||
|
chrono = "0.4"
|
||||||
|
|
||||||
|
# HTTP通信(HttpClientBox用)
|
||||||
|
# reqwest = { version = "0.11", features = ["blocking"] } # Temporarily disabled
|
||||||
|
|
||||||
|
# 正規表現(RegexBox用)
|
||||||
|
regex = "1.0"
|
||||||
|
|
||||||
|
# WebAssembly対応
|
||||||
|
wasm-bindgen = "0.2"
|
||||||
|
console_error_panic_hook = "0.1"
|
||||||
|
js-sys = "0.3"
|
||||||
|
|
||||||
|
# WASM backend dependencies (Phase 8)
|
||||||
|
wabt = "0.10"
|
||||||
|
wasmtime = "35.0.0"
|
||||||
|
|
||||||
|
# GUI フレームワーク - only when gui feature is enabled
|
||||||
|
egui = { version = "0.29", optional = true }
|
||||||
|
eframe = { version = "0.29", default-features = false, features = ["default_fonts", "glow"], optional = true }
|
||||||
|
egui_extras = { version = "0.29", features = ["image"], optional = true }
|
||||||
|
image = { version = "0.25", features = ["png", "ico"], optional = true }
|
||||||
|
|
||||||
|
# Windows API
|
||||||
|
[target.'cfg(windows)'.dependencies]
|
||||||
|
windows = { version = "0.60", features = [
|
||||||
|
"Win32_Foundation",
|
||||||
|
"Win32_Storage_FileSystem",
|
||||||
|
"Win32_UI_Shell",
|
||||||
|
"Win32_UI_WindowsAndMessaging",
|
||||||
|
"Win32_System_Com",
|
||||||
|
"Win32_Graphics_Gdi",
|
||||||
|
] }
|
||||||
|
|
||||||
|
[dependencies.web-sys]
|
||||||
|
version = "0.3"
|
||||||
|
features = [
|
||||||
|
"console",
|
||||||
|
"Document",
|
||||||
|
"Element",
|
||||||
|
"HtmlElement",
|
||||||
|
"Window",
|
||||||
|
"DomTokenList",
|
||||||
|
"CssStyleDeclaration",
|
||||||
|
"HtmlCanvasElement",
|
||||||
|
"CanvasRenderingContext2d",
|
||||||
|
"ImageData",
|
||||||
|
"TextMetrics",
|
||||||
|
"CanvasGradient",
|
||||||
|
"CanvasPattern",
|
||||||
|
"Path2d",
|
||||||
|
]
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
# テスト・ベンチマークツール
|
||||||
|
criterion = "0.5"
|
||||||
|
|
||||||
|
# Benchmark configuration (will be added later)
|
||||||
|
# [[bench]]
|
||||||
|
# name = "box_performance"
|
||||||
|
# harness = false
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
# 最適化設定
|
||||||
|
opt-level = 3
|
||||||
|
lto = true
|
||||||
|
codegen-units = 1
|
||||||
|
panic = "abort"
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
# 開発用設定
|
||||||
|
opt-level = 0
|
||||||
|
debug = true
|
||||||
18
plugins/nyash-file/Cargo.toml
Normal file
18
plugins/nyash-file/Cargo.toml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
[package]
|
||||||
|
name = "nyash-file"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "nyash_file"
|
||||||
|
crate-type = ["cdylib"] # 動的ライブラリとして生成
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
# C FFI用
|
||||||
|
libc = "0.2"
|
||||||
|
|
||||||
|
# Nyashの基本的な型定義が必要になるかも(後で検討)
|
||||||
|
# nyash-core = { path = "../.." }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
196
plugins/nyash-file/src/lib.rs
Normal file
196
plugins/nyash-file/src/lib.rs
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
//! 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user