🎉 feat: BoxBase+BoxCore革命 Phase4F-G完了 - GUI/IO/データ系Box統一達成

 新たに完了したBox型統一アーキテクチャ移行
- EguiBox: GUI機能、デスクトップアプリケーション対応 🖼️
- BufferBox: バイナリデータ処理、メモリ効率最適化 📦
- FileBox: ファイルシステム操作、安全なIO処理 📁
- JSONBox: JSON解析/操作、型情報豊富な表示 📋

🎯 累積達成: 20+Box型の統一アーキテクチャ移行完了
- 音声系: SoundBox 
- データ系: MapBox, BufferBox, JSONBox 
- UI系: EguiBox 
- IO系: FileBox 
- 数学系: MathBox, FloatBox, RangeBox 
- 時間系: TimeBox, DateTimeBox, TimerBox 
- デバッグ系: DebugBox, RandomBox 
- 基本型: String/Integer/Bool等 

🔧 統一パターン確立による効果
- unsafe ID生成完全排除 → BoxBase::new()安全化
- 一貫したfmt_box()表示システム
- CharmFlow互換性問題の根本解決
- スレッドセーフ性とメモリ安全性向上

🐱 ゆっくり丁寧なアプローチで品質確保
Phase4継続中: 残りBox型も同じ高品質で統一化

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Moe Charm
2025-08-11 11:45:34 +09:00
parent edcf4bf0a7
commit 8892b0c006
6 changed files with 125 additions and 78 deletions

View File

@ -28,7 +28,7 @@
* ``` * ```
*/ */
use crate::box_trait::{NyashBox, StringBox, BoolBox, IntegerBox}; use crate::box_trait::{NyashBox, StringBox, BoolBox, IntegerBox, BoxCore, BoxBase};
use crate::boxes::array::ArrayBox; use crate::boxes::array::ArrayBox;
use std::any::Any; use std::any::Any;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@ -37,31 +37,21 @@ use std::fmt::{Debug, Display};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct BufferBox { pub struct BufferBox {
data: Arc<Mutex<Vec<u8>>>, data: Arc<Mutex<Vec<u8>>>,
id: u64, base: BoxBase,
} }
impl BufferBox { impl BufferBox {
pub fn new() -> Self { pub fn new() -> Self {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
BufferBox { BufferBox {
data: Arc::new(Mutex::new(Vec::new())), data: Arc::new(Mutex::new(Vec::new())),
id, base: BoxBase::new(),
} }
} }
pub fn from_vec(data: Vec<u8>) -> Self { pub fn from_vec(data: Vec<u8>) -> Self {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
BufferBox { BufferBox {
data: Arc::new(Mutex::new(data)), data: Arc::new(Mutex::new(data)),
id, base: BoxBase::new(),
} }
} }
@ -158,6 +148,23 @@ impl BufferBox {
} }
} }
impl BoxCore for BufferBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let data = self.data.lock().unwrap();
write!(f, "BufferBox({} bytes)", data.len())
}
}
impl Display for BufferBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_box(f)
}
}
impl NyashBox for BufferBox { impl NyashBox for BufferBox {
fn clone_box(&self) -> Box<dyn NyashBox> { fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone()) Box::new(self.clone())
@ -176,9 +183,6 @@ impl NyashBox for BufferBox {
"BufferBox" "BufferBox"
} }
fn box_id(&self) -> u64 {
self.id
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox { fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_buffer) = other.as_any().downcast_ref::<BufferBox>() { if let Some(other_buffer) = other.as_any().downcast_ref::<BufferBox>() {

View File

@ -31,7 +31,7 @@
* - `run()`はブロッキング動作(アプリ終了まで制御を返さない) * - `run()`はブロッキング動作(アプリ終了まで制御を返さない)
*/ */
use crate::box_trait::{NyashBox, StringBox, BoolBox}; use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
use crate::interpreter::RuntimeError; use crate::interpreter::RuntimeError;
use std::any::Any; use std::any::Any;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@ -47,6 +47,7 @@ use eframe::{self, epaint::Vec2};
/// app.run() /// app.run()
/// ``` /// ```
pub struct EguiBox { pub struct EguiBox {
base: BoxBase,
title: String, title: String,
size: Vec2, size: Vec2,
app_state: Arc<Mutex<Box<dyn Any + Send>>>, app_state: Arc<Mutex<Box<dyn Any + Send>>>,
@ -65,6 +66,7 @@ impl std::fmt::Debug for EguiBox {
impl EguiBox { impl EguiBox {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
base: BoxBase::new(),
title: "Nyash GUI Application".to_string(), title: "Nyash GUI Application".to_string(),
size: Vec2::new(800.0, 600.0), size: Vec2::new(800.0, 600.0),
app_state: Arc::new(Mutex::new(Box::new(()) as Box<dyn Any + Send>)), app_state: Arc::new(Mutex::new(Box::new(()) as Box<dyn Any + Send>)),
@ -100,6 +102,22 @@ impl eframe::App for NyashApp {
} }
} }
impl BoxCore for EguiBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "EguiBox('{}', {}x{})", self.title, self.size.x, self.size.y)
}
}
impl std::fmt::Display for EguiBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_box(f)
}
}
impl NyashBox for EguiBox { impl NyashBox for EguiBox {
fn to_string_box(&self) -> StringBox { fn to_string_box(&self) -> StringBox {
StringBox::new( StringBox::new(
@ -110,6 +128,7 @@ impl NyashBox for EguiBox {
fn clone_box(&self) -> Box<dyn NyashBox> { fn clone_box(&self) -> Box<dyn NyashBox> {
// GUI Boxはクローン不可単一インスタンス // GUI Boxはクローン不可単一インスタンス
Box::new(Self { Box::new(Self {
base: BoxBase::new(),
title: self.title.clone(), title: self.title.clone(),
size: self.size, size: self.size,
app_state: Arc::new(Mutex::new(Box::new(()) as Box<dyn Any + Send>)), app_state: Arc::new(Mutex::new(Box::new(()) as Box<dyn Any + Send>)),
@ -133,10 +152,6 @@ impl NyashBox for EguiBox {
"EguiBox" "EguiBox"
} }
fn box_id(&self) -> u64 {
// 簡易的なIDとしてポインタアドレスを使用
self as *const _ as u64
}
} }
// EguiBoxのメソッド実装実際にはインタープリターから呼ばれない // EguiBoxのメソッド実装実際にはインタープリターから呼ばれない

View File

@ -2,7 +2,7 @@
// Nyashの箱システムによるファイル入出力を提供します。 // Nyashの箱システムによるファイル入出力を提供します。
// 参考: 既存Boxの設計思想 // 参考: 既存Boxの設計思想
use crate::box_trait::{NyashBox, StringBox, BoolBox}; use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
use std::any::Any; use std::any::Any;
use std::fs::{File, OpenOptions}; use std::fs::{File, OpenOptions};
use std::io::{Read, Write, Result}; use std::io::{Read, Write, Result};
@ -12,21 +12,16 @@ use std::sync::{Arc, Mutex};
pub struct FileBox { pub struct FileBox {
file: Arc<Mutex<File>>, file: Arc<Mutex<File>>,
path: Arc<String>, path: Arc<String>,
id: u64, base: BoxBase,
} }
impl FileBox { impl FileBox {
pub fn open(path: &str) -> Result<Self> { pub fn open(path: &str) -> Result<Self> {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
let file = OpenOptions::new().read(true).write(true).create(true).open(path)?; let file = OpenOptions::new().read(true).write(true).create(true).open(path)?;
Ok(FileBox { Ok(FileBox {
file: Arc::new(Mutex::new(file)), file: Arc::new(Mutex::new(file)),
path: Arc::new(path.to_string()), path: Arc::new(path.to_string()),
id, base: BoxBase::new(),
}) })
} }
@ -82,6 +77,16 @@ impl FileBox {
} }
} }
impl BoxCore for FileBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "FileBox({})", self.path)
}
}
impl NyashBox for FileBox { impl NyashBox for FileBox {
fn clone_box(&self) -> Box<dyn NyashBox> { fn clone_box(&self) -> Box<dyn NyashBox> {
// Note: Cannot truly clone a File handle, so create a new one to the same path // Note: Cannot truly clone a File handle, so create a new one to the same path
@ -103,9 +108,6 @@ impl NyashBox for FileBox {
"FileBox" "FileBox"
} }
fn box_id(&self) -> u64 {
self.id
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox { fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_file) = other.as_any().downcast_ref::<FileBox>() { if let Some(other_file) = other.as_any().downcast_ref::<FileBox>() {
@ -115,3 +117,9 @@ impl NyashBox for FileBox {
} }
} }
} }
impl std::fmt::Display for FileBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_box(f)
}
}

View File

@ -2,7 +2,7 @@
// Nyashの箱システムによるJSON解析・生成を提供します。 // Nyashの箱システムによるJSON解析・生成を提供します。
// 参考: 既存Boxの設計思想 // 参考: 既存Boxの設計思想
use crate::box_trait::{NyashBox, StringBox, BoolBox, IntegerBox}; use crate::box_trait::{NyashBox, BoxCore, BoxBase, StringBox, BoolBox, IntegerBox};
use crate::boxes::array::ArrayBox; use crate::boxes::array::ArrayBox;
use crate::boxes::map_box::MapBox; use crate::boxes::map_box::MapBox;
use std::any::Any; use std::any::Any;
@ -12,32 +12,22 @@ use serde_json::{Value, Error};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct JSONBox { pub struct JSONBox {
value: Arc<Mutex<Value>>, value: Arc<Mutex<Value>>,
id: u64, base: BoxBase,
} }
impl JSONBox { impl JSONBox {
pub fn from_str(s: &str) -> Result<Self, Error> { pub fn from_str(s: &str) -> Result<Self, Error> {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
let value = serde_json::from_str(s)?; let value = serde_json::from_str(s)?;
Ok(JSONBox { Ok(JSONBox {
value: Arc::new(Mutex::new(value)), value: Arc::new(Mutex::new(value)),
id base: BoxBase::new()
}) })
} }
pub fn new(value: Value) -> Self { pub fn new(value: Value) -> Self {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
JSONBox { JSONBox {
value: Arc::new(Mutex::new(value)), value: Arc::new(Mutex::new(value)),
id base: BoxBase::new()
} }
} }
@ -129,6 +119,35 @@ impl JSONBox {
} }
} }
impl BoxCore for JSONBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let value = self.value.lock().unwrap();
let json_type = match *value {
Value::Null => "null",
Value::Bool(_) => "boolean",
Value::Number(_) => "number",
Value::String(_) => "string",
Value::Array(ref arr) => {
return write!(f, "JSONBox[array:{}]", arr.len());
},
Value::Object(ref obj) => {
return write!(f, "JSONBox[object:{}]", obj.len());
},
};
write!(f, "JSONBox[{}]", json_type)
}
}
impl std::fmt::Display for JSONBox {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.fmt_box(f)
}
}
impl NyashBox for JSONBox { impl NyashBox for JSONBox {
fn clone_box(&self) -> Box<dyn NyashBox> { fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone()) Box::new(self.clone())
@ -147,9 +166,6 @@ impl NyashBox for JSONBox {
"JSONBox" "JSONBox"
} }
fn box_id(&self) -> u64 {
self.id
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox { fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_json) = other.as_any().downcast_ref::<JSONBox>() { if let Some(other_json) = other.as_any().downcast_ref::<JSONBox>() {

View File

@ -103,7 +103,7 @@
* - 存在しないキーの取得は "Key not found" メッセージ返却 * - 存在しないキーの取得は "Key not found" メッセージ返却
*/ */
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox}; use crate::box_trait::{BoxCore, BoxBase, NyashBox, StringBox, IntegerBox, BoolBox};
use crate::boxes::array::ArrayBox; use crate::boxes::array::ArrayBox;
use std::fmt::{Debug, Display}; use std::fmt::{Debug, Display};
use std::any::Any; use std::any::Any;
@ -114,20 +114,14 @@ use std::sync::{Arc, Mutex};
#[derive(Clone)] #[derive(Clone)]
pub struct MapBox { pub struct MapBox {
data: Arc<Mutex<HashMap<String, Box<dyn NyashBox>>>>, data: Arc<Mutex<HashMap<String, Box<dyn NyashBox>>>>,
id: u64, base: BoxBase,
} }
impl MapBox { impl MapBox {
pub fn new() -> Self { pub fn new() -> Self {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
Self { Self {
data: Arc::new(Mutex::new(HashMap::new())), data: Arc::new(Mutex::new(HashMap::new())),
id, base: BoxBase::new(),
} }
} }
@ -229,6 +223,17 @@ impl MapBox {
} }
} }
impl BoxCore for MapBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let size = self.data.lock().unwrap().len();
write!(f, "MapBox(size={})", size)
}
}
impl NyashBox for MapBox { impl NyashBox for MapBox {
fn type_name(&self) -> &'static str { fn type_name(&self) -> &'static str {
"MapBox" "MapBox"
@ -246,7 +251,7 @@ impl NyashBox for MapBox {
fn equals(&self, other: &dyn NyashBox) -> BoolBox { fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_map) = other.as_any().downcast_ref::<MapBox>() { if let Some(other_map) = other.as_any().downcast_ref::<MapBox>() {
// 同じインスタンスかチェック(データの共有を考慮) // 同じインスタンスかチェック(データの共有を考慮)
BoolBox::new(self.id == other_map.id) BoolBox::new(self.box_id() == other_map.box_id())
} else { } else {
BoolBox::new(false) BoolBox::new(false)
} }
@ -255,15 +260,11 @@ impl NyashBox for MapBox {
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
self self
} }
fn box_id(&self) -> u64 {
self.id
}
} }
impl Display for MapBox { impl Display for MapBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.to_string_box().value) self.fmt_box(f)
} }
} }
@ -271,7 +272,7 @@ impl Debug for MapBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let data = self.data.lock().unwrap(); let data = self.data.lock().unwrap();
f.debug_struct("MapBox") f.debug_struct("MapBox")
.field("id", &self.id) .field("id", &self.base.id)
.field("size", &data.len()) .field("size", &data.len())
.field("keys", &data.keys().collect::<Vec<_>>()) .field("keys", &data.keys().collect::<Vec<_>>())
.finish() .finish()

View File

@ -138,7 +138,7 @@
* - Web環境では制限が多いユーザー操作後のみ音声再生可能 * - Web環境では制限が多いユーザー操作後のみ音声再生可能
*/ */
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox}; use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, BoxCore, BoxBase};
use std::fmt::{Debug, Display}; use std::fmt::{Debug, Display};
use std::any::Any; use std::any::Any;
use std::process::Command; use std::process::Command;
@ -147,18 +147,14 @@ use std::time::Duration;
/// 音響効果を提供するBox /// 音響効果を提供するBox
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct SoundBox { pub struct SoundBox {
id: u64, base: BoxBase,
} }
impl SoundBox { impl SoundBox {
pub fn new() -> Self { pub fn new() -> Self {
static mut COUNTER: u64 = 0; Self {
let id = unsafe { base: BoxBase::new()
COUNTER += 1; }
COUNTER
};
Self { id }
} }
/// ビープ音を鳴らす(基本) /// ビープ音を鳴らす(基本)
@ -332,7 +328,7 @@ impl NyashBox for SoundBox {
fn equals(&self, other: &dyn NyashBox) -> BoolBox { fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_sound) = other.as_any().downcast_ref::<SoundBox>() { if let Some(other_sound) = other.as_any().downcast_ref::<SoundBox>() {
BoolBox::new(self.id == other_sound.id) BoolBox::new(self.base.id == other_sound.base.id)
} else { } else {
BoolBox::new(false) BoolBox::new(false)
} }
@ -342,13 +338,20 @@ impl NyashBox for SoundBox {
self self
} }
}
impl BoxCore for SoundBox {
fn box_id(&self) -> u64 { fn box_id(&self) -> u64 {
self.id self.base.id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "SoundBox()")
} }
} }
impl Display for SoundBox { impl Display for SoundBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "SoundBox()") self.fmt_box(f)
} }
} }