Files
hakorune/src/box_trait.rs

1002 lines
26 KiB
Rust
Raw Normal View History

/*!
* Nyash Box Trait System - Everything is Box in Rust
*
* This module implements the core "Everything is Box" philosophy using Rust's
* ownership system and trait system. Every value in Nyash is a Box that
* implements the NyashBox trait.
*/
use std::fmt::{Debug, Display};
use std::any::Any;
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicU64, Ordering};
use std::fs;
use std::path::Path;
// 🔥 新しい型エイリアス - 将来的にBox<dyn NyashBox>を全て置き換える
pub type SharedNyashBox = Arc<dyn NyashBox>;
/// 🔥 BoxBase + BoxCore革命 - 統一ID生成システム
/// CharmFlow教訓を活かした互換性保証の基盤
pub fn next_box_id() -> u64 {
static COUNTER: AtomicU64 = AtomicU64::new(1);
COUNTER.fetch_add(1, Ordering::Relaxed)
}
/// 🏗️ BoxBase - 全てのBox型の共通基盤構造体
/// Phase 2: 統一的な基盤データを提供
/// 🔥 Phase 1: ビルトインBox継承システム - 最小限拡張
#[derive(Debug, Clone, PartialEq)]
pub struct BoxBase {
pub id: u64,
pub parent_type_id: Option<std::any::TypeId>, // ビルトインBox継承用
}
impl BoxBase {
/// 新しいBoxBase作成 - 安全なID生成
pub fn new() -> Self {
Self {
id: next_box_id(),
parent_type_id: None, // ビルトインBox: 継承なし
}
}
/// ビルトインBox継承用コンストラクタ
pub fn with_parent_type(parent_type_id: std::any::TypeId) -> Self {
Self {
id: next_box_id(),
parent_type_id: Some(parent_type_id),
}
}
}
/// 🎯 BoxCore - Box型共通メソッドの統一インターフェース
/// Phase 2: 重複コードを削減する中核トレイト
/// 🔥 Phase 2: ビルトインBox継承システム対応
pub trait BoxCore: Send + Sync {
/// ボックスの一意ID取得
fn box_id(&self) -> u64;
/// 継承元の型ID取得ビルトインBox継承用
fn parent_type_id(&self) -> Option<std::any::TypeId>;
/// Display実装のための統一フォーマット
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result;
/// Any変換ダウンキャスト用
fn as_any(&self) -> &dyn Any;
/// Anyミュータブル変換ダウンキャスト用
fn as_any_mut(&mut self) -> &mut dyn Any;
}
/// The fundamental trait that all Nyash values must implement.
/// This embodies the "Everything is Box" philosophy with Rust's type safety.
pub trait NyashBox: BoxCore + Debug {
/// Convert this box to a string representation (equivalent to Python's toString())
fn to_string_box(&self) -> StringBox;
/// Check equality with another box (equivalent to Python's equals())
fn equals(&self, other: &dyn NyashBox) -> BoolBox;
/// Get the type name of this box for debugging
fn type_name(&self) -> &'static str {
std::any::type_name::<Self>()
}
/// Clone this box (equivalent to Python's copy())
fn clone_box(&self) -> Box<dyn NyashBox>;
/// Arc参照を返す新しいcloneメソッド参照共有
fn clone_arc(&self) -> SharedNyashBox {
Arc::from(self.clone_box())
}
// 🌟 TypeBox革命: Get type information as a Box
// Everything is Box極限実現 - 型情報もBoxとして取得
// TODO: 次のステップで完全実装
// fn get_type_box(&self) -> std::sync::Arc<crate::type_box::TypeBox>;
}
// ===== Basic Box Types =====
/// String values in Nyash - immutable and owned
#[derive(Debug, Clone, PartialEq)]
pub struct StringBox {
pub value: String,
base: BoxBase,
}
impl StringBox {
pub fn new(value: impl Into<String>) -> Self {
Self {
value: value.into(),
base: BoxBase::new(),
}
}
pub fn empty() -> Self {
Self::new("")
}
// ===== String Methods for Nyash =====
/// Split string by delimiter and return ArrayBox
pub fn split(&self, delimiter: &str) -> Box<dyn NyashBox> {
let parts: Vec<String> = self.value.split(delimiter).map(|s| s.to_string()).collect();
let array_elements: Vec<Box<dyn NyashBox>> = parts.into_iter()
.map(|s| Box::new(StringBox::new(s)) as Box<dyn NyashBox>)
.collect();
Box::new(ArrayBox::new_with_elements(array_elements))
}
/// Find substring and return position (or -1 if not found)
pub fn find(&self, search: &str) -> Box<dyn NyashBox> {
match self.value.find(search) {
Some(pos) => Box::new(IntegerBox::new(pos as i64)),
None => Box::new(IntegerBox::new(-1)),
}
}
/// Replace all occurrences of old with new
pub fn replace(&self, old: &str, new: &str) -> Box<dyn NyashBox> {
Box::new(StringBox::new(self.value.replace(old, new)))
}
/// Trim whitespace from both ends
pub fn trim(&self) -> Box<dyn NyashBox> {
Box::new(StringBox::new(self.value.trim()))
}
/// Convert to uppercase
pub fn to_upper(&self) -> Box<dyn NyashBox> {
Box::new(StringBox::new(self.value.to_uppercase()))
}
/// Convert to lowercase
pub fn to_lower(&self) -> Box<dyn NyashBox> {
Box::new(StringBox::new(self.value.to_lowercase()))
}
/// Check if string contains substring
pub fn contains(&self, search: &str) -> Box<dyn NyashBox> {
Box::new(BoolBox::new(self.value.contains(search)))
}
/// Check if string starts with prefix
pub fn starts_with(&self, prefix: &str) -> Box<dyn NyashBox> {
Box::new(BoolBox::new(self.value.starts_with(prefix)))
}
/// Check if string ends with suffix
pub fn ends_with(&self, suffix: &str) -> Box<dyn NyashBox> {
Box::new(BoolBox::new(self.value.ends_with(suffix)))
}
/// Join array elements using this string as delimiter
pub fn join(&self, array_box: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(array) = array_box.as_any().downcast_ref::<ArrayBox>() {
let strings: Vec<String> = array.items.read().unwrap()
.iter()
.map(|element| element.to_string_box().value)
.collect();
Box::new(StringBox::new(strings.join(&self.value)))
} else {
// If not an ArrayBox, treat as single element
Box::new(StringBox::new(array_box.to_string_box().value))
}
}
/// Get string length
pub fn length(&self) -> Box<dyn NyashBox> {
Box::new(IntegerBox::new(self.value.len() as i64))
}
/// Get character at index
pub fn get(&self, index: usize) -> Option<Box<dyn NyashBox>> {
if let Some(ch) = self.value.chars().nth(index) {
Some(Box::new(StringBox::new(ch.to_string())))
} else {
None
}
}
}
impl BoxCore for StringBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.value)
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl NyashBox for StringBox {
fn to_string_box(&self) -> StringBox {
self.clone()
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_string) = other.as_any().downcast_ref::<StringBox>() {
BoolBox::new(self.value == other_string.value)
} else {
BoolBox::new(false)
}
}
fn type_name(&self) -> &'static str {
"StringBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
}
impl Display for StringBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_box(f)
}
}
/// Integer values in Nyash - 64-bit signed integers
#[derive(Debug, Clone, PartialEq)]
pub struct IntegerBox {
pub value: i64,
base: BoxBase,
}
impl IntegerBox {
pub fn new(value: i64) -> Self {
Self {
value,
base: BoxBase::new()
}
}
pub fn zero() -> Self {
Self::new(0)
}
}
impl BoxCore for IntegerBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.value)
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl NyashBox for IntegerBox {
fn to_string_box(&self) -> StringBox {
StringBox::new(self.value.to_string())
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_int) = other.as_any().downcast_ref::<IntegerBox>() {
BoolBox::new(self.value == other_int.value)
} else {
BoolBox::new(false)
}
}
fn type_name(&self) -> &'static str {
"IntegerBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
}
impl Display for IntegerBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_box(f)
}
}
/// Boolean values in Nyash - true/false
#[derive(Debug, Clone, PartialEq)]
pub struct BoolBox {
pub value: bool,
base: BoxBase,
}
impl BoolBox {
pub fn new(value: bool) -> Self {
Self {
value,
base: BoxBase::new()
}
}
pub fn true_box() -> Self {
Self::new(true)
}
pub fn false_box() -> Self {
Self::new(false)
}
}
impl BoxCore for BoolBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", if self.value { "true" } else { "false" })
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl NyashBox for BoolBox {
fn to_string_box(&self) -> StringBox {
StringBox::new(if self.value { "true" } else { "false" })
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_bool) = other.as_any().downcast_ref::<BoolBox>() {
BoolBox::new(self.value == other_bool.value)
} else {
BoolBox::new(false)
}
}
fn type_name(&self) -> &'static str {
"BoolBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
}
impl Display for BoolBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_box(f)
}
}
/// Void/null values in Nyash - represents empty or null results
#[derive(Debug, Clone, PartialEq)]
pub struct VoidBox {
base: BoxBase,
}
impl VoidBox {
pub fn new() -> Self {
Self {
base: BoxBase::new()
}
}
}
impl Default for VoidBox {
fn default() -> Self {
Self::new()
}
}
impl BoxCore for VoidBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "void")
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl NyashBox for VoidBox {
fn to_string_box(&self) -> StringBox {
StringBox::new("void")
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
BoolBox::new(other.as_any().is::<VoidBox>())
}
fn type_name(&self) -> &'static str {
"VoidBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
}
impl Display for VoidBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_box(f)
}
}
// ArrayBox is now defined in boxes::array module
pub use crate::boxes::array::ArrayBox;
/// File values in Nyash - file system operations
#[derive(Debug, Clone)]
pub struct FileBox {
pub path: String,
base: BoxBase,
}
impl FileBox {
pub fn new(path: impl Into<String>) -> Self {
Self {
path: path.into(),
base: BoxBase::new(),
}
}
// ===== File Methods for Nyash =====
/// Read file contents as string
pub fn read(&self) -> Box<dyn NyashBox> {
match fs::read_to_string(&self.path) {
Ok(content) => Box::new(StringBox::new(content)),
Err(_) => Box::new(VoidBox::new()), // Return void on error for now
}
}
/// Write content to file
pub fn write(&self, content: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let content_str = content.to_string_box().value;
match fs::write(&self.path, content_str) {
Ok(_) => Box::new(BoolBox::new(true)),
Err(_) => Box::new(BoolBox::new(false)),
}
}
/// Check if file exists
pub fn exists(&self) -> Box<dyn NyashBox> {
Box::new(BoolBox::new(Path::new(&self.path).exists()))
}
/// Delete file
pub fn delete(&self) -> Box<dyn NyashBox> {
match fs::remove_file(&self.path) {
Ok(_) => Box::new(BoolBox::new(true)),
Err(_) => Box::new(BoolBox::new(false)),
}
}
/// Copy file to destination
pub fn copy(&self, dest_path: &str) -> Box<dyn NyashBox> {
match fs::copy(&self.path, dest_path) {
Ok(_) => Box::new(BoolBox::new(true)),
Err(_) => Box::new(BoolBox::new(false)),
}
}
}
impl BoxCore for FileBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "<FileBox: {}>", self.path)
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl NyashBox for FileBox {
fn to_string_box(&self) -> StringBox {
StringBox::new(format!("<FileBox: {}>", self.path))
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_file) = other.as_any().downcast_ref::<FileBox>() {
BoolBox::new(self.path == other_file.path)
} else {
BoolBox::new(false)
}
}
fn type_name(&self) -> &'static str {
"FileBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
}
impl Display for FileBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_box(f)
}
}
/// Error values in Nyash - represents error information
#[derive(Debug, Clone)]
pub struct ErrorBox {
pub error_type: String,
pub message: String,
base: BoxBase,
}
impl ErrorBox {
pub fn new(error_type: impl Into<String>, message: impl Into<String>) -> Self {
Self {
error_type: error_type.into(),
message: message.into(),
base: BoxBase::new(),
}
}
}
impl BoxCore for ErrorBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}: {}", self.error_type, self.message)
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl NyashBox for ErrorBox {
fn to_string_box(&self) -> StringBox {
StringBox::new(format!("{}: {}", self.error_type, self.message))
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_error) = other.as_any().downcast_ref::<ErrorBox>() {
BoolBox::new(self.error_type == other_error.error_type && self.message == other_error.message)
} else {
BoolBox::new(false)
}
}
fn type_name(&self) -> &'static str {
"ErrorBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
}
impl Display for ErrorBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_box(f)
}
}
/// Result values in Nyash - represents success or error results
#[derive(Debug)]
pub struct ResultBox {
pub is_success: bool,
pub value: Option<Box<dyn NyashBox>>,
pub error: Option<ErrorBox>,
base: BoxBase,
}
impl ResultBox {
pub fn new_success(value: Box<dyn NyashBox>) -> Self {
Self {
is_success: true,
value: Some(value),
error: None,
base: BoxBase::new(),
}
}
pub fn new_error(error: ErrorBox) -> Self {
Self {
is_success: false,
value: None,
error: Some(error),
base: BoxBase::new(),
}
}
// ===== Result Methods for Nyash =====
/// Check if result is successful
pub fn is_ok(&self) -> Box<dyn NyashBox> {
Box::new(BoolBox::new(self.is_success))
}
/// Get success value (returns void if error)
pub fn get_value(&self) -> Box<dyn NyashBox> {
match &self.value {
Some(val) => val.clone_box(),
None => Box::new(VoidBox::new()),
}
}
/// Get error (returns void if success)
pub fn get_error(&self) -> Box<dyn NyashBox> {
match &self.error {
Some(err) => Box::new(err.clone()),
None => Box::new(VoidBox::new()),
}
}
}
impl BoxCore for ResultBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.to_string_box().value)
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl NyashBox for ResultBox {
fn to_string_box(&self) -> StringBox {
if self.is_success {
if let Some(value) = &self.value {
StringBox::new(format!("Result(OK: {})", value.to_string_box().value))
} else {
StringBox::new("Result(OK: void)".to_string())
}
} else {
if let Some(error) = &self.error {
StringBox::new(format!("Result(Error: {})", error.to_string_box().value))
} else {
StringBox::new("Result(Error: unknown)".to_string())
}
}
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_result) = other.as_any().downcast_ref::<ResultBox>() {
if self.is_success != other_result.is_success {
return BoolBox::new(false);
}
if self.is_success {
// Compare success values
match (&self.value, &other_result.value) {
(Some(a), Some(b)) => a.equals(b.as_ref()),
(None, None) => BoolBox::new(true),
_ => BoolBox::new(false),
}
} else {
// Compare errors
match (&self.error, &other_result.error) {
(Some(a), Some(b)) => a.equals(b),
(None, None) => BoolBox::new(true),
_ => BoolBox::new(false),
}
}
} else {
BoolBox::new(false)
}
}
fn type_name(&self) -> &'static str {
"ResultBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
if self.is_success {
if let Some(value) = &self.value {
Box::new(ResultBox::new_success(value.clone_box()))
} else {
Box::new(ResultBox::new_success(Box::new(VoidBox::new())))
}
} else {
if let Some(error) = &self.error {
Box::new(ResultBox::new_error(error.clone()))
} else {
Box::new(ResultBox::new_error(ErrorBox::new("Unknown", "Unknown error")))
}
}
}
}
impl Display for ResultBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_box(f)
}
}
/// Future values in Nyash - represents async operations
#[derive(Debug)]
pub struct FutureBox {
pub result: Arc<Mutex<Option<Box<dyn NyashBox>>>>,
pub is_ready: Arc<Mutex<bool>>,
base: BoxBase,
}
impl Clone for FutureBox {
fn clone(&self) -> Self {
Self {
result: Arc::clone(&self.result),
is_ready: Arc::clone(&self.is_ready),
base: BoxBase::new(), // 新しいIDを生成
}
}
}
impl FutureBox {
pub fn new() -> Self {
Self {
result: Arc::new(Mutex::new(None)),
is_ready: Arc::new(Mutex::new(false)),
base: BoxBase::new(),
}
}
/// Set the result of the future
pub fn set_result(&self, value: Box<dyn NyashBox>) {
let mut result = self.result.lock().unwrap();
*result = Some(value);
let mut ready = self.is_ready.lock().unwrap();
*ready = true;
}
/// Get the result (blocks until ready)
pub fn get(&self) -> Box<dyn NyashBox> {
// 簡易実装: ビジーウェイト後でcondvarに改善
loop {
let ready = self.is_ready.lock().unwrap();
if *ready {
break;
}
drop(ready);
std::thread::yield_now();
}
let result = self.result.lock().unwrap();
result.as_ref().unwrap().clone_box()
}
/// Check if the future is ready
pub fn ready(&self) -> Box<dyn NyashBox> {
Box::new(BoolBox::new(*self.is_ready.lock().unwrap()))
}
/// Wait and get the result (for await implementation)
pub fn wait_and_get(&self) -> Result<Box<dyn NyashBox>, String> {
// 結果が準備できるまで待機
while !*self.is_ready.lock().unwrap() {
std::thread::yield_now();
}
let result = self.result.lock().unwrap();
result.as_ref()
.map(|v| v.clone_box())
.ok_or_else(|| "Future has no result".to_string())
}
}
impl NyashBox for FutureBox {
fn to_string_box(&self) -> StringBox {
let ready = *self.is_ready.lock().unwrap();
if ready {
let result = self.result.lock().unwrap();
if let Some(value) = result.as_ref() {
StringBox::new(format!("Future(ready: {})", value.to_string_box().value))
} else {
StringBox::new("Future(ready: void)".to_string())
}
} else {
StringBox::new("Future(pending)".to_string())
}
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_future) = other.as_any().downcast_ref::<FutureBox>() {
BoolBox::new(self.base.id == other_future.base.id)
} else {
BoolBox::new(false)
}
}
fn type_name(&self) -> &'static str {
"FutureBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
}
impl BoxCore for FutureBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.to_string_box().value)
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl Display for FutureBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_box(f)
}
}
// Re-export operation boxes from the dedicated operations module
pub use crate::box_arithmetic::{AddBox, SubtractBox, MultiplyBox, DivideBox, CompareBox};
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_string_box_creation() {
let s = StringBox::new("Hello, Rust!");
assert_eq!(s.value, "Hello, Rust!");
assert_eq!(s.type_name(), "StringBox");
assert_eq!(s.to_string_box().value, "Hello, Rust!");
}
#[test]
fn test_integer_box_creation() {
let i = IntegerBox::new(42);
assert_eq!(i.value, 42);
assert_eq!(i.type_name(), "IntegerBox");
assert_eq!(i.to_string_box().value, "42");
}
#[test]
fn test_bool_box_creation() {
let b = BoolBox::new(true);
assert_eq!(b.value, true);
assert_eq!(b.type_name(), "BoolBox");
assert_eq!(b.to_string_box().value, "true");
}
#[test]
fn test_box_equality() {
let s1 = StringBox::new("test");
let s2 = StringBox::new("test");
let s3 = StringBox::new("different");
assert!(s1.equals(&s2).value);
assert!(!s1.equals(&s3).value);
}
#[test]
fn test_add_box_integers() {
let left = Box::new(IntegerBox::new(5)) as Box<dyn NyashBox>;
let right = Box::new(IntegerBox::new(3)) as Box<dyn NyashBox>;
let add = AddBox::new(left, right);
let result = add.execute();
let result_int = result.as_any().downcast_ref::<IntegerBox>().unwrap();
assert_eq!(result_int.value, 8);
}
#[test]
fn test_add_box_strings() {
let left = Box::new(StringBox::new("Hello, ")) as Box<dyn NyashBox>;
let right = Box::new(StringBox::new("Rust!")) as Box<dyn NyashBox>;
let add = AddBox::new(left, right);
let result = add.execute();
let result_str = result.as_any().downcast_ref::<StringBox>().unwrap();
assert_eq!(result_str.value, "Hello, Rust!");
}
#[test]
fn test_box_ids_unique() {
let s1 = StringBox::new("test");
let s2 = StringBox::new("test");
// Same content but different IDs
assert_ne!(s1.box_id(), s2.box_id());
}
#[test]
fn test_void_box() {
let v = VoidBox::new();
assert_eq!(v.type_name(), "VoidBox");
assert_eq!(v.to_string_box().value, "void");
}
}