chore(fmt): add legacy stubs and strip trailing whitespace to unblock cargo fmt
This commit is contained in:
@ -1,25 +1,55 @@
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
|
||||
use std::any::Any;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AotCompilerBox { base: BoxBase }
|
||||
pub struct AotCompilerBox {
|
||||
base: BoxBase,
|
||||
}
|
||||
|
||||
impl AotCompilerBox { pub fn new() -> Self { Self { base: BoxBase::new() } } }
|
||||
impl AotCompilerBox {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BoxCore for AotCompilerBox {
|
||||
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, "AotCompilerBox") }
|
||||
fn as_any(&self) -> &dyn Any { self }
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any { self }
|
||||
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, "AotCompilerBox")
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl NyashBox for AotCompilerBox {
|
||||
fn to_string_box(&self) -> StringBox { StringBox::new("AotCompilerBox".to_string()) }
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<AotCompilerBox>()) }
|
||||
fn type_name(&self) -> &'static str { "AotCompilerBox" }
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self { base: self.base.clone() }) }
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new("AotCompilerBox".to_string())
|
||||
}
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
BoolBox::new(other.as_any().is::<AotCompilerBox>())
|
||||
}
|
||||
fn type_name(&self) -> &'static str {
|
||||
"AotCompilerBox"
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(Self {
|
||||
base: self.base.clone(),
|
||||
})
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
}
|
||||
|
||||
impl AotCompilerBox {
|
||||
@ -28,26 +58,32 @@ impl AotCompilerBox {
|
||||
pub fn compile(&self, file: &str, out: &str) -> Box<dyn NyashBox> {
|
||||
let mut cmd = match std::env::current_exe() {
|
||||
Ok(p) => std::process::Command::new(p),
|
||||
Err(e) => return Box::new(StringBox::new(format!("ERR: current_exe(): {}", e)))
|
||||
Err(e) => return Box::new(StringBox::new(format!("ERR: current_exe(): {}", e))),
|
||||
};
|
||||
// Propagate relevant envs (AOT/JIT observe)
|
||||
let c = cmd.arg("--backend").arg("vm") // ensures runner path
|
||||
.arg("--compile-native")
|
||||
.arg("-o").arg(out)
|
||||
.arg(file)
|
||||
.envs(std::env::vars());
|
||||
let c = cmd
|
||||
.arg("--backend")
|
||||
.arg("vm") // ensures runner path
|
||||
.arg("--compile-native")
|
||||
.arg("-o")
|
||||
.arg(out)
|
||||
.arg(file)
|
||||
.envs(std::env::vars());
|
||||
match c.output() {
|
||||
Ok(o) => {
|
||||
let mut s = String::new();
|
||||
s.push_str(&String::from_utf8_lossy(&o.stdout));
|
||||
s.push_str(&String::from_utf8_lossy(&o.stderr));
|
||||
if !o.status.success() {
|
||||
s = format!("AOT FAILED (code={}):\n{}", o.status.code().unwrap_or(-1), s);
|
||||
s = format!(
|
||||
"AOT FAILED (code={}):\n{}",
|
||||
o.status.code().unwrap_or(-1),
|
||||
s
|
||||
);
|
||||
}
|
||||
Box::new(StringBox::new(s))
|
||||
}
|
||||
Err(e) => Box::new(StringBox::new(format!("ERR: spawn compile-native: {}", e)))
|
||||
Err(e) => Box::new(StringBox::new(format!("ERR: spawn compile-native: {}", e))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, VoidBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox, VoidBox};
|
||||
use std::any::Any;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -10,44 +10,104 @@ pub struct AotConfigBox {
|
||||
pub plugin_paths: Option<String>,
|
||||
}
|
||||
|
||||
impl AotConfigBox { pub fn new() -> Self { Self { base: BoxBase::new(), output_file: None, emit_obj_out: None, plugin_paths: None } } }
|
||||
impl AotConfigBox {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
base: BoxBase::new(),
|
||||
output_file: None,
|
||||
emit_obj_out: None,
|
||||
plugin_paths: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BoxCore for AotConfigBox {
|
||||
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, "AotConfigBox") }
|
||||
fn as_any(&self) -> &dyn Any { self }
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any { self }
|
||||
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, "AotConfigBox")
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl NyashBox for AotConfigBox {
|
||||
fn to_string_box(&self) -> StringBox { self.summary() }
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<AotConfigBox>()) }
|
||||
fn type_name(&self) -> &'static str { "AotConfigBox" }
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self { base: self.base.clone(), output_file: self.output_file.clone(), emit_obj_out: self.emit_obj_out.clone(), plugin_paths: self.plugin_paths.clone() }) }
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
self.summary()
|
||||
}
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
BoolBox::new(other.as_any().is::<AotConfigBox>())
|
||||
}
|
||||
fn type_name(&self) -> &'static str {
|
||||
"AotConfigBox"
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(Self {
|
||||
base: self.base.clone(),
|
||||
output_file: self.output_file.clone(),
|
||||
emit_obj_out: self.emit_obj_out.clone(),
|
||||
plugin_paths: self.plugin_paths.clone(),
|
||||
})
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
}
|
||||
|
||||
impl AotConfigBox {
|
||||
pub fn set_output(&mut self, path: &str) -> Box<dyn NyashBox> { self.output_file = Some(path.to_string()); Box::new(VoidBox::new()) }
|
||||
pub fn set_obj_out(&mut self, path: &str) -> Box<dyn NyashBox> { self.emit_obj_out = Some(path.to_string()); Box::new(VoidBox::new()) }
|
||||
pub fn set_plugin_paths(&mut self, paths: &str) -> Box<dyn NyashBox> { self.plugin_paths = Some(paths.to_string()); Box::new(VoidBox::new()) }
|
||||
pub fn clear(&mut self) -> Box<dyn NyashBox> { self.output_file = None; self.emit_obj_out = None; self.plugin_paths = None; Box::new(VoidBox::new()) }
|
||||
pub fn set_output(&mut self, path: &str) -> Box<dyn NyashBox> {
|
||||
self.output_file = Some(path.to_string());
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn set_obj_out(&mut self, path: &str) -> Box<dyn NyashBox> {
|
||||
self.emit_obj_out = Some(path.to_string());
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn set_plugin_paths(&mut self, paths: &str) -> Box<dyn NyashBox> {
|
||||
self.plugin_paths = Some(paths.to_string());
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn clear(&mut self) -> Box<dyn NyashBox> {
|
||||
self.output_file = None;
|
||||
self.emit_obj_out = None;
|
||||
self.plugin_paths = None;
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
|
||||
/// Apply staged config to environment for CLI/runner consumption
|
||||
pub fn apply(&self) -> Box<dyn NyashBox> {
|
||||
if let Some(p) = &self.output_file { std::env::set_var("NYASH_AOT_OUT", p); }
|
||||
if let Some(p) = &self.emit_obj_out { std::env::set_var("NYASH_AOT_OBJECT_OUT", p); }
|
||||
if let Some(p) = &self.plugin_paths { std::env::set_var("NYASH_PLUGIN_PATHS", p); }
|
||||
if let Some(p) = &self.output_file {
|
||||
std::env::set_var("NYASH_AOT_OUT", p);
|
||||
}
|
||||
if let Some(p) = &self.emit_obj_out {
|
||||
std::env::set_var("NYASH_AOT_OBJECT_OUT", p);
|
||||
}
|
||||
if let Some(p) = &self.plugin_paths {
|
||||
std::env::set_var("NYASH_PLUGIN_PATHS", p);
|
||||
}
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
|
||||
pub fn summary(&self) -> StringBox {
|
||||
let s = format!(
|
||||
"output={} obj_out={} plugin_paths={}",
|
||||
self.output_file.clone().unwrap_or_else(|| "<none>".to_string()),
|
||||
self.emit_obj_out.clone().unwrap_or_else(|| "<none>".to_string()),
|
||||
self.plugin_paths.clone().unwrap_or_else(|| "<none>".to_string()),
|
||||
self.output_file
|
||||
.clone()
|
||||
.unwrap_or_else(|| "<none>".to_string()),
|
||||
self.emit_obj_out
|
||||
.clone()
|
||||
.unwrap_or_else(|| "<none>".to_string()),
|
||||
self.plugin_paths
|
||||
.clone()
|
||||
.unwrap_or_else(|| "<none>".to_string()),
|
||||
);
|
||||
StringBox::new(s)
|
||||
}
|
||||
|
||||
@ -2,39 +2,39 @@
|
||||
// Nyashの箱システムによる配列・リスト操作を提供します。
|
||||
// RwLockパターンで内部可変性を実現(Phase 9.75-B Arc<Mutex>削除)
|
||||
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, IntegerBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox};
|
||||
use std::any::Any;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::fmt::Display;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
pub struct ArrayBox {
|
||||
pub items: Arc<RwLock<Vec<Box<dyn NyashBox>>>>, // Arc追加
|
||||
pub items: Arc<RwLock<Vec<Box<dyn NyashBox>>>>, // Arc追加
|
||||
base: BoxBase,
|
||||
}
|
||||
|
||||
impl ArrayBox {
|
||||
/// 新しいArrayBoxを作成
|
||||
pub fn new() -> Self {
|
||||
ArrayBox {
|
||||
items: Arc::new(RwLock::new(Vec::new())), // Arc::new追加
|
||||
ArrayBox {
|
||||
items: Arc::new(RwLock::new(Vec::new())), // Arc::new追加
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 要素を持つArrayBoxを作成
|
||||
pub fn new_with_elements(elements: Vec<Box<dyn NyashBox>>) -> Self {
|
||||
ArrayBox {
|
||||
items: Arc::new(RwLock::new(elements)), // Arc::new追加
|
||||
ArrayBox {
|
||||
items: Arc::new(RwLock::new(elements)), // Arc::new追加
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 要素を追加
|
||||
pub fn push(&self, item: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
self.items.write().unwrap().push(item);
|
||||
Box::new(StringBox::new("ok"))
|
||||
}
|
||||
|
||||
|
||||
/// 最後の要素を取り出す
|
||||
pub fn pop(&self) -> Box<dyn NyashBox> {
|
||||
match self.items.write().unwrap().pop() {
|
||||
@ -42,7 +42,7 @@ impl ArrayBox {
|
||||
None => Box::new(crate::boxes::null_box::NullBox::new()),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 要素数を取得
|
||||
pub fn length(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(IntegerBox::new(self.items.read().unwrap().len() as i64))
|
||||
@ -52,7 +52,7 @@ impl ArrayBox {
|
||||
pub fn len(&self) -> usize {
|
||||
self.items.read().unwrap().len()
|
||||
}
|
||||
|
||||
|
||||
/// インデックスで要素を取得
|
||||
pub fn get(&self, index: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(idx_box) = index.as_any().downcast_ref::<IntegerBox>() {
|
||||
@ -61,7 +61,11 @@ impl ArrayBox {
|
||||
match items.get(idx) {
|
||||
Some(item) => {
|
||||
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||
if item.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some() {
|
||||
if item
|
||||
.as_any()
|
||||
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
|
||||
.is_some()
|
||||
{
|
||||
return item.share_box();
|
||||
}
|
||||
item.clone_box()
|
||||
@ -72,7 +76,7 @@ impl ArrayBox {
|
||||
Box::new(StringBox::new("Error: get() requires integer index"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// インデックスで要素を設定
|
||||
pub fn set(&self, index: Box<dyn NyashBox>, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(idx_box) = index.as_any().downcast_ref::<IntegerBox>() {
|
||||
@ -92,7 +96,7 @@ impl ArrayBox {
|
||||
Box::new(StringBox::new("Error: set() requires integer index"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 要素を削除
|
||||
pub fn remove(&self, index: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(idx_box) = index.as_any().downcast_ref::<IntegerBox>() {
|
||||
@ -107,7 +111,7 @@ impl ArrayBox {
|
||||
Box::new(StringBox::new("Error: remove() requires integer index"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 指定された値のインデックスを検索
|
||||
pub fn indexOf(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let items = self.items.read().unwrap();
|
||||
@ -118,7 +122,7 @@ impl ArrayBox {
|
||||
}
|
||||
Box::new(IntegerBox::new(-1))
|
||||
}
|
||||
|
||||
|
||||
/// 指定された値が含まれているか確認
|
||||
pub fn contains(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let items = self.items.read().unwrap();
|
||||
@ -129,13 +133,13 @@ impl ArrayBox {
|
||||
}
|
||||
Box::new(BoolBox::new(false))
|
||||
}
|
||||
|
||||
|
||||
/// 配列を空にする
|
||||
pub fn clear(&self) -> Box<dyn NyashBox> {
|
||||
self.items.write().unwrap().clear();
|
||||
Box::new(StringBox::new("ok"))
|
||||
}
|
||||
|
||||
|
||||
/// 文字列結合
|
||||
pub fn join(&self, delimiter: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(sep_box) = delimiter.as_any().downcast_ref::<StringBox>() {
|
||||
@ -149,66 +153,78 @@ impl ArrayBox {
|
||||
Box::new(StringBox::new("Error: join() requires string separator"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 配列をソート(昇順)
|
||||
pub fn sort(&self) -> Box<dyn NyashBox> {
|
||||
let mut items = self.items.write().unwrap();
|
||||
|
||||
|
||||
// Numeric values first, then string values
|
||||
items.sort_by(|a, b| {
|
||||
use std::cmp::Ordering;
|
||||
|
||||
|
||||
// Try to compare as numbers first
|
||||
if let (Some(a_int), Some(b_int)) = (
|
||||
a.as_any().downcast_ref::<IntegerBox>(),
|
||||
b.as_any().downcast_ref::<IntegerBox>()
|
||||
b.as_any().downcast_ref::<IntegerBox>(),
|
||||
) {
|
||||
return a_int.value.cmp(&b_int.value);
|
||||
}
|
||||
|
||||
|
||||
// Try FloatBox comparison
|
||||
if let (Some(a_float), Some(b_float)) = (
|
||||
a.as_any().downcast_ref::<crate::boxes::math_box::FloatBox>(),
|
||||
b.as_any().downcast_ref::<crate::boxes::math_box::FloatBox>()
|
||||
a.as_any()
|
||||
.downcast_ref::<crate::boxes::math_box::FloatBox>(),
|
||||
b.as_any()
|
||||
.downcast_ref::<crate::boxes::math_box::FloatBox>(),
|
||||
) {
|
||||
return a_float.value.partial_cmp(&b_float.value).unwrap_or(Ordering::Equal);
|
||||
return a_float
|
||||
.value
|
||||
.partial_cmp(&b_float.value)
|
||||
.unwrap_or(Ordering::Equal);
|
||||
}
|
||||
|
||||
|
||||
// Mixed numeric types
|
||||
if let (Some(a_int), Some(b_float)) = (
|
||||
a.as_any().downcast_ref::<IntegerBox>(),
|
||||
b.as_any().downcast_ref::<crate::boxes::math_box::FloatBox>()
|
||||
b.as_any()
|
||||
.downcast_ref::<crate::boxes::math_box::FloatBox>(),
|
||||
) {
|
||||
return (a_int.value as f64).partial_cmp(&b_float.value).unwrap_or(Ordering::Equal);
|
||||
return (a_int.value as f64)
|
||||
.partial_cmp(&b_float.value)
|
||||
.unwrap_or(Ordering::Equal);
|
||||
}
|
||||
|
||||
|
||||
if let (Some(a_float), Some(b_int)) = (
|
||||
a.as_any().downcast_ref::<crate::boxes::math_box::FloatBox>(),
|
||||
b.as_any().downcast_ref::<IntegerBox>()
|
||||
a.as_any()
|
||||
.downcast_ref::<crate::boxes::math_box::FloatBox>(),
|
||||
b.as_any().downcast_ref::<IntegerBox>(),
|
||||
) {
|
||||
return a_float.value.partial_cmp(&(b_int.value as f64)).unwrap_or(Ordering::Equal);
|
||||
return a_float
|
||||
.value
|
||||
.partial_cmp(&(b_int.value as f64))
|
||||
.unwrap_or(Ordering::Equal);
|
||||
}
|
||||
|
||||
|
||||
// Fall back to string comparison
|
||||
let a_str = a.to_string_box().value;
|
||||
let b_str = b.to_string_box().value;
|
||||
a_str.cmp(&b_str)
|
||||
});
|
||||
|
||||
|
||||
Box::new(StringBox::new("ok"))
|
||||
}
|
||||
|
||||
|
||||
/// 配列を反転
|
||||
pub fn reverse(&self) -> Box<dyn NyashBox> {
|
||||
let mut items = self.items.write().unwrap();
|
||||
items.reverse();
|
||||
Box::new(StringBox::new("ok"))
|
||||
}
|
||||
|
||||
|
||||
/// 部分配列を取得
|
||||
pub fn slice(&self, start: Box<dyn NyashBox>, end: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let items = self.items.read().unwrap();
|
||||
|
||||
|
||||
// Extract start and end indices
|
||||
let start_idx = if let Some(start_int) = start.as_any().downcast_ref::<IntegerBox>() {
|
||||
if start_int.value < 0 {
|
||||
@ -217,9 +233,11 @@ impl ArrayBox {
|
||||
start_int.value as usize
|
||||
}
|
||||
} else {
|
||||
return Box::new(StringBox::new("Error: slice() start index must be an integer"));
|
||||
return Box::new(StringBox::new(
|
||||
"Error: slice() start index must be an integer",
|
||||
));
|
||||
};
|
||||
|
||||
|
||||
let end_idx = if let Some(end_int) = end.as_any().downcast_ref::<IntegerBox>() {
|
||||
if end_int.value < 0 {
|
||||
items.len()
|
||||
@ -227,26 +245,32 @@ impl ArrayBox {
|
||||
(end_int.value as usize).min(items.len())
|
||||
}
|
||||
} else {
|
||||
return Box::new(StringBox::new("Error: slice() end index must be an integer"));
|
||||
return Box::new(StringBox::new(
|
||||
"Error: slice() end index must be an integer",
|
||||
));
|
||||
};
|
||||
|
||||
|
||||
// Validate indices
|
||||
if start_idx > items.len() || start_idx > end_idx {
|
||||
return Box::new(ArrayBox::new());
|
||||
}
|
||||
|
||||
|
||||
// Create slice
|
||||
let slice_items: Vec<Box<dyn NyashBox>> = items[start_idx..end_idx]
|
||||
.iter()
|
||||
.map(|item| {
|
||||
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||
if item.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some() {
|
||||
if item
|
||||
.as_any()
|
||||
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
|
||||
.is_some()
|
||||
{
|
||||
return item.share_box();
|
||||
}
|
||||
item.clone_box()
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
||||
Box::new(ArrayBox::new_with_elements(slice_items))
|
||||
}
|
||||
}
|
||||
@ -256,18 +280,23 @@ impl Clone for ArrayBox {
|
||||
fn clone(&self) -> Self {
|
||||
// ディープコピー(独立インスタンス)
|
||||
let items_guard = self.items.read().unwrap();
|
||||
let cloned_items: Vec<Box<dyn NyashBox>> = items_guard.iter()
|
||||
let cloned_items: Vec<Box<dyn NyashBox>> = items_guard
|
||||
.iter()
|
||||
.map(|item| {
|
||||
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||
if item.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some() {
|
||||
if item
|
||||
.as_any()
|
||||
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
|
||||
.is_some()
|
||||
{
|
||||
return item.share_box();
|
||||
}
|
||||
item.clone_box()
|
||||
}) // 要素もディープコピー(ハンドルは共有)
|
||||
}) // 要素もディープコピー(ハンドルは共有)
|
||||
.collect();
|
||||
|
||||
|
||||
ArrayBox {
|
||||
items: Arc::new(RwLock::new(cloned_items)), // 新しいArc
|
||||
items: Arc::new(RwLock::new(cloned_items)), // 新しいArc
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
@ -277,23 +306,24 @@ impl BoxCore for ArrayBox {
|
||||
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 {
|
||||
let items = self.items.read().unwrap();
|
||||
let strings: Vec<String> = items.iter()
|
||||
let strings: Vec<String> = items
|
||||
.iter()
|
||||
.map(|item| item.to_string_box().value)
|
||||
.collect();
|
||||
write!(f, "[{}]", strings.join(", "))
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
@ -306,49 +336,50 @@ impl Display for ArrayBox {
|
||||
}
|
||||
|
||||
impl NyashBox for ArrayBox {
|
||||
fn is_identity(&self) -> bool { true }
|
||||
fn is_identity(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 🎯 状態共有の核心実装
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
let new_instance = ArrayBox {
|
||||
items: Arc::clone(&self.items), // Arcクローンで状態共有
|
||||
base: BoxBase::new(), // 新しいID
|
||||
items: Arc::clone(&self.items), // Arcクローンで状態共有
|
||||
base: BoxBase::new(), // 新しいID
|
||||
};
|
||||
Box::new(new_instance)
|
||||
}
|
||||
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
let items = self.items.read().unwrap();
|
||||
let strings: Vec<String> = items.iter()
|
||||
let strings: Vec<String> = items
|
||||
.iter()
|
||||
.map(|item| item.to_string_box().value)
|
||||
.collect();
|
||||
StringBox::new(format!("[{}]", strings.join(", ")))
|
||||
}
|
||||
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
"ArrayBox"
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(other_array) = other.as_any().downcast_ref::<ArrayBox>() {
|
||||
let self_items = self.items.read().unwrap();
|
||||
let other_items = other_array.items.read().unwrap();
|
||||
|
||||
|
||||
if self_items.len() != other_items.len() {
|
||||
return BoolBox::new(false);
|
||||
}
|
||||
|
||||
|
||||
for (a, b) in self_items.iter().zip(other_items.iter()) {
|
||||
if !a.equals(b.as_ref()).value {
|
||||
return BoolBox::new(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BoolBox::new(true)
|
||||
} else {
|
||||
BoolBox::new(false)
|
||||
|
||||
@ -1,56 +1,56 @@
|
||||
/*!
|
||||
* AudioBox - 音声再生・合成Box
|
||||
*
|
||||
*
|
||||
* ## 📝 概要
|
||||
* Web Audio APIを使用してブラウザでの音声再生、
|
||||
* 合成、エフェクト処理を統一的に管理するBox。
|
||||
* ゲーム、音楽アプリ、オーディオビジュアライザー開発に最適。
|
||||
*
|
||||
*
|
||||
* ## 🛠️ 利用可能メソッド
|
||||
*
|
||||
*
|
||||
* ### 🔊 基本再生
|
||||
* - `loadAudio(url)` - 音声ファイル読み込み
|
||||
* - `play()` - 再生開始
|
||||
* - `pause()` - 一時停止
|
||||
* - `stop()` - 停止
|
||||
* - `setVolume(volume)` - 音量設定 (0.0-1.0)
|
||||
*
|
||||
*
|
||||
* ### 🎵 音声合成
|
||||
* - `createTone(frequency, duration)` - 純音生成
|
||||
* - `createNoise(type, duration)` - ノイズ生成
|
||||
* - `createBeep()` - システム音
|
||||
*
|
||||
*
|
||||
* ### 📊 解析・ビジュアライザー
|
||||
* - `getFrequencyData()` - 周波数解析データ取得
|
||||
* - `getWaveformData()` - 波形データ取得
|
||||
* - `getVolume()` - 現在の音量レベル
|
||||
*
|
||||
*
|
||||
* ### 🎛️ エフェクト
|
||||
* - `addReverb(room)` - リバーブエフェクト
|
||||
* - `addFilter(type, frequency)` - フィルター適用
|
||||
* - `addDistortion(amount)` - ディストーション
|
||||
*
|
||||
*
|
||||
* ## 💡 使用例
|
||||
* ```nyash
|
||||
* local audio, visualizer
|
||||
* audio = new AudioBox()
|
||||
*
|
||||
*
|
||||
* // 効果音再生
|
||||
* audio.loadAudio("sounds/explosion.wav")
|
||||
* audio.setVolume(0.7)
|
||||
* audio.play()
|
||||
*
|
||||
*
|
||||
* // 音声合成
|
||||
* audio.createTone(440, 1000) // A4音を1秒
|
||||
* audio.createBeep() // システム音
|
||||
*
|
||||
*
|
||||
* // オーディオビジュアライザー
|
||||
* local freqData = audio.getFrequencyData()
|
||||
* // freqDataを使用してcanvasに描画
|
||||
* ```
|
||||
*/
|
||||
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
|
||||
use std::any::Any;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
@ -58,8 +58,8 @@ use wasm_bindgen::prelude::*;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use web_sys::{
|
||||
AudioContext, AudioBuffer, AudioBufferSourceNode, GainNode,
|
||||
AnalyserNode, AudioDestinationNode, PeriodicWave, OscillatorNode
|
||||
AnalyserNode, AudioBuffer, AudioBufferSourceNode, AudioContext, AudioDestinationNode, GainNode,
|
||||
OscillatorNode, PeriodicWave,
|
||||
};
|
||||
|
||||
/// 音声管理Box
|
||||
@ -82,7 +82,7 @@ impl AudioBox {
|
||||
/// 音量を設定 (0.0 - 1.0)
|
||||
pub fn set_volume(&mut self, volume: f64) {
|
||||
self.volume = volume.max(0.0).min(1.0);
|
||||
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
if let Some(gain) = &self.gain_node {
|
||||
@ -104,21 +104,24 @@ impl AudioBox {
|
||||
if let Ok(gain) = context.create_gain() {
|
||||
// 周波数設定
|
||||
oscillator.frequency().set_value(frequency as f32);
|
||||
|
||||
|
||||
// 音量設定
|
||||
gain.gain().set_value(self.volume as f32);
|
||||
|
||||
|
||||
// ノード接続
|
||||
oscillator.connect_with_audio_node(&gain).unwrap_or_default();
|
||||
gain.connect_with_audio_node(&context.destination()).unwrap_or_default();
|
||||
|
||||
oscillator
|
||||
.connect_with_audio_node(&gain)
|
||||
.unwrap_or_default();
|
||||
gain.connect_with_audio_node(&context.destination())
|
||||
.unwrap_or_default();
|
||||
|
||||
// 再生
|
||||
let start_time = context.current_time();
|
||||
let end_time = start_time + duration_ms / 1000.0;
|
||||
|
||||
|
||||
oscillator.start_with_when(start_time).unwrap_or_default();
|
||||
oscillator.stop_with_when(end_time).unwrap_or_default();
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -138,7 +141,7 @@ impl AudioBox {
|
||||
if let Some(context) = &self.context {
|
||||
let sample_rate = context.sample_rate() as usize;
|
||||
let length = ((duration_ms / 1000.0) * sample_rate as f64) as u32;
|
||||
|
||||
|
||||
if let Ok(buffer) = context.create_buffer(1, length, sample_rate as f32) {
|
||||
if let Ok(channel_data) = buffer.get_channel_data(0) {
|
||||
// ホワイトノイズデータ生成
|
||||
@ -146,15 +149,16 @@ impl AudioBox {
|
||||
let noise = (js_sys::Math::random() - 0.5) * 2.0; // -1.0 to 1.0
|
||||
channel_data.set_index(i, noise as f32);
|
||||
}
|
||||
|
||||
|
||||
if let Ok(source) = context.create_buffer_source() {
|
||||
source.set_buffer(Some(&buffer));
|
||||
|
||||
|
||||
if let Ok(gain) = context.create_gain() {
|
||||
gain.gain().set_value(self.volume as f32);
|
||||
source.connect_with_audio_node(&gain).unwrap_or_default();
|
||||
gain.connect_with_audio_node(&context.destination()).unwrap_or_default();
|
||||
|
||||
gain.connect_with_audio_node(&context.destination())
|
||||
.unwrap_or_default();
|
||||
|
||||
source.start().unwrap_or_default();
|
||||
return true;
|
||||
}
|
||||
@ -171,7 +175,7 @@ impl AudioBox {
|
||||
if let Some(analyser) = &self.analyser_node {
|
||||
let buffer_length = analyser.frequency_bin_count() as usize;
|
||||
let mut data_array = vec![0u8; buffer_length];
|
||||
|
||||
|
||||
// 周波数データを取得
|
||||
analyser.get_byte_frequency_data(&mut data_array);
|
||||
return data_array;
|
||||
@ -185,7 +189,7 @@ impl AudioBox {
|
||||
if let Some(analyser) = &self.analyser_node {
|
||||
let buffer_length = analyser.fft_size() as usize;
|
||||
let mut data_array = vec![0u8; buffer_length];
|
||||
|
||||
|
||||
// 時間領域データを取得
|
||||
analyser.get_byte_time_domain_data(&mut data_array);
|
||||
return data_array;
|
||||
@ -201,7 +205,10 @@ impl AudioBox {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
/// Non-WASM環境用のダミー実装
|
||||
pub fn create_tone(&self, frequency: f64, duration: f64) -> bool {
|
||||
println!("AudioBox: Playing tone {}Hz for {}ms (simulated)", frequency, duration);
|
||||
println!(
|
||||
"AudioBox: Playing tone {}Hz for {}ms (simulated)",
|
||||
frequency, duration
|
||||
);
|
||||
true
|
||||
}
|
||||
|
||||
@ -220,13 +227,17 @@ impl AudioBox {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn get_frequency_data(&self) -> Vec<u8> {
|
||||
// シミュレーション用のダミーデータ
|
||||
(0..64).map(|i| ((i as f64 * 4.0).sin() * 128.0 + 128.0) as u8).collect()
|
||||
(0..64)
|
||||
.map(|i| ((i as f64 * 4.0).sin() * 128.0 + 128.0) as u8)
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn get_waveform_data(&self) -> Vec<u8> {
|
||||
// シミュレーション用のダミーデータ
|
||||
(0..128).map(|i| ((i as f64 * 0.1).sin() * 64.0 + 128.0) as u8).collect()
|
||||
(0..128)
|
||||
.map(|i| ((i as f64 * 0.1).sin() * 64.0 + 128.0) as u8)
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// オーディオコンテキストの状態を確認
|
||||
@ -260,19 +271,23 @@ impl BoxCore for AudioBox {
|
||||
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, "AudioBox(volume={:.2}, playing={})", self.volume, self.is_playing)
|
||||
write!(
|
||||
f,
|
||||
"AudioBox(volume={:.2}, playing={})",
|
||||
self.volume, self.is_playing
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
@ -282,20 +297,23 @@ impl NyashBox for AudioBox {
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new(format!("AudioBox(volume={:.2}, playing={})", self.volume, self.is_playing))
|
||||
StringBox::new(format!(
|
||||
"AudioBox(volume={:.2}, playing={})",
|
||||
self.volume, self.is_playing
|
||||
))
|
||||
}
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
"AudioBox"
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(other_audio) = other.as_any().downcast_ref::<AudioBox>() {
|
||||
BoolBox::new(self.base.id == other_audio.base.id)
|
||||
@ -309,4 +327,4 @@ impl std::fmt::Display for AudioBox {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.fmt_box(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,44 +1,44 @@
|
||||
/*! ✅ BoolBox - 真偽値Box
|
||||
*
|
||||
*
|
||||
* ## 📝 概要
|
||||
* true/false値を扱うためのBox。
|
||||
* JavaScript Boolean型のように直感的な論理演算が可能。
|
||||
*
|
||||
*
|
||||
* ## 🛠️ 利用可能メソッド
|
||||
* - `toString()` - 文字列変換 ("true" / "false")
|
||||
* - `not()` - 論理NOT (演算子: not)
|
||||
* - `and(other)` - 論理AND (演算子: and)
|
||||
* - `or(other)` - 論理OR (演算子: or)
|
||||
* - `equals(other)` - 等価比較 (演算子: ==)
|
||||
*
|
||||
*
|
||||
* ## 💡 使用例
|
||||
* ```nyash
|
||||
* local flag, result, text
|
||||
* flag = true
|
||||
*
|
||||
*
|
||||
* result = not flag // false
|
||||
* result = flag and true // true
|
||||
* result = flag or false // true
|
||||
* text = flag.toString() // "true"
|
||||
*
|
||||
*
|
||||
* // 条件分岐での利用
|
||||
* if (flag) {
|
||||
* print("Flag is true!")
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* ## 🔄 型変換
|
||||
* - 数値への変換: true → 1, false → 0
|
||||
* - 文字列への変換: "true" / "false"
|
||||
* - 空文字・null・0は false として扱われる
|
||||
*
|
||||
*
|
||||
* ## ⚡ 論理演算子実装済み
|
||||
* - `not condition` - NOT演算子
|
||||
* - `a and b` - AND演算子
|
||||
* - `a or b` - OR演算子
|
||||
*/
|
||||
|
||||
use crate::box_trait::{NyashBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoxBase, BoxCore, NyashBox};
|
||||
use std::any::Any;
|
||||
use std::fmt::Display;
|
||||
|
||||
@ -51,16 +51,16 @@ pub struct BoolBox {
|
||||
|
||||
impl BoolBox {
|
||||
pub fn new(value: bool) -> Self {
|
||||
Self {
|
||||
value,
|
||||
Self {
|
||||
value,
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn true_box() -> Self {
|
||||
Self::new(true)
|
||||
}
|
||||
|
||||
|
||||
pub fn false_box() -> Self {
|
||||
Self::new(false)
|
||||
}
|
||||
@ -70,7 +70,7 @@ impl NyashBox for BoolBox {
|
||||
fn to_string_box(&self) -> crate::box_trait::StringBox {
|
||||
crate::box_trait::StringBox::new(if self.value { "true" } else { "false" })
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> crate::box_trait::BoolBox {
|
||||
if let Some(other_bool) = other.as_any().downcast_ref::<BoolBox>() {
|
||||
crate::box_trait::BoolBox::new(self.value == other_bool.value)
|
||||
@ -78,16 +78,15 @@ impl NyashBox for BoolBox {
|
||||
crate::box_trait::BoolBox::new(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
"BoolBox"
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
@ -98,19 +97,19 @@ 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
|
||||
}
|
||||
@ -120,4 +119,4 @@ impl Display for BoolBox {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.fmt_box(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
/*! 📊 BufferBox - バイナリデータ処理Box
|
||||
*
|
||||
*
|
||||
* ## 📝 概要
|
||||
* バイナリデータの読み書きを扱うBox。
|
||||
* ファイル操作、ネットワーク通信、画像処理などで使用。
|
||||
*
|
||||
*
|
||||
* ## 🛠️ 利用可能メソッド
|
||||
* - `write(data)` - バイトデータ書き込み
|
||||
* - `read(count)` - 指定バイト数読み取り
|
||||
@ -12,37 +12,37 @@
|
||||
* - `length()` - データサイズ取得
|
||||
* - `append(buffer)` - 他のBufferを追加
|
||||
* - `slice(start, end)` - 部分データ取得
|
||||
*
|
||||
*
|
||||
* ## 💡 使用例
|
||||
* ```nyash
|
||||
* local buffer
|
||||
* buffer = new BufferBox()
|
||||
*
|
||||
*
|
||||
* // データ書き込み
|
||||
* buffer.write([72, 101, 108, 108, 111]) // "Hello"
|
||||
* print("Size: " + buffer.length())
|
||||
*
|
||||
*
|
||||
* // データ読み取り
|
||||
* local data
|
||||
* data = buffer.readAll()
|
||||
* ```
|
||||
*/
|
||||
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, IntegerBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox};
|
||||
use crate::boxes::array::ArrayBox;
|
||||
use std::any::Any;
|
||||
use std::sync::{Arc, RwLock}; // Arc追加
|
||||
use std::fmt::Display;
|
||||
use std::sync::{Arc, RwLock}; // Arc追加
|
||||
|
||||
pub struct BufferBox {
|
||||
data: Arc<RwLock<Vec<u8>>>, // Arc追加
|
||||
data: Arc<RwLock<Vec<u8>>>, // Arc追加
|
||||
base: BoxBase,
|
||||
}
|
||||
|
||||
impl BufferBox {
|
||||
pub fn new() -> Self {
|
||||
BufferBox {
|
||||
data: Arc::new(RwLock::new(Vec::new())), // Arc::new追加
|
||||
BufferBox {
|
||||
data: Arc::new(RwLock::new(Vec::new())), // Arc::new追加
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
@ -56,18 +56,21 @@ impl BufferBox {
|
||||
pub fn len(&self) -> usize {
|
||||
self.data.read().unwrap().len()
|
||||
}
|
||||
|
||||
|
||||
pub fn from_vec(data: Vec<u8>) -> Self {
|
||||
BufferBox {
|
||||
data: Arc::new(RwLock::new(data)), // Arc::new追加
|
||||
BufferBox {
|
||||
data: Arc::new(RwLock::new(data)), // Arc::new追加
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// データを書き込む
|
||||
pub fn write(&self, data: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
// ArrayBoxから変換 - use crate::boxes::array::ArrayBox directly
|
||||
if let Some(array_box) = data.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
|
||||
if let Some(array_box) = data
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::array::ArrayBox>()
|
||||
{
|
||||
let mut buffer = self.data.write().unwrap();
|
||||
let items = array_box.items.read().unwrap();
|
||||
for item in items.iter() {
|
||||
@ -80,10 +83,13 @@ impl BufferBox {
|
||||
Box::new(IntegerBox::new(buffer.len() as i64))
|
||||
} else {
|
||||
let type_name = data.type_name();
|
||||
Box::new(StringBox::new(&format!("Error: write() requires ArrayBox of integers, got {}", type_name)))
|
||||
Box::new(StringBox::new(&format!(
|
||||
"Error: write() requires ArrayBox of integers, got {}",
|
||||
type_name
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// すべてのデータを読み取る
|
||||
pub fn readAll(&self) -> Box<dyn NyashBox> {
|
||||
let buffer = self.data.read().unwrap();
|
||||
@ -93,14 +99,14 @@ impl BufferBox {
|
||||
}
|
||||
Box::new(array)
|
||||
}
|
||||
|
||||
|
||||
/// 指定バイト数読み取る
|
||||
pub fn read(&self, count: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(count_int) = count.as_any().downcast_ref::<IntegerBox>() {
|
||||
let mut buffer = self.data.write().unwrap();
|
||||
let count = count_int.value.min(buffer.len() as i64) as usize;
|
||||
let array = ArrayBox::new();
|
||||
|
||||
|
||||
// 先頭からcount個取り出す
|
||||
let bytes: Vec<u8> = buffer.drain(0..count).collect();
|
||||
for byte in bytes {
|
||||
@ -111,18 +117,18 @@ impl BufferBox {
|
||||
Box::new(StringBox::new("Error: read() requires integer count"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// バッファをクリア
|
||||
pub fn clear(&self) -> Box<dyn NyashBox> {
|
||||
self.data.write().unwrap().clear();
|
||||
Box::new(StringBox::new("ok"))
|
||||
}
|
||||
|
||||
|
||||
/// データサイズを取得
|
||||
pub fn length(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(IntegerBox::new(self.data.read().unwrap().len() as i64))
|
||||
}
|
||||
|
||||
|
||||
/// 他のBufferBoxを追加
|
||||
pub fn append(&self, other: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(other_buffer) = other.as_any().downcast_ref::<BufferBox>() {
|
||||
@ -134,17 +140,17 @@ impl BufferBox {
|
||||
Box::new(StringBox::new("Error: append() requires BufferBox"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 部分データ取得
|
||||
pub fn slice(&self, start: Box<dyn NyashBox>, end: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let (Some(start_int), Some(end_int)) = (
|
||||
start.as_any().downcast_ref::<IntegerBox>(),
|
||||
end.as_any().downcast_ref::<IntegerBox>()
|
||||
end.as_any().downcast_ref::<IntegerBox>(),
|
||||
) {
|
||||
let data = self.data.read().unwrap();
|
||||
let start = (start_int.value as usize).min(data.len());
|
||||
let end = (end_int.value as usize).min(data.len());
|
||||
|
||||
|
||||
if start <= end {
|
||||
let slice_data = data[start..end].to_vec();
|
||||
Box::new(BufferBox::from_vec(slice_data))
|
||||
@ -155,7 +161,7 @@ impl BufferBox {
|
||||
Box::new(StringBox::new("Error: slice() requires integer indices"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// ⭐ Phase 10: Zero-copy detection - check if buffer is shared with another buffer
|
||||
pub fn is_shared_with(&self, other: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(other_buffer) = other.as_any().downcast_ref::<BufferBox>() {
|
||||
@ -167,17 +173,17 @@ impl BufferBox {
|
||||
Box::new(BoolBox::new(false))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// ⭐ Phase 10: Share reference - create a zero-copy shared reference to this buffer's data
|
||||
pub fn share_reference(&self, _data: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
// Create a new BufferBox that shares the same Arc as this buffer
|
||||
let shared_buffer = BufferBox {
|
||||
data: Arc::clone(&self.data), // Share THIS buffer's data
|
||||
base: BoxBase::new(), // New ID but shared data
|
||||
base: BoxBase::new(), // New ID but shared data
|
||||
};
|
||||
Box::new(shared_buffer)
|
||||
}
|
||||
|
||||
|
||||
/// ⭐ Phase 10: Memory footprint - get current memory usage in bytes
|
||||
pub fn memory_footprint(&self) -> Box<dyn NyashBox> {
|
||||
let data = self.data.read().unwrap();
|
||||
@ -192,7 +198,7 @@ impl Clone for BufferBox {
|
||||
// ディープコピー(独立インスタンス)
|
||||
let data_guard = self.data.read().unwrap();
|
||||
BufferBox {
|
||||
data: Arc::new(RwLock::new(data_guard.clone())), // 新しいArc
|
||||
data: Arc::new(RwLock::new(data_guard.clone())), // 新しいArc
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
@ -202,20 +208,20 @@ impl BoxCore for BufferBox {
|
||||
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 {
|
||||
let data = self.data.read().unwrap();
|
||||
write!(f, "BufferBox({} bytes)", data.len())
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
@ -231,12 +237,12 @@ impl NyashBox for BufferBox {
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
/// 🎯 状态共享的核心实现
|
||||
|
||||
/// 🎯 状态共享的核心实现
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
let new_instance = BufferBox {
|
||||
data: Arc::clone(&self.data), // Arcクローンで状態共有
|
||||
base: BoxBase::new(), // 新しいID
|
||||
data: Arc::clone(&self.data), // Arcクローンで状態共有
|
||||
base: BoxBase::new(), // 新しいID
|
||||
};
|
||||
Box::new(new_instance)
|
||||
}
|
||||
@ -246,12 +252,10 @@ impl NyashBox for BufferBox {
|
||||
StringBox::new(format!("BufferBox({} bytes)", data.len()))
|
||||
}
|
||||
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
"BufferBox"
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(other_buffer) = other.as_any().downcast_ref::<BufferBox>() {
|
||||
// RwLock内容を比較
|
||||
@ -264,7 +268,7 @@ impl NyashBox for BufferBox {
|
||||
}
|
||||
}
|
||||
|
||||
// Debug implementation for BufferBox
|
||||
// Debug implementation for BufferBox
|
||||
impl std::fmt::Debug for BufferBox {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let data = self.data.read().unwrap();
|
||||
|
||||
@ -1,45 +1,45 @@
|
||||
/*!
|
||||
* CanvasEventBox - Canvas入力イベント管理Box
|
||||
*
|
||||
*
|
||||
* ## 📝 概要
|
||||
* HTML5 Canvasでのマウス・タッチ・キーボードイベントを
|
||||
* Nyashから利用可能にするBox。ゲーム開発、インタラクティブ
|
||||
* アプリケーション開発に必須の入力機能を提供。
|
||||
*
|
||||
*
|
||||
* ## 🛠️ 利用可能メソッド
|
||||
*
|
||||
*
|
||||
* ### 🖱️ マウスイベント
|
||||
* - `onMouseDown(callback)` - マウスボタン押下
|
||||
* - `onMouseUp(callback)` - マウスボタン離上
|
||||
* - `onMouseMove(callback)` - マウス移動
|
||||
* - `onMouseClick(callback)` - マウスクリック
|
||||
* - `onMouseWheel(callback)` - マウスホイール
|
||||
*
|
||||
*
|
||||
* ### 👆 タッチイベント
|
||||
* - `onTouchStart(callback)` - タッチ開始
|
||||
* - `onTouchMove(callback)` - タッチ移動
|
||||
* - `onTouchEnd(callback)` - タッチ終了
|
||||
*
|
||||
*
|
||||
* ### ⌨️ キーボードイベント
|
||||
* - `onKeyDown(callback)` - キー押下
|
||||
* - `onKeyUp(callback)` - キー離上
|
||||
*
|
||||
*
|
||||
* ### 📊 座標取得
|
||||
* - `getMouseX()` - 現在のマウスX座標
|
||||
* - `getMouseY()` - 現在のマウスY座標
|
||||
* - `isPressed(button)` - ボタン押下状態確認
|
||||
*
|
||||
*
|
||||
* ## 💡 使用例
|
||||
* ```nyash
|
||||
* local events, canvas
|
||||
* events = new CanvasEventBox("game-canvas")
|
||||
* canvas = new WebCanvasBox("game-canvas", 800, 600)
|
||||
*
|
||||
*
|
||||
* // マウスクリックで円を描画
|
||||
* events.onMouseClick(function(x, y) {
|
||||
* canvas.fillCircle(x, y, 10, "red")
|
||||
* })
|
||||
*
|
||||
*
|
||||
* // ドラッグで線を描画
|
||||
* local isDrawing = false
|
||||
* events.onMouseDown(function(x, y) {
|
||||
@ -47,31 +47,28 @@
|
||||
* canvas.beginPath()
|
||||
* canvas.moveTo(x, y)
|
||||
* })
|
||||
*
|
||||
*
|
||||
* events.onMouseMove(function(x, y) {
|
||||
* if (isDrawing) {
|
||||
* canvas.lineTo(x, y)
|
||||
* canvas.stroke("black", 2)
|
||||
* }
|
||||
* })
|
||||
*
|
||||
*
|
||||
* events.onMouseUp(function() {
|
||||
* isDrawing = false
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
|
||||
use std::any::Any;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use web_sys::{
|
||||
HtmlCanvasElement, MouseEvent, TouchEvent, KeyboardEvent,
|
||||
EventTarget, Element
|
||||
};
|
||||
use web_sys::{Element, EventTarget, HtmlCanvasElement, KeyboardEvent, MouseEvent, TouchEvent};
|
||||
|
||||
/// Canvas入力イベント管理Box
|
||||
#[derive(Debug, Clone)]
|
||||
@ -140,7 +137,8 @@ impl CanvasEventBox {
|
||||
callback.call0(&JsValue::NULL).unwrap_or_default();
|
||||
}) as Box<dyn FnMut(MouseEvent)>);
|
||||
|
||||
canvas.add_event_listener_with_callback("mousedown", closure.as_ref().unchecked_ref())
|
||||
canvas
|
||||
.add_event_listener_with_callback("mousedown", closure.as_ref().unchecked_ref())
|
||||
.unwrap_or_default();
|
||||
closure.forget(); // メモリリークを防ぐため通常は適切な管理が必要
|
||||
}
|
||||
@ -154,7 +152,8 @@ impl CanvasEventBox {
|
||||
callback.call0(&JsValue::NULL).unwrap_or_default();
|
||||
}) as Box<dyn FnMut(MouseEvent)>);
|
||||
|
||||
canvas.add_event_listener_with_callback("mouseup", closure.as_ref().unchecked_ref())
|
||||
canvas
|
||||
.add_event_listener_with_callback("mouseup", closure.as_ref().unchecked_ref())
|
||||
.unwrap_or_default();
|
||||
closure.forget();
|
||||
}
|
||||
@ -168,7 +167,8 @@ impl CanvasEventBox {
|
||||
callback.call0(&JsValue::NULL).unwrap_or_default();
|
||||
}) as Box<dyn FnMut(MouseEvent)>);
|
||||
|
||||
canvas.add_event_listener_with_callback("mousemove", closure.as_ref().unchecked_ref())
|
||||
canvas
|
||||
.add_event_listener_with_callback("mousemove", closure.as_ref().unchecked_ref())
|
||||
.unwrap_or_default();
|
||||
closure.forget();
|
||||
}
|
||||
@ -182,7 +182,8 @@ impl CanvasEventBox {
|
||||
callback.call0(&JsValue::NULL).unwrap_or_default();
|
||||
}) as Box<dyn FnMut(MouseEvent)>);
|
||||
|
||||
canvas.add_event_listener_with_callback("click", closure.as_ref().unchecked_ref())
|
||||
canvas
|
||||
.add_event_listener_with_callback("click", closure.as_ref().unchecked_ref())
|
||||
.unwrap_or_default();
|
||||
closure.forget();
|
||||
}
|
||||
@ -196,7 +197,8 @@ impl CanvasEventBox {
|
||||
callback.call0(&JsValue::NULL).unwrap_or_default();
|
||||
}) as Box<dyn FnMut(TouchEvent)>);
|
||||
|
||||
canvas.add_event_listener_with_callback("touchstart", closure.as_ref().unchecked_ref())
|
||||
canvas
|
||||
.add_event_listener_with_callback("touchstart", closure.as_ref().unchecked_ref())
|
||||
.unwrap_or_default();
|
||||
closure.forget();
|
||||
}
|
||||
@ -210,7 +212,8 @@ impl CanvasEventBox {
|
||||
callback.call0(&JsValue::NULL).unwrap_or_default();
|
||||
}) as Box<dyn FnMut(KeyboardEvent)>);
|
||||
|
||||
window.add_event_listener_with_callback("keydown", closure.as_ref().unchecked_ref())
|
||||
window
|
||||
.add_event_listener_with_callback("keydown", closure.as_ref().unchecked_ref())
|
||||
.unwrap_or_default();
|
||||
closure.forget();
|
||||
}
|
||||
@ -252,19 +255,19 @@ impl BoxCore for CanvasEventBox {
|
||||
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, "CanvasEventBox({})", self.canvas_id)
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
@ -274,7 +277,7 @@ impl NyashBox for CanvasEventBox {
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
@ -287,7 +290,7 @@ impl NyashBox for CanvasEventBox {
|
||||
fn type_name(&self) -> &'static str {
|
||||
"CanvasEventBox"
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(other_events) = other.as_any().downcast_ref::<CanvasEventBox>() {
|
||||
BoolBox::new(self.base.id == other_events.base.id)
|
||||
@ -301,4 +304,4 @@ impl std::fmt::Display for CanvasEventBox {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.fmt_box(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,29 +1,29 @@
|
||||
/*!
|
||||
* CanvasLoopBox - アニメーションループ管理Box
|
||||
*
|
||||
*
|
||||
* ## 📝 概要
|
||||
* ゲームや動的コンテンツのためのアニメーションループを
|
||||
* 管理するBox。requestAnimationFrame、フレームレート制御、
|
||||
* ループ状態管理を統一的に提供。
|
||||
*
|
||||
*
|
||||
* ## 🛠️ 利用可能メソッド
|
||||
*
|
||||
*
|
||||
* ### 🎮 ループ制御
|
||||
* - `start(callback)` - アニメーションループ開始
|
||||
* - `stop()` - アニメーションループ停止
|
||||
* - `pause()` - アニメーションループ一時停止
|
||||
* - `resume()` - アニメーションループ再開
|
||||
*
|
||||
*
|
||||
* ### 📊 フレーム情報
|
||||
* - `getFPS()` - 現在のFPS取得
|
||||
* - `getFrameCount()` - 総フレーム数取得
|
||||
* - `getDeltaTime()` - 前フレームからの経過時間
|
||||
* - `setTargetFPS(fps)` - 目標FPS設定
|
||||
*
|
||||
*
|
||||
* ### ⏱️ 時間管理
|
||||
* - `getElapsedTime()` - ループ開始からの経過時間
|
||||
* - `reset()` - タイマーリセット
|
||||
*
|
||||
*
|
||||
* ## 💡 使用例
|
||||
* ```nyash
|
||||
* local loop, canvas, ball_x, ball_y
|
||||
@ -31,7 +31,7 @@
|
||||
* canvas = new WebCanvasBox("game-canvas", 800, 600)
|
||||
* ball_x = 400
|
||||
* ball_y = 300
|
||||
*
|
||||
*
|
||||
* // ゲームループ
|
||||
* loop.start(function(deltaTime) {
|
||||
* // 更新処理
|
||||
@ -47,7 +47,7 @@
|
||||
* ```
|
||||
*/
|
||||
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
|
||||
use crate::boxes::TimerBox;
|
||||
use std::any::Any;
|
||||
|
||||
@ -75,7 +75,7 @@ impl CanvasLoopBox {
|
||||
pub fn new() -> Self {
|
||||
let timer = TimerBox::new();
|
||||
let current_time = timer.now();
|
||||
|
||||
|
||||
Self {
|
||||
base: BoxBase::new(),
|
||||
is_running: false,
|
||||
@ -108,12 +108,16 @@ impl CanvasLoopBox {
|
||||
// アニメーションフレーム用のクロージャを作成
|
||||
let closure = Closure::wrap(Box::new(move |time: f64| {
|
||||
// ここでフレーム処理を実行
|
||||
callback.call1(&JsValue::NULL, &JsValue::from_f64(time)).unwrap_or_default();
|
||||
callback
|
||||
.call1(&JsValue::NULL, &JsValue::from_f64(time))
|
||||
.unwrap_or_default();
|
||||
}) as Box<dyn FnMut(f64)>);
|
||||
|
||||
let id = self.timer.request_animation_frame(closure.as_ref().unchecked_ref());
|
||||
let id = self
|
||||
.timer
|
||||
.request_animation_frame(closure.as_ref().unchecked_ref());
|
||||
self.animation_id = Some(id);
|
||||
|
||||
|
||||
closure.forget(); // クロージャの所有権を手放す
|
||||
}
|
||||
|
||||
@ -147,9 +151,9 @@ impl CanvasLoopBox {
|
||||
if !self.is_running || self.is_paused {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
self.is_paused = true;
|
||||
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
if let Some(id) = self.animation_id {
|
||||
@ -170,12 +174,16 @@ impl CanvasLoopBox {
|
||||
self.last_frame_time = self.timer.now(); // 時間をリセット
|
||||
|
||||
let closure = Closure::wrap(Box::new(move |time: f64| {
|
||||
callback.call1(&JsValue::NULL, &JsValue::from_f64(time)).unwrap_or_default();
|
||||
callback
|
||||
.call1(&JsValue::NULL, &JsValue::from_f64(time))
|
||||
.unwrap_or_default();
|
||||
}) as Box<dyn FnMut(f64)>);
|
||||
|
||||
let id = self.timer.request_animation_frame(closure.as_ref().unchecked_ref());
|
||||
let id = self
|
||||
.timer
|
||||
.request_animation_frame(closure.as_ref().unchecked_ref());
|
||||
self.animation_id = Some(id);
|
||||
|
||||
|
||||
closure.forget();
|
||||
}
|
||||
|
||||
@ -262,19 +270,23 @@ impl BoxCore for CanvasLoopBox {
|
||||
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, "CanvasLoopBox(running={}, fps={:.1})", self.is_running, self.fps)
|
||||
write!(
|
||||
f,
|
||||
"CanvasLoopBox(running={}, fps={:.1})",
|
||||
self.is_running, self.fps
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
@ -284,20 +296,23 @@ impl NyashBox for CanvasLoopBox {
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new(format!("CanvasLoopBox(running={}, fps={:.1})", self.is_running, self.fps))
|
||||
StringBox::new(format!(
|
||||
"CanvasLoopBox(running={}, fps={:.1})",
|
||||
self.is_running, self.fps
|
||||
))
|
||||
}
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
"CanvasLoopBox"
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(other_loop) = other.as_any().downcast_ref::<CanvasLoopBox>() {
|
||||
BoolBox::new(self.base.id == other_loop.base.id)
|
||||
@ -311,4 +326,4 @@ impl std::fmt::Display for CanvasLoopBox {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.fmt_box(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,35 +1,35 @@
|
||||
/*! 📟 ConsoleBox - コンソール出力Box
|
||||
*
|
||||
*
|
||||
* ## 📝 概要
|
||||
* Webブラウザのコンソール機能を統合したBox。
|
||||
* WASM環境ではブラウザコンソール、ネイティブ環境では標準出力。
|
||||
*
|
||||
*
|
||||
* ## 🛠️ 利用可能メソッド
|
||||
* - `log(message)` - 通常のメッセージ出力
|
||||
* - `warn(message)` - 警告メッセージ出力
|
||||
* - `error(message)` - エラーメッセージ出力
|
||||
* - `clear()` - コンソール画面クリア
|
||||
*
|
||||
*
|
||||
* ## 💡 使用例
|
||||
* ```nyash
|
||||
* local console
|
||||
* console = new ConsoleBox()
|
||||
*
|
||||
*
|
||||
* console.log("Hello, Nyash!") // 通常ログ
|
||||
* console.warn("This is a warning") // 警告
|
||||
* console.error("Something went wrong") // エラー
|
||||
* console.clear() // クリア
|
||||
*
|
||||
*
|
||||
* // デバッグ用途
|
||||
* local value
|
||||
* value = 42
|
||||
* console.log("Debug: value = " + value.toString())
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* ## 🌐 環境別動作
|
||||
* - **WASM環境**: ブラウザの開発者ツールコンソールに出力
|
||||
* - **ネイティブ環境**: ターミナル標準出力にプレフィックス付きで出力
|
||||
*
|
||||
*
|
||||
* ## 🔍 デバッグ活用
|
||||
* ```nyash
|
||||
* // エラーハンドリング
|
||||
@ -37,7 +37,7 @@
|
||||
* console.error("Critical error occurred!")
|
||||
* return null
|
||||
* }
|
||||
*
|
||||
*
|
||||
* // 実行トレース
|
||||
* console.log("Function start")
|
||||
* // 処理...
|
||||
@ -45,7 +45,7 @@
|
||||
* ```
|
||||
*/
|
||||
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
|
||||
use std::any::Any;
|
||||
use std::fmt::Display;
|
||||
|
||||
@ -59,24 +59,26 @@ pub struct ConsoleBox {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
impl ConsoleBox {
|
||||
pub fn new() -> Self {
|
||||
Self { base: BoxBase::new() }
|
||||
Self {
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Log messages to browser console
|
||||
pub fn log(&self, message: &str) {
|
||||
web_sys::console::log_1(&message.into());
|
||||
}
|
||||
|
||||
|
||||
/// Log warning to browser console
|
||||
pub fn warn(&self, message: &str) {
|
||||
web_sys::console::warn_1(&message.into());
|
||||
}
|
||||
|
||||
|
||||
/// Log error to browser console
|
||||
pub fn error(&self, message: &str) {
|
||||
web_sys::console::error_1(&message.into());
|
||||
}
|
||||
|
||||
|
||||
/// Clear browser console
|
||||
pub fn clear(&self) {
|
||||
web_sys::console::clear();
|
||||
@ -88,19 +90,19 @@ impl BoxCore for ConsoleBox {
|
||||
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, "[ConsoleBox - Browser Console Interface]")
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
@ -111,27 +113,26 @@ impl NyashBox for ConsoleBox {
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new("[ConsoleBox - Browser Console Interface]")
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
BoolBox::new(other.as_any().is::<ConsoleBox>())
|
||||
}
|
||||
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
"ConsoleBox"
|
||||
}
|
||||
|
||||
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Non-WASM版 - モックアップ実装
|
||||
// Non-WASM版 - モックアップ実装
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ConsoleBox {
|
||||
@ -141,22 +142,24 @@ pub struct ConsoleBox {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
impl ConsoleBox {
|
||||
pub fn new() -> Self {
|
||||
Self { base: BoxBase::new() }
|
||||
Self {
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Mock log method for non-WASM environments
|
||||
pub fn log(&self, message: &str) {
|
||||
println!("[Console LOG] {}", message);
|
||||
}
|
||||
|
||||
|
||||
pub fn warn(&self, message: &str) {
|
||||
println!("[Console WARN] {}", message);
|
||||
}
|
||||
|
||||
|
||||
pub fn error(&self, message: &str) {
|
||||
println!("[Console ERROR] {}", message);
|
||||
}
|
||||
|
||||
|
||||
pub fn clear(&self) {
|
||||
println!("[Console CLEAR]");
|
||||
}
|
||||
@ -167,19 +170,19 @@ impl BoxCore for ConsoleBox {
|
||||
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, "[ConsoleBox - Mock Implementation]")
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
@ -190,27 +193,25 @@ impl NyashBox for ConsoleBox {
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new("[ConsoleBox - Mock Implementation]")
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
BoolBox::new(other.as_any().is::<ConsoleBox>())
|
||||
}
|
||||
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
"ConsoleBox"
|
||||
}
|
||||
|
||||
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Display implementations for both WASM and non-WASM versions
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
impl Display for ConsoleBox {
|
||||
|
||||
@ -1,57 +1,57 @@
|
||||
/*! 🔍 DebugBox - デバッグ支援Box
|
||||
*
|
||||
*
|
||||
* ## 📝 概要
|
||||
* プロフェッショナル開発向けデバッグ機能を提供するBox。
|
||||
* メモリ使用量監視、実行トレース、ブレークポイントなど
|
||||
* 高度なデバッグ機能を完備。
|
||||
*
|
||||
*
|
||||
* ## 🛠️ 利用可能メソッド
|
||||
*
|
||||
*
|
||||
* ### 🎯 基本デバッグ
|
||||
* - `startTracking()` - デバッグ追跡開始
|
||||
* - `stopTracking()` - デバッグ追跡停止
|
||||
* - `trackBox(box, name)` - 特定Boxを追跡対象に追加
|
||||
* - `watch(box, name)` - リアルタイム監視
|
||||
* - `clear()` - 全デバッグ情報クリア
|
||||
*
|
||||
*
|
||||
* ### 📊 レポート・分析
|
||||
* - `dumpAll()` - 全追跡データダンプ
|
||||
* - `memoryReport()` - メモリ使用量レポート
|
||||
* - `showCallStack()` - 関数呼び出しスタック表示
|
||||
* - `saveToFile(filename)` - デバッグ情報をファイル保存
|
||||
*
|
||||
*
|
||||
* ### 🎮 高度機能
|
||||
* - `setBreakpoint(function)` - ブレークポイント設定
|
||||
* - `traceCall(function, args)` - 関数呼び出しトレース
|
||||
* - `isTracking()` - 追跡状態確認
|
||||
* - `getTrackedCount()` - 追跡中Box数取得
|
||||
*
|
||||
*
|
||||
* ## 💡 使用例
|
||||
* ```nyash
|
||||
* local debug, user, product
|
||||
* debug = new DebugBox()
|
||||
*
|
||||
*
|
||||
* // デバッグ開始
|
||||
* debug.startTracking()
|
||||
*
|
||||
*
|
||||
* // オブジェクトを追跡
|
||||
* user = new User("Alice", 25)
|
||||
* debug.trackBox(user, "user_alice")
|
||||
*
|
||||
*
|
||||
* product = new Product("Book", 1500)
|
||||
* debug.trackBox(product, "book_product")
|
||||
*
|
||||
*
|
||||
* // リアルタイム監視
|
||||
* debug.watch(user.age, "user_age")
|
||||
*
|
||||
*
|
||||
* // レポート生成
|
||||
* print(debug.memoryReport())
|
||||
* print(debug.dumpAll())
|
||||
*
|
||||
*
|
||||
* // ファイルに保存
|
||||
* debug.saveToFile("debug_report.txt")
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* ## 🎮 実用例 - パフォーマンス診断
|
||||
* ```nyash
|
||||
* static box PerformanceTest {
|
||||
@ -76,13 +76,13 @@
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* ## ⚡ ベストプラクティス
|
||||
* ```nyash
|
||||
* // エラーハンドリング付きデバッグ
|
||||
* local debug
|
||||
* debug = new DebugBox()
|
||||
*
|
||||
*
|
||||
* try {
|
||||
* debug.startTracking()
|
||||
* // 問題のあるコード
|
||||
@ -92,20 +92,20 @@
|
||||
* print("Debug info saved to error_dump.txt")
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* ## ⚠️ 注意
|
||||
* - 本格運用時はtrackingを無効にしてパフォーマンス向上
|
||||
* - 大量データ追跡時はメモリ消費に注意
|
||||
* - call stackは直近100件まで自動保持
|
||||
*/
|
||||
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox, VoidBox};
|
||||
use crate::instance_v2::InstanceBox;
|
||||
use crate::interpreter::RuntimeError;
|
||||
use chrono::Local;
|
||||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::RwLock;
|
||||
use chrono::Local;
|
||||
use crate::box_trait::{BoxCore, BoxBase, NyashBox, StringBox, BoolBox, VoidBox};
|
||||
use crate::interpreter::RuntimeError;
|
||||
use crate::instance_v2::InstanceBox;
|
||||
use std::any::Any;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DebugBox {
|
||||
@ -156,23 +156,27 @@ impl DebugBox {
|
||||
Ok(Box::new(VoidBox::new()))
|
||||
}
|
||||
|
||||
pub fn track_box(&self, box_value: &dyn NyashBox, name: &str) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
pub fn track_box(
|
||||
&self,
|
||||
box_value: &dyn NyashBox,
|
||||
name: &str,
|
||||
) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
let enabled = self.tracking_enabled.read().unwrap();
|
||||
if !*enabled {
|
||||
return Ok(Box::new(VoidBox::new()));
|
||||
}
|
||||
|
||||
let mut tracked = self.tracked_boxes.write().unwrap();
|
||||
|
||||
|
||||
let info = TrackedBoxInfo {
|
||||
box_type: box_value.type_name().to_string(),
|
||||
created_at: Local::now().format("%Y-%m-%d %H:%M:%S").to_string(),
|
||||
fields: self.get_box_fields(box_value),
|
||||
value_repr: box_value.to_string_box().value,
|
||||
};
|
||||
|
||||
|
||||
tracked.insert(name.to_string(), info);
|
||||
|
||||
|
||||
Ok(Box::new(VoidBox::new()))
|
||||
}
|
||||
|
||||
@ -192,9 +196,12 @@ impl DebugBox {
|
||||
pub fn dump_all(&self) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
let tracked = self.tracked_boxes.read().unwrap();
|
||||
let mut output = String::from("=== Box State Dump ===\n");
|
||||
output.push_str(&format!("Time: {}\n", Local::now().format("%Y-%m-%d %H:%M:%S")));
|
||||
output.push_str(&format!(
|
||||
"Time: {}\n",
|
||||
Local::now().format("%Y-%m-%d %H:%M:%S")
|
||||
));
|
||||
output.push_str(&format!("Total tracked boxes: {}\n\n", tracked.len()));
|
||||
|
||||
|
||||
for (name, info) in tracked.iter() {
|
||||
output.push_str(&format!("Box: {}\n", name));
|
||||
output.push_str(&format!(" Type: {}\n", info.box_type));
|
||||
@ -203,28 +210,31 @@ impl DebugBox {
|
||||
output.push_str(&format!(" Value: {}\n", info.value_repr));
|
||||
output.push_str("\n");
|
||||
}
|
||||
|
||||
|
||||
Ok(Box::new(StringBox::new(output)))
|
||||
}
|
||||
|
||||
pub fn save_to_file(&self, filename: &str) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
let dump_result = self.dump_all()?;
|
||||
let content = dump_result.to_string_box().value;
|
||||
|
||||
|
||||
// Write to file using std::fs
|
||||
std::fs::write(filename, content)
|
||||
.map_err(|e| RuntimeError::InvalidOperation {
|
||||
message: format!("Failed to write debug file: {}", e),
|
||||
})?;
|
||||
|
||||
std::fs::write(filename, content).map_err(|e| RuntimeError::InvalidOperation {
|
||||
message: format!("Failed to write debug file: {}", e),
|
||||
})?;
|
||||
|
||||
println!("[DEBUG] Saved debug info to {}", filename);
|
||||
Ok(Box::new(VoidBox::new()))
|
||||
}
|
||||
|
||||
pub fn watch(&self, box_value: &dyn NyashBox, name: &str) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
pub fn watch(
|
||||
&self,
|
||||
box_value: &dyn NyashBox,
|
||||
name: &str,
|
||||
) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
let value_str = box_value.to_string_box().value;
|
||||
let type_name = box_value.type_name();
|
||||
|
||||
|
||||
println!("[DEBUG] Watching {} ({}): {}", name, type_name, value_str);
|
||||
Ok(Box::new(VoidBox::new()))
|
||||
}
|
||||
@ -233,18 +243,18 @@ impl DebugBox {
|
||||
let tracked = self.tracked_boxes.read().unwrap();
|
||||
let mut report = String::from("=== Memory Report ===\n");
|
||||
report.push_str(&format!("Tracked boxes: {}\n", tracked.len()));
|
||||
|
||||
|
||||
// Count by type
|
||||
let mut type_counts: HashMap<String, usize> = HashMap::new();
|
||||
for info in tracked.values() {
|
||||
*type_counts.entry(info.box_type.clone()).or_insert(0) += 1;
|
||||
}
|
||||
|
||||
|
||||
report.push_str("\nBoxes by type:\n");
|
||||
for (box_type, count) in type_counts.iter() {
|
||||
report.push_str(&format!(" {}: {}\n", box_type, count));
|
||||
}
|
||||
|
||||
|
||||
Ok(Box::new(StringBox::new(report)))
|
||||
}
|
||||
|
||||
@ -256,45 +266,50 @@ impl DebugBox {
|
||||
Ok(Box::new(VoidBox::new()))
|
||||
}
|
||||
|
||||
pub fn trace_call(&self, function_name: &str, args: Vec<String>) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
pub fn trace_call(
|
||||
&self,
|
||||
function_name: &str,
|
||||
args: Vec<String>,
|
||||
) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
let mut stack = self.call_stack.write().unwrap();
|
||||
stack.push(CallInfo {
|
||||
function_name: function_name.to_string(),
|
||||
args,
|
||||
timestamp: Local::now().format("%H:%M:%S.%3f").to_string(),
|
||||
});
|
||||
|
||||
|
||||
// Keep only last 100 calls to prevent memory issues
|
||||
if stack.len() > 100 {
|
||||
stack.remove(0);
|
||||
}
|
||||
|
||||
|
||||
Ok(Box::new(VoidBox::new()))
|
||||
}
|
||||
|
||||
pub fn show_call_stack(&self) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
let stack = self.call_stack.read().unwrap();
|
||||
let mut output = String::from("=== Call Stack ===\n");
|
||||
|
||||
|
||||
for (i, call) in stack.iter().enumerate() {
|
||||
output.push_str(&format!("{}: [{}] {}({})\n",
|
||||
i,
|
||||
call.timestamp,
|
||||
output.push_str(&format!(
|
||||
"{}: [{}] {}({})\n",
|
||||
i,
|
||||
call.timestamp,
|
||||
call.function_name,
|
||||
call.args.join(", ")
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
Ok(Box::new(StringBox::new(output)))
|
||||
}
|
||||
|
||||
pub fn clear(&self) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
let mut tracked = self.tracked_boxes.write().unwrap();
|
||||
tracked.clear();
|
||||
|
||||
|
||||
let mut stack = self.call_stack.write().unwrap();
|
||||
stack.clear();
|
||||
|
||||
|
||||
println!("[DEBUG] Cleared all debug information");
|
||||
Ok(Box::new(VoidBox::new()))
|
||||
}
|
||||
@ -306,13 +321,18 @@ impl DebugBox {
|
||||
|
||||
pub fn get_tracked_count(&self) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
let tracked = self.tracked_boxes.read().unwrap();
|
||||
Ok(Box::new(crate::box_trait::IntegerBox::new(tracked.len() as i64)))
|
||||
Ok(Box::new(crate::box_trait::IntegerBox::new(
|
||||
tracked.len() as i64
|
||||
)))
|
||||
}
|
||||
|
||||
// --- Phase 1: JIT/Plugin shim tracing ---
|
||||
pub fn trace_plugin_calls(&self, on: bool) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
crate::jit::shim_trace::set_enabled(on);
|
||||
println!("[DEBUG] JIT shim trace: {}", if on {"ENABLED"} else {"DISABLED"});
|
||||
println!(
|
||||
"[DEBUG] JIT shim trace: {}",
|
||||
if on { "ENABLED" } else { "DISABLED" }
|
||||
);
|
||||
Ok(Box::new(VoidBox::new()))
|
||||
}
|
||||
|
||||
@ -329,7 +349,7 @@ impl Clone for DebugBox {
|
||||
let breakpoints = self.breakpoints.read().unwrap();
|
||||
let call_stack = self.call_stack.read().unwrap();
|
||||
let tracking_enabled = self.tracking_enabled.read().unwrap();
|
||||
|
||||
|
||||
DebugBox {
|
||||
base: BoxBase::new(), // New unique ID for cloned instance
|
||||
tracking_enabled: RwLock::new(*tracking_enabled),
|
||||
@ -345,20 +365,20 @@ impl BoxCore for DebugBox {
|
||||
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 {
|
||||
let tracked = self.tracked_boxes.read().unwrap();
|
||||
write!(f, "DebugBox[{} tracked]", tracked.len())
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
@ -377,7 +397,7 @@ impl NyashBox for DebugBox {
|
||||
let tracked = self.tracked_boxes.read().unwrap();
|
||||
StringBox::new(format!("DebugBox[{} tracked]", tracked.len()))
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(other_debug) = other.as_any().downcast_ref::<DebugBox>() {
|
||||
BoolBox::new(self.base.id == other_debug.base.id)
|
||||
@ -385,19 +405,17 @@ impl NyashBox for DebugBox {
|
||||
BoolBox::new(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
"DebugBox"
|
||||
}
|
||||
|
||||
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, VoidBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox, VoidBox};
|
||||
use std::any::Any;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -20,13 +20,19 @@ impl DebugConfigBox {
|
||||
Self {
|
||||
base: BoxBase::new(),
|
||||
jit_events: std::env::var("NYASH_JIT_EVENTS").ok().as_deref() == Some("1"),
|
||||
jit_events_compile: std::env::var("NYASH_JIT_EVENTS_COMPILE").ok().as_deref() == Some("1"),
|
||||
jit_events_runtime: std::env::var("NYASH_JIT_EVENTS_RUNTIME").ok().as_deref() == Some("1"),
|
||||
jit_events_compile: std::env::var("NYASH_JIT_EVENTS_COMPILE").ok().as_deref()
|
||||
== Some("1"),
|
||||
jit_events_runtime: std::env::var("NYASH_JIT_EVENTS_RUNTIME").ok().as_deref()
|
||||
== Some("1"),
|
||||
jit_stats: std::env::var("NYASH_JIT_STATS").ok().as_deref() == Some("1"),
|
||||
jit_stats_json: std::env::var("NYASH_JIT_STATS_JSON").ok().as_deref() == Some("1"),
|
||||
jit_dump: std::env::var("NYASH_JIT_DUMP").ok().as_deref() == Some("1"),
|
||||
jit_dot_path: std::env::var("NYASH_JIT_DOT").ok().filter(|s| !s.is_empty()),
|
||||
jit_events_path: std::env::var("NYASH_JIT_EVENTS_PATH").ok().filter(|s| !s.is_empty()),
|
||||
jit_dot_path: std::env::var("NYASH_JIT_DOT")
|
||||
.ok()
|
||||
.filter(|s| !s.is_empty()),
|
||||
jit_events_path: std::env::var("NYASH_JIT_EVENTS_PATH")
|
||||
.ok()
|
||||
.filter(|s| !s.is_empty()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,7 +44,7 @@ impl DebugConfigBox {
|
||||
"jit_stats" => self.jit_stats = on,
|
||||
"jit_stats_json" => self.jit_stats_json = on,
|
||||
"jit_dump" => self.jit_dump = on,
|
||||
_ => return Box::new(StringBox::new(format!("Unknown flag: {}", name)))
|
||||
_ => return Box::new(StringBox::new(format!("Unknown flag: {}", name))),
|
||||
}
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
@ -47,7 +53,7 @@ impl DebugConfigBox {
|
||||
match name {
|
||||
"jit_dot" | "jit_dot_path" => self.jit_dot_path = Some(value.to_string()),
|
||||
"jit_events_path" => self.jit_events_path = Some(value.to_string()),
|
||||
_ => return Box::new(StringBox::new(format!("Unknown path: {}", name)))
|
||||
_ => return Box::new(StringBox::new(format!("Unknown path: {}", name))),
|
||||
}
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
@ -75,17 +81,29 @@ impl DebugConfigBox {
|
||||
}
|
||||
|
||||
pub fn apply(&self) -> Box<dyn NyashBox> {
|
||||
let setb = |k: &str, v: bool| { if v { std::env::set_var(k, "1"); } else { std::env::remove_var(k); } };
|
||||
let setb = |k: &str, v: bool| {
|
||||
if v {
|
||||
std::env::set_var(k, "1");
|
||||
} else {
|
||||
std::env::remove_var(k);
|
||||
}
|
||||
};
|
||||
setb("NYASH_JIT_EVENTS", self.jit_events);
|
||||
setb("NYASH_JIT_EVENTS_COMPILE", self.jit_events_compile);
|
||||
setb("NYASH_JIT_EVENTS_RUNTIME", self.jit_events_runtime);
|
||||
setb("NYASH_JIT_STATS", self.jit_stats);
|
||||
setb("NYASH_JIT_STATS_JSON", self.jit_stats_json);
|
||||
setb("NYASH_JIT_DUMP", self.jit_dump);
|
||||
if let Some(p) = &self.jit_dot_path { std::env::set_var("NYASH_JIT_DOT", p); }
|
||||
else { std::env::remove_var("NYASH_JIT_DOT"); }
|
||||
if let Some(p) = &self.jit_events_path { std::env::set_var("NYASH_JIT_EVENTS_PATH", p); }
|
||||
else { std::env::remove_var("NYASH_JIT_EVENTS_PATH"); }
|
||||
if let Some(p) = &self.jit_dot_path {
|
||||
std::env::set_var("NYASH_JIT_DOT", p);
|
||||
} else {
|
||||
std::env::remove_var("NYASH_JIT_DOT");
|
||||
}
|
||||
if let Some(p) = &self.jit_events_path {
|
||||
std::env::set_var("NYASH_JIT_EVENTS_PATH", p);
|
||||
} else {
|
||||
std::env::remove_var("NYASH_JIT_EVENTS_PATH");
|
||||
}
|
||||
// If any events are enabled and threshold is not set, default to 1 so lowering runs early
|
||||
if (self.jit_events || self.jit_events_compile || self.jit_events_runtime)
|
||||
&& std::env::var("NYASH_JIT_THRESHOLD").is_err()
|
||||
@ -108,17 +126,40 @@ impl DebugConfigBox {
|
||||
}
|
||||
|
||||
impl BoxCore for DebugConfigBox {
|
||||
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, "DebugConfigBox") }
|
||||
fn as_any(&self) -> &dyn Any { self }
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any { self }
|
||||
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, "DebugConfigBox")
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl NyashBox for DebugConfigBox {
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<DebugConfigBox>()) }
|
||||
fn type_name(&self) -> &'static str { "DebugConfigBox" }
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self { base: self.base.clone(), ..self.clone() }) }
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||
fn to_string_box(&self) -> StringBox { StringBox::new("DebugConfigBox".to_string()) }
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
BoolBox::new(other.as_any().is::<DebugConfigBox>())
|
||||
}
|
||||
fn type_name(&self) -> &'static str {
|
||||
"DebugConfigBox"
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(Self {
|
||||
base: self.base.clone(),
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new("DebugConfigBox".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,11 +3,11 @@
|
||||
/*! 🖼️ EguiBox - デスクトップGUIアプリBox
|
||||
* Everything is Box哲学によるGUIフレームワーク統合
|
||||
* 「なんでもBoxにできる」化け物言語の第一歩!
|
||||
*
|
||||
*
|
||||
* ## 📝 概要
|
||||
* Rustの人気GUI框架eframeを使ったネイティブデスクトップアプリ作成。
|
||||
* Nyashコードから直接GUI操作が可能!
|
||||
*
|
||||
*
|
||||
* ## 🛠️ 利用可能メソッド
|
||||
* - `setTitle(title)` - ウィンドウタイトル設定
|
||||
* - `setSize(width, height)` - ウィンドウサイズ設定
|
||||
@ -15,7 +15,7 @@
|
||||
* - `addText(text)` - テキスト表示追加
|
||||
* - `addButton(label)` - ボタン追加
|
||||
* - `close()` - ウィンドウ閉じる
|
||||
*
|
||||
*
|
||||
* ## 💡 使用例
|
||||
* ```nyash
|
||||
* // 基本的なGUIアプリ
|
||||
@ -27,20 +27,20 @@
|
||||
* app.addButton("Click Me")
|
||||
* app.run() // GUIアプリ開始
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* ## ⚠️ 注意
|
||||
* - デスクトップ環境でのみ利用可能(WASM環境では無効)
|
||||
* - `run()`はブロッキング動作(アプリ終了まで制御を返さない)
|
||||
*/
|
||||
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
|
||||
use crate::interpreter::RuntimeError;
|
||||
use eframe::{self, egui, epaint::Vec2};
|
||||
use std::any::Any;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use eframe::{self, egui, epaint::Vec2};
|
||||
|
||||
/// EguiBox - GUI アプリケーションを包むBox
|
||||
///
|
||||
///
|
||||
/// # 使用例
|
||||
/// ```nyash
|
||||
/// app = new EguiBox()
|
||||
@ -89,16 +89,16 @@ impl EguiBox {
|
||||
update_fn: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// アプリケーション状態を設定
|
||||
pub fn set_app_state<T: Any + Send + Sync + 'static>(&mut self, state: T) {
|
||||
*self.app_state.write().unwrap() = Box::new(state);
|
||||
}
|
||||
|
||||
|
||||
/// 更新関数を設定
|
||||
pub fn set_update_fn<F>(&mut self, f: F)
|
||||
where
|
||||
F: Fn(&mut Box<dyn Any + Send + Sync>, &egui::Context) + Send + Sync + 'static
|
||||
pub fn set_update_fn<F>(&mut self, f: F)
|
||||
where
|
||||
F: Fn(&mut Box<dyn Any + Send + Sync>, &egui::Context) + Send + Sync + 'static,
|
||||
{
|
||||
self.update_fn = Some(Arc::new(f));
|
||||
}
|
||||
@ -122,19 +122,23 @@ impl BoxCore for EguiBox {
|
||||
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, "EguiBox('{}', {}x{})", self.title, self.size.x, self.size.y)
|
||||
write!(
|
||||
f,
|
||||
"EguiBox('{}', {}x{})",
|
||||
self.title, self.size.x, self.size.y
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
@ -148,21 +152,21 @@ impl std::fmt::Display for EguiBox {
|
||||
|
||||
impl NyashBox for EguiBox {
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new(
|
||||
format!("EguiBox('{}', {}x{})", self.title, self.size.x, self.size.y)
|
||||
)
|
||||
StringBox::new(format!(
|
||||
"EguiBox('{}', {}x{})",
|
||||
self.title, self.size.x, self.size.y
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(other_egui) = other.as_any().downcast_ref::<EguiBox>() {
|
||||
BoolBox::new(self.title == other_egui.title && self.size == other_egui.size)
|
||||
@ -170,11 +174,10 @@ impl NyashBox for EguiBox {
|
||||
BoolBox::new(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
"EguiBox"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// EguiBoxのメソッド実装(実際にはインタープリターから呼ばれない)
|
||||
@ -187,28 +190,24 @@ impl EguiBox {
|
||||
// we would need a more sophisticated state sharing mechanism
|
||||
let app_state = Arc::new(RwLock::new(Box::new(()) as Box<dyn Any + Send + Sync>));
|
||||
drop(state_snapshot);
|
||||
|
||||
|
||||
let update_fn = Arc::clone(update_fn);
|
||||
|
||||
|
||||
let options = eframe::NativeOptions {
|
||||
viewport: egui::ViewportBuilder::default()
|
||||
.with_inner_size(self.size)
|
||||
.with_title(&self.title),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
|
||||
let app = NyashApp {
|
||||
app_state,
|
||||
update_fn,
|
||||
};
|
||||
|
||||
|
||||
// 注意: これはブロッキング呼び出し
|
||||
let _ = eframe::run_native(
|
||||
&self.title,
|
||||
options,
|
||||
Box::new(|_cc| Ok(Box::new(app))),
|
||||
);
|
||||
|
||||
let _ = eframe::run_native(&self.title, options, Box::new(|_cc| Ok(Box::new(app))));
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
Err(RuntimeError::InvalidOperation {
|
||||
@ -221,18 +220,18 @@ impl EguiBox {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_egui_box_creation() {
|
||||
let gui = EguiBox::new();
|
||||
assert_eq!(gui.title, "Nyash GUI Application");
|
||||
assert_eq!(gui.size, Vec2::new(800.0, 600.0));
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_egui_box_to_string() {
|
||||
let gui = EguiBox::new();
|
||||
let s = gui.to_string_box();
|
||||
assert_eq!(s.value, "EguiBox('Nyash GUI Application', 800x600)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,10 +2,10 @@
|
||||
// Nyashの箱システムによるファイル入出力を提供します。
|
||||
// 参考: 既存Boxの設計思想
|
||||
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
|
||||
use std::any::Any;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::{Read, Write, Result};
|
||||
use std::io::{Read, Result, Write};
|
||||
use std::sync::RwLock;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -38,8 +38,12 @@ impl FileBox {
|
||||
Err(_) => {
|
||||
// Fallback: create with empty file handle - only for dispatch
|
||||
use std::fs::OpenOptions;
|
||||
let file = OpenOptions::new().create(true).write(true).read(true)
|
||||
.open("/dev/null").unwrap_or_else(|_| File::open("/dev/null").unwrap());
|
||||
let file = OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.read(true)
|
||||
.open("/dev/null")
|
||||
.unwrap_or_else(|_| File::open("/dev/null").unwrap());
|
||||
FileBox {
|
||||
file: RwLock::new(file),
|
||||
path: String::new(),
|
||||
@ -48,28 +52,32 @@ impl FileBox {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn open(path: &str) -> Result<Self> {
|
||||
let file = OpenOptions::new().read(true).write(true).create(true).open(path)?;
|
||||
Ok(FileBox {
|
||||
let file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(path)?;
|
||||
Ok(FileBox {
|
||||
file: RwLock::new(file),
|
||||
path: path.to_string(),
|
||||
base: BoxBase::new(),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
pub fn read_to_string(&self) -> Result<String> {
|
||||
let mut file = self.file.write().unwrap();
|
||||
let mut s = String::new();
|
||||
file.read_to_string(&mut s)?;
|
||||
Ok(s)
|
||||
}
|
||||
|
||||
|
||||
pub fn write_all(&self, buf: &[u8]) -> Result<()> {
|
||||
let mut file = self.file.write().unwrap();
|
||||
file.write_all(buf)
|
||||
}
|
||||
|
||||
|
||||
/// ファイルの内容を読み取る
|
||||
pub fn read(&self) -> Box<dyn NyashBox> {
|
||||
match self.read_to_string() {
|
||||
@ -77,7 +85,7 @@ impl FileBox {
|
||||
Err(e) => Box::new(StringBox::new(&format!("Error reading file: {}", e))),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// ファイルに内容を書き込む
|
||||
pub fn write(&self, content: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let content_str = content.to_string_box().value;
|
||||
@ -86,13 +94,13 @@ impl FileBox {
|
||||
Err(e) => Box::new(StringBox::new(&format!("Error writing file: {}", e))),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// ファイルが存在するかチェック
|
||||
pub fn exists(&self) -> Box<dyn NyashBox> {
|
||||
use std::path::Path;
|
||||
Box::new(BoolBox::new(Path::new(&self.path).exists()))
|
||||
}
|
||||
|
||||
|
||||
/// ファイルを削除
|
||||
pub fn delete(&self) -> Box<dyn NyashBox> {
|
||||
match std::fs::remove_file(&self.path) {
|
||||
@ -100,7 +108,7 @@ impl FileBox {
|
||||
Err(e) => Box::new(StringBox::new(&format!("Error deleting file: {}", e))),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// ファイルをコピー
|
||||
pub fn copy(&self, dest: &str) -> Box<dyn NyashBox> {
|
||||
match std::fs::copy(&self.path, dest) {
|
||||
@ -114,19 +122,19 @@ 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
|
||||
}
|
||||
@ -137,10 +145,10 @@ impl NyashBox for FileBox {
|
||||
// Note: Cannot truly clone a File handle, so create a new one to the same path
|
||||
match FileBox::open(&self.path) {
|
||||
Ok(new_file) => Box::new(new_file),
|
||||
Err(_) => Box::new(crate::box_trait::VoidBox::new()) // Return void on error
|
||||
Err(_) => Box::new(crate::box_trait::VoidBox::new()), // Return void on error
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
@ -150,12 +158,10 @@ impl NyashBox for FileBox {
|
||||
StringBox::new(format!("FileBox({})", self.path))
|
||||
}
|
||||
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
"FileBox"
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
|
||||
use crate::ast::ASTNode;
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
|
||||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Weak;
|
||||
use std::any::Any;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ClosureEnv {
|
||||
@ -11,7 +11,12 @@ pub struct ClosureEnv {
|
||||
}
|
||||
|
||||
impl ClosureEnv {
|
||||
pub fn new() -> Self { Self { me_value: None, captures: HashMap::new() } }
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
me_value: None,
|
||||
captures: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -24,32 +29,70 @@ pub struct FunctionBox {
|
||||
|
||||
impl FunctionBox {
|
||||
pub fn new(params: Vec<String>, body: Vec<ASTNode>) -> Self {
|
||||
Self { params, body, env: ClosureEnv::new(), base: BoxBase::new() }
|
||||
Self {
|
||||
params,
|
||||
body,
|
||||
env: ClosureEnv::new(),
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
pub fn with_env(params: Vec<String>, body: Vec<ASTNode>, env: ClosureEnv) -> Self {
|
||||
Self { params, body, env, base: BoxBase::new() }
|
||||
Self {
|
||||
params,
|
||||
body,
|
||||
env,
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BoxCore for FunctionBox {
|
||||
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, "FunctionBox(params={}, body={})", self.params.len(), self.body.len())
|
||||
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,
|
||||
"FunctionBox(params={}, body={})",
|
||||
self.params.len(),
|
||||
self.body.len()
|
||||
)
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any { self }
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any { self }
|
||||
}
|
||||
|
||||
impl NyashBox for FunctionBox {
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(self.clone()) }
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||
fn to_string_box(&self) -> StringBox { StringBox::new(format!("FunctionBox(params={}, captures={}, body={})", self.params.len(), self.env.captures.len(), self.body.len())) }
|
||||
fn type_name(&self) -> &'static str { "FunctionBox" }
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new(format!(
|
||||
"FunctionBox(params={}, captures={}, body={})",
|
||||
self.params.len(),
|
||||
self.env.captures.len(),
|
||||
self.body.len()
|
||||
))
|
||||
}
|
||||
fn type_name(&self) -> &'static str {
|
||||
"FunctionBox"
|
||||
}
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(o) = other.as_any().downcast_ref::<FunctionBox>() {
|
||||
BoolBox::new(self.box_id() == o.box_id())
|
||||
} else { BoolBox::new(false) }
|
||||
} else {
|
||||
BoolBox::new(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,13 +100,20 @@ impl Clone for ClosureEnv {
|
||||
fn clone(&self) -> Self {
|
||||
let me_value = self.me_value.as_ref().map(|w| Weak::clone(w));
|
||||
let mut captures: HashMap<String, Box<dyn NyashBox>> = HashMap::new();
|
||||
for (k, v) in &self.captures { captures.insert(k.clone(), v.clone_box()); }
|
||||
for (k, v) in &self.captures {
|
||||
captures.insert(k.clone(), v.clone_box());
|
||||
}
|
||||
Self { me_value, captures }
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for FunctionBox {
|
||||
fn clone(&self) -> Self {
|
||||
Self { params: self.params.clone(), body: self.body.clone(), env: self.env.clone(), base: BoxBase::new() }
|
||||
Self {
|
||||
params: self.params.clone(),
|
||||
body: self.body.clone(),
|
||||
env: self.env.clone(),
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
// Nyashの箱システムによる非同期処理の基盤を提供します。
|
||||
// 参考: 既存Boxの設計思想
|
||||
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
|
||||
use std::any::Any;
|
||||
use std::sync::{Mutex, Condvar, Arc, Weak};
|
||||
use std::sync::{Arc, Condvar, Mutex, Weak};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NyashFutureBox {
|
||||
@ -33,7 +33,10 @@ pub struct FutureWeak {
|
||||
|
||||
impl Clone for NyashFutureBox {
|
||||
fn clone(&self) -> Self {
|
||||
Self { inner: self.inner.clone(), base: BoxBase::new() }
|
||||
Self {
|
||||
inner: self.inner.clone(),
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,13 +44,16 @@ impl NyashFutureBox {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
inner: Arc::new(Inner {
|
||||
state: Mutex::new(FutureState { result: None, ready: false }),
|
||||
state: Mutex::new(FutureState {
|
||||
result: None,
|
||||
ready: false,
|
||||
}),
|
||||
cv: Condvar::new(),
|
||||
}),
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Set the result of the future
|
||||
pub fn set_result(&self, value: Box<dyn NyashBox>) {
|
||||
let mut st = self.inner.state.lock().unwrap();
|
||||
@ -55,7 +61,7 @@ impl NyashFutureBox {
|
||||
st.ready = true;
|
||||
self.inner.cv.notify_all();
|
||||
}
|
||||
|
||||
|
||||
/// Get the result (blocks until ready)
|
||||
pub fn get(&self) -> Box<dyn NyashBox> {
|
||||
let mut st = self.inner.state.lock().unwrap();
|
||||
@ -64,21 +70,25 @@ impl NyashFutureBox {
|
||||
}
|
||||
st.result.as_ref().unwrap().clone_box()
|
||||
}
|
||||
|
||||
|
||||
/// Check if the future is ready
|
||||
pub fn ready(&self) -> bool {
|
||||
self.inner.state.lock().unwrap().ready
|
||||
}
|
||||
|
||||
/// Create a non-owning weak handle to this Future's state
|
||||
pub fn downgrade(&self) -> FutureWeak { FutureWeak { inner: Arc::downgrade(&self.inner) } }
|
||||
pub fn downgrade(&self) -> FutureWeak {
|
||||
FutureWeak {
|
||||
inner: Arc::downgrade(&self.inner),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NyashBox for NyashFutureBox {
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
@ -98,12 +108,10 @@ impl NyashBox for NyashFutureBox {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
"NyashFutureBox"
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(other_future) = other.as_any().downcast_ref::<NyashFutureBox>() {
|
||||
BoolBox::new(self.base.id == other_future.base.id)
|
||||
@ -117,7 +125,7 @@ impl BoxCore for NyashFutureBox {
|
||||
fn box_id(&self) -> u64 {
|
||||
self.base.id
|
||||
}
|
||||
|
||||
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> {
|
||||
self.base.parent_type_id
|
||||
}
|
||||
@ -135,11 +143,11 @@ impl BoxCore for NyashFutureBox {
|
||||
write!(f, "Future(pending)")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
@ -164,6 +172,8 @@ impl FutureBox {
|
||||
impl FutureWeak {
|
||||
/// Try to upgrade and check readiness
|
||||
pub(crate) fn is_ready(&self) -> Option<bool> {
|
||||
self.inner.upgrade().map(|arc| arc.state.lock().unwrap().ready)
|
||||
self.inner
|
||||
.upgrade()
|
||||
.map(|arc| arc.state.lock().unwrap().ready)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,13 @@
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, VoidBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox, VoidBox};
|
||||
use std::any::Any;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GcConfigBox { base: BoxBase, counting: bool, trace: bool, barrier_strict: bool }
|
||||
pub struct GcConfigBox {
|
||||
base: BoxBase,
|
||||
counting: bool,
|
||||
trace: bool,
|
||||
barrier_strict: bool,
|
||||
}
|
||||
|
||||
impl GcConfigBox {
|
||||
pub fn new() -> Self {
|
||||
@ -18,7 +23,7 @@ impl GcConfigBox {
|
||||
"counting" => self.counting = on,
|
||||
"trace" => self.trace = on,
|
||||
"barrier_strict" | "strict" => self.barrier_strict = on,
|
||||
_ => return Box::new(StringBox::new(format!("Unknown flag: {}", name)))
|
||||
_ => return Box::new(StringBox::new(format!("Unknown flag: {}", name))),
|
||||
}
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
@ -32,31 +37,59 @@ impl GcConfigBox {
|
||||
Box::new(BoolBox::new(v))
|
||||
}
|
||||
pub fn apply(&self) -> Box<dyn NyashBox> {
|
||||
let setb = |k: &str, v: bool| { if v { std::env::set_var(k, "1"); } else { std::env::remove_var(k); } };
|
||||
let setb = |k: &str, v: bool| {
|
||||
if v {
|
||||
std::env::set_var(k, "1");
|
||||
} else {
|
||||
std::env::remove_var(k);
|
||||
}
|
||||
};
|
||||
setb("NYASH_GC_COUNTING", self.counting);
|
||||
setb("NYASH_GC_TRACE", self.trace);
|
||||
setb("NYASH_GC_BARRIER_STRICT", self.barrier_strict);
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn summary(&self) -> Box<dyn NyashBox> {
|
||||
let s = format!("counting={} trace={} barrier_strict={}", self.counting, self.trace, self.barrier_strict);
|
||||
let s = format!(
|
||||
"counting={} trace={} barrier_strict={}",
|
||||
self.counting, self.trace, self.barrier_strict
|
||||
);
|
||||
Box::new(StringBox::new(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl BoxCore for GcConfigBox {
|
||||
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, "GcConfigBox") }
|
||||
fn as_any(&self) -> &dyn Any { self }
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any { self }
|
||||
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, "GcConfigBox")
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl NyashBox for GcConfigBox {
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<GcConfigBox>()) }
|
||||
fn type_name(&self) -> &'static str { "GcConfigBox" }
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(self.clone()) }
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||
fn to_string_box(&self) -> StringBox { StringBox::new(self.summary().to_string_box().value) }
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
BoolBox::new(other.as_any().is::<GcConfigBox>())
|
||||
}
|
||||
fn type_name(&self) -> &'static str {
|
||||
"GcConfigBox"
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new(self.summary().to_string_box().value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
//! HttpClientBox 🌐 - HTTP通信
|
||||
// Nyashの箱システムによるHTTP通信を提供します。
|
||||
// 参考: 既存Boxの設計思想
|
||||
//
|
||||
//
|
||||
// NOTE: HTTPサポートは現在開発中です。
|
||||
// reqwestクレートの依存関係のため、一時的に無効化されています。
|
||||
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
|
||||
use std::any::Any;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -15,33 +15,38 @@ pub struct HttpClientBox {
|
||||
|
||||
impl HttpClientBox {
|
||||
pub fn new() -> Self {
|
||||
HttpClientBox {
|
||||
base: BoxBase::new()
|
||||
HttpClientBox {
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// HTTP GETリクエスト(スタブ)
|
||||
pub fn http_get(&self, _url: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
Box::new(StringBox::new("HTTP support is currently disabled"))
|
||||
}
|
||||
|
||||
|
||||
/// HTTP POSTリクエスト(スタブ)
|
||||
pub fn post(&self, _url: Box<dyn NyashBox>, _body: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
Box::new(StringBox::new("HTTP support is currently disabled"))
|
||||
}
|
||||
|
||||
|
||||
/// HTTP PUT リクエスト(スタブ)
|
||||
pub fn put(&self, _url: Box<dyn NyashBox>, _body: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
Box::new(StringBox::new("HTTP support is currently disabled"))
|
||||
}
|
||||
|
||||
|
||||
/// HTTP DELETE リクエスト(スタブ)
|
||||
pub fn delete(&self, _url: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
Box::new(StringBox::new("HTTP support is currently disabled"))
|
||||
}
|
||||
|
||||
|
||||
/// ヘッダー付きHTTPリクエスト(スタブ)
|
||||
pub fn request(&self, _method: Box<dyn NyashBox>, _url: Box<dyn NyashBox>, _options: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
pub fn request(
|
||||
&self,
|
||||
_method: Box<dyn NyashBox>,
|
||||
_url: Box<dyn NyashBox>,
|
||||
_options: Box<dyn NyashBox>,
|
||||
) -> Box<dyn NyashBox> {
|
||||
Box::new(StringBox::new("HTTP support is currently disabled"))
|
||||
}
|
||||
}
|
||||
@ -50,7 +55,7 @@ impl NyashBox for HttpClientBox {
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
@ -60,12 +65,10 @@ impl NyashBox for HttpClientBox {
|
||||
StringBox::new(format!("HttpClientBox(id: {})", self.base.id))
|
||||
}
|
||||
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
"HttpClientBox"
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(other_http) = other.as_any().downcast_ref::<HttpClientBox>() {
|
||||
BoolBox::new(self.base.id == other_http.base.id)
|
||||
@ -79,7 +82,7 @@ impl BoxCore for HttpClientBox {
|
||||
fn box_id(&self) -> u64 {
|
||||
self.base.id
|
||||
}
|
||||
|
||||
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> {
|
||||
self.base.parent_type_id
|
||||
}
|
||||
@ -87,11 +90,11 @@ impl BoxCore for HttpClientBox {
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "HttpClientBox(id: {})", self.base.id)
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
@ -101,4 +104,4 @@ impl std::fmt::Display for HttpClientBox {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.fmt_box(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,36 +1,36 @@
|
||||
/*! 📬 HTTPRequestBox & HTTPResponseBox - HTTP メッセージ処理
|
||||
*
|
||||
*
|
||||
* ## 📝 概要
|
||||
* HTTP/1.1 プロトコルのリクエスト・レスポンス処理を提供するBox群
|
||||
* SocketBox と連携して完全なHTTPサーバー・クライアント機能を実現
|
||||
*
|
||||
*
|
||||
* ## 🛠️ HTTPRequestBox - リクエスト処理
|
||||
* ### HTTP Method & URL
|
||||
* - `getMethod()` - HTTP メソッド取得 (GET, POST, etc.)
|
||||
* - `getPath()` - URL パス取得
|
||||
* - `getQueryString()` - クエリ文字列取得
|
||||
*
|
||||
*
|
||||
* ### Headers
|
||||
* - `getHeader(name)` - 特定ヘッダー取得
|
||||
* - `getAllHeaders()` - 全ヘッダー取得(MapBox)
|
||||
* - `hasHeader(name)` - ヘッダー存在確認
|
||||
*
|
||||
*
|
||||
* ### Body & Content
|
||||
* - `getBody()` - リクエストボディ取得
|
||||
* - `getContentType()` - Content-Type取得
|
||||
* - `getContentLength()` - Content-Length取得
|
||||
*
|
||||
*
|
||||
* ## 🛠️ HTTPResponseBox - レスポンス生成
|
||||
* ### Status & Headers
|
||||
* - `setStatus(code, message)` - ステータス設定
|
||||
* - `setHeader(name, value)` - ヘッダー設定
|
||||
* - `setContentType(type)` - Content-Type設定
|
||||
*
|
||||
*
|
||||
* ### Body & Output
|
||||
* - `setBody(content)` - レスポンスボディ設定
|
||||
* - `appendBody(content)` - ボディ追加
|
||||
* - `toHttpString()` - HTTP形式文字列生成
|
||||
*
|
||||
*
|
||||
* ## 💡 使用例
|
||||
* ```nyash
|
||||
* // Request parsing
|
||||
@ -38,7 +38,7 @@
|
||||
* local request = HTTPRequestBox.parse(rawRequest)
|
||||
* print("Method: " + request.getMethod())
|
||||
* print("Path: " + request.getPath())
|
||||
*
|
||||
*
|
||||
* // Response generation
|
||||
* local response = new HTTPResponseBox()
|
||||
* response.setStatus(200, "OK")
|
||||
@ -48,7 +48,7 @@
|
||||
* ```
|
||||
*/
|
||||
|
||||
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox};
|
||||
use crate::boxes::MapBox;
|
||||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
@ -77,32 +77,32 @@ impl HTTPRequestBox {
|
||||
http_version: "HTTP/1.1".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 生のHTTPリクエスト文字列を解析
|
||||
pub fn parse(raw_request: Box<dyn NyashBox>) -> Self {
|
||||
let request_str = raw_request.to_string_box().value;
|
||||
let mut request = HTTPRequestBox::new();
|
||||
|
||||
|
||||
let lines: Vec<&str> = request_str.lines().collect();
|
||||
if lines.is_empty() {
|
||||
return request;
|
||||
}
|
||||
|
||||
|
||||
// Parse request line: "GET /path HTTP/1.1"
|
||||
let request_line_parts: Vec<&str> = lines[0].split_whitespace().collect();
|
||||
if request_line_parts.len() >= 3 {
|
||||
request.method = request_line_parts[0].to_string();
|
||||
|
||||
|
||||
// Split path and query string
|
||||
let url_parts: Vec<&str> = request_line_parts[1].splitn(2, '?').collect();
|
||||
request.path = url_parts[0].to_string();
|
||||
if url_parts.len() > 1 {
|
||||
request.query_string = url_parts[1].to_string();
|
||||
}
|
||||
|
||||
|
||||
request.http_version = request_line_parts[2].to_string();
|
||||
}
|
||||
|
||||
|
||||
// Parse headers
|
||||
let mut header_end = 1;
|
||||
for (i, line) in lines.iter().enumerate().skip(1) {
|
||||
@ -110,37 +110,37 @@ impl HTTPRequestBox {
|
||||
header_end = i + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if let Some(colon_pos) = line.find(':') {
|
||||
let name = line[..colon_pos].trim().to_lowercase();
|
||||
let value = line[colon_pos + 1..].trim().to_string();
|
||||
request.headers.insert(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Parse body (everything after headers)
|
||||
if header_end < lines.len() {
|
||||
request.body = lines[header_end..].join("\n");
|
||||
}
|
||||
|
||||
|
||||
request
|
||||
}
|
||||
|
||||
|
||||
/// HTTP メソッド取得
|
||||
pub fn get_method(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(StringBox::new(self.method.clone()))
|
||||
}
|
||||
|
||||
|
||||
/// URL パス取得
|
||||
pub fn get_path(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(StringBox::new(self.path.clone()))
|
||||
}
|
||||
|
||||
|
||||
/// クエリ文字列取得
|
||||
pub fn get_query_string(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(StringBox::new(self.query_string.clone()))
|
||||
}
|
||||
|
||||
|
||||
/// 特定ヘッダー取得
|
||||
pub fn get_header(&self, name: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let header_name = name.to_string_box().value.to_lowercase();
|
||||
@ -149,7 +149,7 @@ impl HTTPRequestBox {
|
||||
None => Box::new(StringBox::new("".to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 全ヘッダー取得(MapBox形式)
|
||||
pub fn get_all_headers(&self) -> Box<dyn NyashBox> {
|
||||
let headers_map = MapBox::new();
|
||||
@ -160,31 +160,29 @@ impl HTTPRequestBox {
|
||||
}
|
||||
Box::new(headers_map)
|
||||
}
|
||||
|
||||
|
||||
/// ヘッダー存在確認
|
||||
pub fn has_header(&self, name: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let header_name = name.to_string_box().value.to_lowercase();
|
||||
Box::new(BoolBox::new(self.headers.contains_key(&header_name)))
|
||||
}
|
||||
|
||||
|
||||
/// リクエストボディ取得
|
||||
pub fn get_body(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(StringBox::new(self.body.clone()))
|
||||
}
|
||||
|
||||
|
||||
/// Content-Type取得
|
||||
pub fn get_content_type(&self) -> Box<dyn NyashBox> {
|
||||
self.get_header(Box::new(StringBox::new("content-type".to_string())))
|
||||
}
|
||||
|
||||
|
||||
/// Content-Length取得
|
||||
pub fn get_content_length(&self) -> Box<dyn NyashBox> {
|
||||
match self.headers.get("content-length") {
|
||||
Some(length_str) => {
|
||||
match length_str.parse::<i64>() {
|
||||
Ok(length) => Box::new(IntegerBox::new(length)),
|
||||
Err(_) => Box::new(IntegerBox::new(0)),
|
||||
}
|
||||
Some(length_str) => match length_str.parse::<i64>() {
|
||||
Ok(length) => Box::new(IntegerBox::new(length)),
|
||||
Err(_) => Box::new(IntegerBox::new(0)),
|
||||
},
|
||||
None => Box::new(IntegerBox::new(0)),
|
||||
}
|
||||
@ -195,15 +193,19 @@ impl NyashBox for HTTPRequestBox {
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new(format!("HTTPRequest({} {} - {} headers)",
|
||||
self.method, self.path, self.headers.len()))
|
||||
StringBox::new(format!(
|
||||
"HTTPRequest({} {} - {} headers)",
|
||||
self.method,
|
||||
self.path,
|
||||
self.headers.len()
|
||||
))
|
||||
}
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
@ -223,20 +225,25 @@ impl BoxCore for HTTPRequestBox {
|
||||
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, "HTTPRequest({} {} - {} headers)",
|
||||
self.method, self.path, self.headers.len())
|
||||
write!(
|
||||
f,
|
||||
"HTTPRequest({} {} - {} headers)",
|
||||
self.method,
|
||||
self.path,
|
||||
self.headers.len()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
@ -270,105 +277,123 @@ impl HTTPResponseBox {
|
||||
http_version: "HTTP/1.1".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// ステータスコード・メッセージ設定
|
||||
pub fn set_status(&self, code: Box<dyn NyashBox>, message: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
pub fn set_status(
|
||||
&self,
|
||||
code: Box<dyn NyashBox>,
|
||||
message: Box<dyn NyashBox>,
|
||||
) -> Box<dyn NyashBox> {
|
||||
// Note: This would need interior mutability for actual mutation
|
||||
// For now, this is a placeholder for the API structure
|
||||
let _code_val = code.to_string_box().value.parse::<i32>().unwrap_or(200);
|
||||
let _message_val = message.to_string_box().value;
|
||||
|
||||
|
||||
// TODO: Use RefCell or similar for interior mutability
|
||||
Box::new(BoolBox::new(true))
|
||||
}
|
||||
|
||||
|
||||
/// ヘッダー設定
|
||||
pub fn set_header(&self, name: Box<dyn NyashBox>, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
pub fn set_header(
|
||||
&self,
|
||||
name: Box<dyn NyashBox>,
|
||||
value: Box<dyn NyashBox>,
|
||||
) -> Box<dyn NyashBox> {
|
||||
let _name_str = name.to_string_box().value;
|
||||
let _value_str = value.to_string_box().value;
|
||||
|
||||
|
||||
// TODO: Use RefCell for interior mutability
|
||||
Box::new(BoolBox::new(true))
|
||||
}
|
||||
|
||||
|
||||
/// Content-Type設定
|
||||
pub fn set_content_type(&self, content_type: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let content_type_str = content_type.to_string_box().value;
|
||||
self.set_header(
|
||||
Box::new(StringBox::new("Content-Type".to_string())),
|
||||
Box::new(StringBox::new(content_type_str))
|
||||
Box::new(StringBox::new(content_type_str)),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
/// レスポンスボディ設定
|
||||
pub fn set_body(&self, content: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let _content_str = content.to_string_box().value;
|
||||
|
||||
|
||||
// TODO: Use RefCell for interior mutability
|
||||
Box::new(BoolBox::new(true))
|
||||
}
|
||||
|
||||
|
||||
/// ボディ追加
|
||||
pub fn append_body(&self, content: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let _content_str = content.to_string_box().value;
|
||||
|
||||
|
||||
// TODO: Use RefCell for interior mutability
|
||||
Box::new(BoolBox::new(true))
|
||||
}
|
||||
|
||||
|
||||
/// HTTP形式文字列生成
|
||||
pub fn to_http_string(&self) -> Box<dyn NyashBox> {
|
||||
let mut response = String::new();
|
||||
|
||||
|
||||
// Status line
|
||||
response.push_str(&format!("{} {} {}\r\n",
|
||||
self.http_version, self.status_code, self.status_message));
|
||||
|
||||
response.push_str(&format!(
|
||||
"{} {} {}\r\n",
|
||||
self.http_version, self.status_code, self.status_message
|
||||
));
|
||||
|
||||
// Headers
|
||||
for (name, value) in &self.headers {
|
||||
response.push_str(&format!("{}: {}\r\n", name, value));
|
||||
}
|
||||
|
||||
|
||||
// Content-Length if not already set
|
||||
if !self.headers.contains_key("content-length") && !self.body.is_empty() {
|
||||
response.push_str(&format!("Content-Length: {}\r\n", self.body.len()));
|
||||
}
|
||||
|
||||
|
||||
// Empty line before body
|
||||
response.push_str("\r\n");
|
||||
|
||||
|
||||
// Body
|
||||
response.push_str(&self.body);
|
||||
|
||||
|
||||
Box::new(StringBox::new(response))
|
||||
}
|
||||
|
||||
|
||||
/// Quick HTML response creation
|
||||
pub fn create_html_response(content: Box<dyn NyashBox>) -> Self {
|
||||
let mut response = HTTPResponseBox::new();
|
||||
response.status_code = 200;
|
||||
response.status_message = "OK".to_string();
|
||||
response.headers.insert("Content-Type".to_string(), "text/html; charset=utf-8".to_string());
|
||||
response.headers.insert(
|
||||
"Content-Type".to_string(),
|
||||
"text/html; charset=utf-8".to_string(),
|
||||
);
|
||||
response.body = content.to_string_box().value;
|
||||
response
|
||||
}
|
||||
|
||||
|
||||
/// Quick JSON response creation
|
||||
pub fn create_json_response(content: Box<dyn NyashBox>) -> Self {
|
||||
let mut response = HTTPResponseBox::new();
|
||||
response.status_code = 200;
|
||||
response.status_message = "OK".to_string();
|
||||
response.headers.insert("Content-Type".to_string(), "application/json".to_string());
|
||||
response
|
||||
.headers
|
||||
.insert("Content-Type".to_string(), "application/json".to_string());
|
||||
response.body = content.to_string_box().value;
|
||||
response
|
||||
}
|
||||
|
||||
|
||||
/// Quick 404 response creation
|
||||
pub fn create_404_response() -> Self {
|
||||
let mut response = HTTPResponseBox::new();
|
||||
response.status_code = 404;
|
||||
response.status_message = "Not Found".to_string();
|
||||
response.headers.insert("Content-Type".to_string(), "text/html; charset=utf-8".to_string());
|
||||
response.headers.insert(
|
||||
"Content-Type".to_string(),
|
||||
"text/html; charset=utf-8".to_string(),
|
||||
);
|
||||
response.body = "<html><body><h1>404 - Not Found</h1></body></html>".to_string();
|
||||
response
|
||||
}
|
||||
@ -378,15 +403,19 @@ impl NyashBox for HTTPResponseBox {
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new(format!("HTTPResponse({} {} - {} bytes)",
|
||||
self.status_code, self.status_message, self.body.len()))
|
||||
StringBox::new(format!(
|
||||
"HTTPResponse({} {} - {} bytes)",
|
||||
self.status_code,
|
||||
self.status_message,
|
||||
self.body.len()
|
||||
))
|
||||
}
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
@ -406,20 +435,25 @@ impl BoxCore for HTTPResponseBox {
|
||||
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, "HTTPResponse({} {} - {} bytes)",
|
||||
self.status_code, self.status_message, self.body.len())
|
||||
write!(
|
||||
f,
|
||||
"HTTPResponse({} {} - {} bytes)",
|
||||
self.status_code,
|
||||
self.status_message,
|
||||
self.body.len()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
@ -429,4 +463,4 @@ impl std::fmt::Display for HTTPResponseBox {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.fmt_box(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,51 +1,51 @@
|
||||
/*! 🌐 HTTPServerBox - HTTP サーバー実装
|
||||
*
|
||||
*
|
||||
* ## 📝 概要
|
||||
* TCP SocketBox を基盤とした高性能 HTTP/1.1 サーバー
|
||||
* 並行処理・ルーティング・ミドルウェア対応で実用アプリケーション開発可能
|
||||
*
|
||||
*
|
||||
* ## 🛠️ 利用可能メソッド
|
||||
* ### Server Management
|
||||
* - `bind(address, port)` - サーバーアドレス bind
|
||||
* - `listen(backlog)` - 接続待機開始
|
||||
* - `start()` - HTTP サーバー開始(ブロッキング)
|
||||
* - `stop()` - サーバー停止
|
||||
*
|
||||
*
|
||||
* ### Routing & Handlers
|
||||
* - `route(path, handler)` - ルート・ハンドラー登録
|
||||
* - `get(path, handler)` - GET ルート登録
|
||||
* - `post(path, handler)` - POST ルート登録
|
||||
* - `put(path, handler)` - PUT ルート登録
|
||||
* - `delete(path, handler)` - DELETE ルート登録
|
||||
*
|
||||
*
|
||||
* ### Middleware & Configuration
|
||||
* - `use(middleware)` - ミドルウェア登録
|
||||
* - `setStaticPath(path)` - 静的ファイル配信設定
|
||||
* - `setTimeout(seconds)` - リクエストタイムアウト設定
|
||||
*
|
||||
*
|
||||
* ## 💡 使用例
|
||||
* ```nyash
|
||||
* // HTTP Server creation
|
||||
* local server = new HTTPServerBox()
|
||||
* server.bind("0.0.0.0", 8080)
|
||||
*
|
||||
*
|
||||
* // Route handlers
|
||||
* server.get("/", APIHandler.home)
|
||||
* server.get("/api/status", APIHandler.status)
|
||||
* server.post("/api/users", APIHandler.createUser)
|
||||
*
|
||||
*
|
||||
* // Start server (blocking)
|
||||
* print("🚀 Server starting on port 8080...")
|
||||
* server.start()
|
||||
* ```
|
||||
*/
|
||||
|
||||
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, BoxCore, BoxBase};
|
||||
use crate::boxes::SocketBox;
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox};
|
||||
use crate::boxes::http_message_box::{HTTPRequestBox, HTTPResponseBox};
|
||||
use crate::boxes::SocketBox;
|
||||
use std::any::Any;
|
||||
use std::sync::RwLock;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::RwLock;
|
||||
use std::thread;
|
||||
|
||||
/// HTTP サーバーを提供するBox
|
||||
@ -66,26 +66,29 @@ impl Clone for HTTPServerBox {
|
||||
// State-preserving clone implementation following PR #87 pattern
|
||||
let socket_guard = self.socket.read().unwrap();
|
||||
let socket_val = socket_guard.as_ref().map(|s| s.clone());
|
||||
|
||||
|
||||
let routes_guard = self.routes.read().unwrap();
|
||||
let routes_val: HashMap<String, Box<dyn NyashBox>> = routes_guard.iter()
|
||||
let routes_val: HashMap<String, Box<dyn NyashBox>> = routes_guard
|
||||
.iter()
|
||||
.map(|(k, v)| (k.clone(), v.clone_box()))
|
||||
.collect();
|
||||
|
||||
|
||||
let middleware_guard = self.middleware.read().unwrap();
|
||||
let middleware_val: Vec<Box<dyn NyashBox>> = middleware_guard.iter()
|
||||
let middleware_val: Vec<Box<dyn NyashBox>> = middleware_guard
|
||||
.iter()
|
||||
.map(|item| item.clone_box())
|
||||
.collect();
|
||||
|
||||
|
||||
let running_val = *self.running.read().unwrap();
|
||||
let static_path_val = self.static_path.read().unwrap().clone();
|
||||
let timeout_val = *self.timeout_seconds.read().unwrap();
|
||||
|
||||
|
||||
let connections_guard = self.active_connections.read().unwrap();
|
||||
let connections_val: Vec<Box<dyn NyashBox>> = connections_guard.iter()
|
||||
let connections_val: Vec<Box<dyn NyashBox>> = connections_guard
|
||||
.iter()
|
||||
.map(|item| item.clone_box())
|
||||
.collect();
|
||||
|
||||
|
||||
Self {
|
||||
base: BoxBase::new(), // New unique ID for clone
|
||||
socket: RwLock::new(socket_val),
|
||||
@ -112,39 +115,43 @@ impl HTTPServerBox {
|
||||
active_connections: RwLock::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// サーバーアドレスにバインド
|
||||
pub fn bind(&self, address: Box<dyn NyashBox>, port: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let socket = SocketBox::new();
|
||||
let bind_result = socket.bind(address, port);
|
||||
|
||||
|
||||
if bind_result.to_string_box().value == "true" {
|
||||
match self.socket.write() {
|
||||
Ok(mut socket_guard) => {
|
||||
*socket_guard = Some(socket);
|
||||
Box::new(BoolBox::new(true))
|
||||
},
|
||||
Err(_) => {
|
||||
Box::new(StringBox::new("Error: Failed to acquire socket lock".to_string()))
|
||||
}
|
||||
Err(_) => Box::new(StringBox::new(
|
||||
"Error: Failed to acquire socket lock".to_string(),
|
||||
)),
|
||||
}
|
||||
} else {
|
||||
Box::new(BoolBox::new(false))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 接続待機開始
|
||||
pub fn listen(&self, _backlog: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let socket_guard = match self.socket.read() {
|
||||
Ok(guard) => guard,
|
||||
Err(_) => return Box::new(StringBox::new("Error: Failed to acquire socket lock".to_string())),
|
||||
Err(_) => {
|
||||
return Box::new(StringBox::new(
|
||||
"Error: Failed to acquire socket lock".to_string(),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
if let Some(ref _socket) = *socket_guard {
|
||||
// For HTTPServerBox, if we have a socket stored, it means bind() was successful
|
||||
// and the socket should be in listening state. TcpListener::bind already puts
|
||||
// the socket in listening state, so we just need to verify it's working.
|
||||
|
||||
|
||||
// Try to access the stored listener directly (this is a simplified check)
|
||||
// In a real implementation, we'd store the listener state separately
|
||||
Box::new(BoolBox::new(true))
|
||||
@ -152,27 +159,35 @@ impl HTTPServerBox {
|
||||
Box::new(BoolBox::new(false))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// HTTP サーバー開始(メインループ)
|
||||
pub fn start(&self) -> Box<dyn NyashBox> {
|
||||
// Set running state
|
||||
match self.running.write() {
|
||||
Ok(mut running) => *running = true,
|
||||
Err(_) => return Box::new(StringBox::new("Error: Failed to set running state".to_string())),
|
||||
Err(_) => {
|
||||
return Box::new(StringBox::new(
|
||||
"Error: Failed to set running state".to_string(),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
let socket_guard = match self.socket.read() {
|
||||
Ok(guard) => guard,
|
||||
Err(_) => return Box::new(StringBox::new("Error: Failed to acquire socket lock".to_string())),
|
||||
Err(_) => {
|
||||
return Box::new(StringBox::new(
|
||||
"Error: Failed to acquire socket lock".to_string(),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
if let Some(ref socket) = *socket_guard {
|
||||
// Clone socket for the server loop
|
||||
let server_socket = socket.clone();
|
||||
drop(socket_guard);
|
||||
|
||||
|
||||
println!("🚀 HTTP Server starting...");
|
||||
|
||||
|
||||
// Main server loop - need to handle RwLock references carefully for threading
|
||||
loop {
|
||||
// Check if server should stop
|
||||
@ -180,53 +195,54 @@ impl HTTPServerBox {
|
||||
Ok(running_guard) => *running_guard,
|
||||
Err(_) => break, // Exit loop if we can't check running state
|
||||
};
|
||||
|
||||
|
||||
if !should_continue {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// Accept new connection
|
||||
let client_result = server_socket.accept();
|
||||
|
||||
|
||||
// Check if we got a valid client connection
|
||||
let client_socket = match client_result.as_any().downcast_ref::<SocketBox>() {
|
||||
Some(socket) => socket.clone(),
|
||||
None => continue, // Skip invalid connections
|
||||
};
|
||||
|
||||
|
||||
// Add to active connections (with error handling)
|
||||
if let Ok(mut connections) = self.active_connections.write() {
|
||||
connections.push(Box::new(client_socket.clone()));
|
||||
}
|
||||
|
||||
|
||||
// Handle client in separate thread (simulate nowait)
|
||||
// For RwLock pattern, we need to pass the data needed for the thread
|
||||
let routes_snapshot = match self.routes.read() {
|
||||
Ok(routes_guard) => {
|
||||
let routes_clone: HashMap<String, Box<dyn NyashBox>> = routes_guard.iter()
|
||||
let routes_clone: HashMap<String, Box<dyn NyashBox>> = routes_guard
|
||||
.iter()
|
||||
.map(|(k, v)| (k.clone(), v.clone_box()))
|
||||
.collect();
|
||||
routes_clone
|
||||
},
|
||||
}
|
||||
Err(_) => continue, // Skip this connection if we can't read routes
|
||||
};
|
||||
|
||||
|
||||
thread::spawn(move || {
|
||||
Self::handle_client_request_with_routes(client_socket, routes_snapshot);
|
||||
// Note: Connection cleanup is handled separately to avoid complex lifetime issues
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Box::new(BoolBox::new(true))
|
||||
} else {
|
||||
Box::new(BoolBox::new(false))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// サーバー停止
|
||||
pub fn stop(&self) -> Box<dyn NyashBox> {
|
||||
*self.running.write().unwrap() = false;
|
||||
|
||||
|
||||
// Close all active connections
|
||||
let mut connections = self.active_connections.write().unwrap();
|
||||
for connection in connections.iter() {
|
||||
@ -235,128 +251,128 @@ impl HTTPServerBox {
|
||||
}
|
||||
}
|
||||
connections.clear();
|
||||
|
||||
|
||||
// Close server socket
|
||||
if let Some(ref socket) = *self.socket.read().unwrap() {
|
||||
let _ = socket.close();
|
||||
}
|
||||
|
||||
|
||||
println!("🛑 HTTP Server stopped");
|
||||
Box::new(BoolBox::new(true))
|
||||
}
|
||||
|
||||
|
||||
/// ルート・ハンドラー登録
|
||||
pub fn route(&self, path: Box<dyn NyashBox>, handler: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let path_str = path.to_string_box().value;
|
||||
let route_key = format!("ANY {}", path_str);
|
||||
|
||||
|
||||
self.routes.write().unwrap().insert(route_key, handler);
|
||||
Box::new(BoolBox::new(true))
|
||||
}
|
||||
|
||||
|
||||
/// GET ルート登録
|
||||
pub fn get(&self, path: Box<dyn NyashBox>, handler: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let path_str = path.to_string_box().value;
|
||||
let route_key = format!("GET {}", path_str);
|
||||
|
||||
|
||||
self.routes.write().unwrap().insert(route_key, handler);
|
||||
Box::new(BoolBox::new(true))
|
||||
}
|
||||
|
||||
|
||||
/// POST ルート登録
|
||||
pub fn post(&self, path: Box<dyn NyashBox>, handler: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let path_str = path.to_string_box().value;
|
||||
let route_key = format!("POST {}", path_str);
|
||||
|
||||
|
||||
self.routes.write().unwrap().insert(route_key, handler);
|
||||
Box::new(BoolBox::new(true))
|
||||
}
|
||||
|
||||
|
||||
/// PUT ルート登録
|
||||
pub fn put(&self, path: Box<dyn NyashBox>, handler: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let path_str = path.to_string_box().value;
|
||||
let route_key = format!("PUT {}", path_str);
|
||||
|
||||
|
||||
self.routes.write().unwrap().insert(route_key, handler);
|
||||
Box::new(BoolBox::new(true))
|
||||
}
|
||||
|
||||
|
||||
/// DELETE ルート登録
|
||||
pub fn delete(&self, path: Box<dyn NyashBox>, handler: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let path_str = path.to_string_box().value;
|
||||
let route_key = format!("DELETE {}", path_str);
|
||||
|
||||
|
||||
self.routes.write().unwrap().insert(route_key, handler);
|
||||
Box::new(BoolBox::new(true))
|
||||
}
|
||||
|
||||
|
||||
/// 静的ファイル配信パス設定
|
||||
pub fn set_static_path(&self, path: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let path_str = path.to_string_box().value;
|
||||
*self.static_path.write().unwrap() = Some(path_str);
|
||||
Box::new(BoolBox::new(true))
|
||||
}
|
||||
|
||||
|
||||
/// リクエストタイムアウト設定
|
||||
pub fn set_timeout(&self, seconds: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let timeout_val = seconds.to_string_box().value.parse::<u64>().unwrap_or(30);
|
||||
*self.timeout_seconds.write().unwrap() = timeout_val;
|
||||
Box::new(BoolBox::new(true))
|
||||
}
|
||||
|
||||
|
||||
/// クライアントリクエスト処理(内部メソッド)
|
||||
fn handle_client_request_with_routes(
|
||||
client_socket: SocketBox,
|
||||
routes: HashMap<String, Box<dyn NyashBox>>
|
||||
client_socket: SocketBox,
|
||||
routes: HashMap<String, Box<dyn NyashBox>>,
|
||||
) {
|
||||
// Read HTTP request
|
||||
let raw_request = client_socket.read_http_request();
|
||||
let request_str = raw_request.to_string_box().value;
|
||||
|
||||
|
||||
if request_str.trim().is_empty() {
|
||||
let _ = client_socket.close();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Parse HTTP request
|
||||
let request = HTTPRequestBox::parse(raw_request);
|
||||
let method = request.get_method().to_string_box().value;
|
||||
let path = request.get_path().to_string_box().value;
|
||||
|
||||
|
||||
println!("📬 {} {}", method, path);
|
||||
|
||||
|
||||
// Find matching route
|
||||
let route_key = format!("{} {}", method, path);
|
||||
let fallback_key = format!("ANY {}", path);
|
||||
|
||||
|
||||
let response = if let Some(_handler) = routes.get(&route_key) {
|
||||
// Found specific method route
|
||||
// TODO: Actual handler invocation would need method calling infrastructure
|
||||
HTTPResponseBox::create_json_response(
|
||||
Box::new(StringBox::new(r#"{"message": "Route found", "method": ""#.to_string() + &method + r#""}"#))
|
||||
)
|
||||
HTTPResponseBox::create_json_response(Box::new(StringBox::new(
|
||||
r#"{"message": "Route found", "method": ""#.to_string() + &method + r#""}"#,
|
||||
)))
|
||||
} else if let Some(_handler) = routes.get(&fallback_key) {
|
||||
// Found generic route
|
||||
HTTPResponseBox::create_json_response(
|
||||
Box::new(StringBox::new(r#"{"message": "Generic route found"}"#))
|
||||
)
|
||||
HTTPResponseBox::create_json_response(Box::new(StringBox::new(
|
||||
r#"{"message": "Generic route found"}"#,
|
||||
)))
|
||||
} else {
|
||||
// No route found - 404
|
||||
HTTPResponseBox::create_404_response()
|
||||
};
|
||||
|
||||
|
||||
// Send response
|
||||
let response_str = response.to_http_string();
|
||||
let _ = client_socket.write(response_str);
|
||||
let _ = client_socket.close();
|
||||
}
|
||||
|
||||
|
||||
/// アクティブ接続数取得
|
||||
pub fn get_active_connections(&self) -> Box<dyn NyashBox> {
|
||||
let connections = self.active_connections.read().unwrap();
|
||||
Box::new(IntegerBox::new(connections.len() as i64))
|
||||
}
|
||||
|
||||
|
||||
/// サーバー状態取得
|
||||
pub fn is_running(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(BoolBox::new(*self.running.read().unwrap()))
|
||||
@ -367,7 +383,7 @@ impl NyashBox for HTTPServerBox {
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
@ -377,9 +393,9 @@ impl NyashBox for HTTPServerBox {
|
||||
let running = *self.running.read().unwrap();
|
||||
let routes_count = self.routes.read().unwrap().len();
|
||||
let connections_count = self.active_connections.read().unwrap().len();
|
||||
|
||||
|
||||
StringBox::new(format!(
|
||||
"HTTPServer(id: {}, running: {}, routes: {}, connections: {})",
|
||||
"HTTPServer(id: {}, running: {}, routes: {}, connections: {})",
|
||||
self.base.id, running, routes_count, connections_count
|
||||
))
|
||||
}
|
||||
@ -401,7 +417,7 @@ impl BoxCore for HTTPServerBox {
|
||||
fn box_id(&self) -> u64 {
|
||||
self.base.id
|
||||
}
|
||||
|
||||
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> {
|
||||
self.base.parent_type_id
|
||||
}
|
||||
@ -410,15 +426,18 @@ impl BoxCore for HTTPServerBox {
|
||||
let running = *self.running.read().unwrap();
|
||||
let routes_count = self.routes.read().unwrap().len();
|
||||
let connections_count = self.active_connections.read().unwrap().len();
|
||||
|
||||
write!(f, "HTTPServer(id: {}, running: {}, routes: {}, connections: {})",
|
||||
self.base.id, running, routes_count, connections_count)
|
||||
|
||||
write!(
|
||||
f,
|
||||
"HTTPServer(id: {}, running: {}, routes: {}, connections: {})",
|
||||
self.base.id, running, routes_count, connections_count
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
@ -436,4 +455,4 @@ impl Drop for HTTPServerBox {
|
||||
// Ensure server is stopped and resources are cleaned up
|
||||
let _ = self.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,29 +1,29 @@
|
||||
/*! 📦 IntentBox - Structured Message Box
|
||||
*
|
||||
*
|
||||
* ## 📝 概要
|
||||
* IntentBoxは構造化メッセージを表現するBoxです。
|
||||
* P2P通信において、メッセージの種類(name)と内容(payload)を
|
||||
* 明確に分離して管理します。
|
||||
*
|
||||
*
|
||||
* ## 🏗️ 設計
|
||||
* - **name**: メッセージの種類 ("chat.message", "file.share"等)
|
||||
* - **payload**: JSON形式の任意データ
|
||||
* - **Arc<Mutex>**: 他のBoxと統一されたメモリ管理パターン
|
||||
*
|
||||
*
|
||||
* ## 🛠️ 利用可能メソッド
|
||||
* - `new(name, payload)` - 構造化メッセージを作成
|
||||
* - `getName()` - メッセージ名を取得
|
||||
* - `getPayload()` - ペイロードを取得
|
||||
* - `setPayload(data)` - ペイロードを更新
|
||||
*
|
||||
*
|
||||
* ## 💡 使用例
|
||||
* ```nyash
|
||||
* // チャットメッセージ
|
||||
* local msg = new IntentBox("chat.message", {
|
||||
* text: "Hello P2P!",
|
||||
* from: "alice"
|
||||
* local msg = new IntentBox("chat.message", {
|
||||
* text: "Hello P2P!",
|
||||
* from: "alice"
|
||||
* })
|
||||
*
|
||||
*
|
||||
* // ファイル共有メッセージ
|
||||
* local file_msg = new IntentBox("file.share", {
|
||||
* filename: "document.pdf",
|
||||
@ -32,10 +32,10 @@
|
||||
* ```
|
||||
*/
|
||||
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
|
||||
use std::any::Any;
|
||||
use std::sync::RwLock;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::RwLock;
|
||||
|
||||
/// IntentBox - 構造化メッセージBox (RwLock pattern)
|
||||
#[derive(Debug)]
|
||||
@ -51,7 +51,7 @@ impl Clone for IntentBox {
|
||||
fn clone(&self) -> Self {
|
||||
let name_val = self.name.read().unwrap().clone();
|
||||
let payload_val = self.payload.read().unwrap().clone();
|
||||
|
||||
|
||||
Self {
|
||||
base: BoxBase::new(), // New unique ID for clone
|
||||
name: RwLock::new(name_val),
|
||||
@ -69,19 +69,19 @@ impl IntentBox {
|
||||
payload: RwLock::new(payload),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// メッセージ名を取得
|
||||
pub fn get_name(&self) -> Box<dyn NyashBox> {
|
||||
let name = self.name.read().unwrap().clone();
|
||||
Box::new(StringBox::new(name))
|
||||
}
|
||||
|
||||
|
||||
/// ペイロードを取得
|
||||
pub fn get_payload(&self) -> Box<dyn NyashBox> {
|
||||
let payload = self.payload.read().unwrap().clone();
|
||||
Box::new(StringBox::new(payload.to_string()))
|
||||
}
|
||||
|
||||
|
||||
/// ペイロードを更新
|
||||
pub fn set_payload(&self, payload: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let payload_str = payload.to_string_box().value;
|
||||
@ -89,8 +89,8 @@ impl IntentBox {
|
||||
Ok(json_val) => {
|
||||
*self.payload.write().unwrap() = json_val;
|
||||
Box::new(BoolBox::new(true))
|
||||
},
|
||||
Err(_) => Box::new(BoolBox::new(false))
|
||||
}
|
||||
Err(_) => Box::new(BoolBox::new(false)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -99,7 +99,7 @@ impl NyashBox for IntentBox {
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
@ -109,7 +109,7 @@ impl NyashBox for IntentBox {
|
||||
let name = self.name.read().unwrap().clone();
|
||||
StringBox::new(format!("IntentBox[{}]", name))
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(other_intent) = other.as_any().downcast_ref::<IntentBox>() {
|
||||
BoolBox::new(self.base.id == other_intent.base.id)
|
||||
@ -117,7 +117,7 @@ impl NyashBox for IntentBox {
|
||||
BoolBox::new(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
"IntentBox"
|
||||
}
|
||||
@ -127,7 +127,7 @@ impl BoxCore for IntentBox {
|
||||
fn box_id(&self) -> u64 {
|
||||
self.base.id
|
||||
}
|
||||
|
||||
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> {
|
||||
self.base.parent_type_id
|
||||
}
|
||||
@ -136,11 +136,11 @@ impl BoxCore for IntentBox {
|
||||
let name = self.name.read().unwrap().clone();
|
||||
write!(f, "IntentBox[{}]", name)
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
@ -151,4 +151,3 @@ impl std::fmt::Display for IntentBox {
|
||||
self.fmt_box(f)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, IntegerBox, VoidBox, BoxCore, BoxBase};
|
||||
use crate::jit::config::JitConfig;
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox, VoidBox};
|
||||
use crate::interpreter::RuntimeError;
|
||||
use crate::jit::config::JitConfig;
|
||||
use std::any::Any;
|
||||
use std::sync::RwLock;
|
||||
|
||||
@ -11,12 +11,19 @@ pub struct JitConfigBox {
|
||||
}
|
||||
|
||||
impl JitConfigBox {
|
||||
pub fn new() -> Self { Self { base: BoxBase::new(), config: RwLock::new(JitConfig::from_env()) } }
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
base: BoxBase::new(),
|
||||
config: RwLock::new(JitConfig::from_env()),
|
||||
}
|
||||
}
|
||||
/// Update internal config flags from runtime capability probe
|
||||
pub fn from_runtime_probe(&self) -> Box<dyn NyashBox> {
|
||||
let caps = crate::jit::config::probe_capabilities();
|
||||
let mut cfg = self.config.write().unwrap();
|
||||
if cfg.native_bool_abi && !caps.supports_b1_sig { cfg.native_bool_abi = false; }
|
||||
if cfg.native_bool_abi && !caps.supports_b1_sig {
|
||||
cfg.native_bool_abi = false;
|
||||
}
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn set_flag(&self, name: &str, on: bool) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
@ -34,7 +41,11 @@ impl JitConfigBox {
|
||||
"bool_abi" | "native_bool_abi" => cfg.native_bool_abi = on,
|
||||
"ret_b1" | "ret_bool_b1" => cfg.ret_bool_b1 = on,
|
||||
"relax_numeric" | "hostcall_relax_numeric" => cfg.relax_numeric = on,
|
||||
_ => return Err(RuntimeError::InvalidOperation { message: format!("Unknown flag: {}", name) }),
|
||||
_ => {
|
||||
return Err(RuntimeError::InvalidOperation {
|
||||
message: format!("Unknown flag: {}", name),
|
||||
})
|
||||
}
|
||||
}
|
||||
Ok(Box::new(VoidBox::new()))
|
||||
}
|
||||
@ -53,19 +64,28 @@ impl JitConfigBox {
|
||||
"bool_abi" | "native_bool_abi" => cfg.native_bool_abi,
|
||||
"ret_b1" | "ret_bool_b1" => cfg.ret_bool_b1,
|
||||
"relax_numeric" | "hostcall_relax_numeric" => cfg.relax_numeric,
|
||||
_ => return Err(RuntimeError::InvalidOperation { message: format!("Unknown flag: {}", name) }),
|
||||
_ => {
|
||||
return Err(RuntimeError::InvalidOperation {
|
||||
message: format!("Unknown flag: {}", name),
|
||||
})
|
||||
}
|
||||
};
|
||||
Ok(Box::new(BoolBox::new(b)))
|
||||
}
|
||||
pub fn set_threshold(&self, v: i64) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
let mut cfg = self.config.write().unwrap();
|
||||
if v <= 0 { cfg.threshold = None; }
|
||||
else { cfg.threshold = Some(v as u32); }
|
||||
if v <= 0 {
|
||||
cfg.threshold = None;
|
||||
} else {
|
||||
cfg.threshold = Some(v as u32);
|
||||
}
|
||||
Ok(Box::new(VoidBox::new()))
|
||||
}
|
||||
pub fn get_threshold(&self) -> Box<dyn NyashBox> {
|
||||
let cfg = self.config.read().unwrap();
|
||||
Box::new(IntegerBox::new(cfg.threshold.map(|v| v as i64).unwrap_or(0)))
|
||||
Box::new(IntegerBox::new(
|
||||
cfg.threshold.map(|v| v as i64).unwrap_or(0),
|
||||
))
|
||||
}
|
||||
pub fn to_json(&self) -> Box<dyn NyashBox> {
|
||||
let cfg = self.config.read().unwrap();
|
||||
@ -88,20 +108,49 @@ impl JitConfigBox {
|
||||
}
|
||||
pub fn from_json(&self, s: &str) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
let mut cfg = self.config.write().unwrap();
|
||||
let v: serde_json::Value = serde_json::from_str(s).map_err(|e| RuntimeError::InvalidOperation { message: format!("Invalid JSON: {}", e) })?;
|
||||
if let Some(b) = v.get("exec").and_then(|x| x.as_bool()) { cfg.exec = b; }
|
||||
if let Some(b) = v.get("stats").and_then(|x| x.as_bool()) { cfg.stats = b; }
|
||||
if let Some(b) = v.get("stats_json").and_then(|x| x.as_bool()) { cfg.stats_json = b; }
|
||||
if let Some(b) = v.get("dump").and_then(|x| x.as_bool()) { cfg.dump = b; }
|
||||
if let Some(n) = v.get("threshold").and_then(|x| x.as_u64()) { cfg.threshold = Some(n as u32); }
|
||||
if let Some(b) = v.get("phi_min").and_then(|x| x.as_bool()) { cfg.phi_min = b; }
|
||||
if let Some(b) = v.get("hostcall").and_then(|x| x.as_bool()) { cfg.hostcall = b; }
|
||||
if let Some(b) = v.get("handle_debug").and_then(|x| x.as_bool()) { cfg.handle_debug = b; }
|
||||
if let Some(b) = v.get("native_f64").and_then(|x| x.as_bool()) { cfg.native_f64 = b; }
|
||||
if let Some(b) = v.get("native_bool").and_then(|x| x.as_bool()) { cfg.native_bool = b; }
|
||||
if let Some(b) = v.get("native_bool_abi").and_then(|x| x.as_bool()) { cfg.native_bool_abi = b; }
|
||||
if let Some(b) = v.get("ret_bool_b1").and_then(|x| x.as_bool()) { cfg.ret_bool_b1 = b; }
|
||||
if let Some(b) = v.get("relax_numeric").and_then(|x| x.as_bool()) { cfg.relax_numeric = b; }
|
||||
let v: serde_json::Value =
|
||||
serde_json::from_str(s).map_err(|e| RuntimeError::InvalidOperation {
|
||||
message: format!("Invalid JSON: {}", e),
|
||||
})?;
|
||||
if let Some(b) = v.get("exec").and_then(|x| x.as_bool()) {
|
||||
cfg.exec = b;
|
||||
}
|
||||
if let Some(b) = v.get("stats").and_then(|x| x.as_bool()) {
|
||||
cfg.stats = b;
|
||||
}
|
||||
if let Some(b) = v.get("stats_json").and_then(|x| x.as_bool()) {
|
||||
cfg.stats_json = b;
|
||||
}
|
||||
if let Some(b) = v.get("dump").and_then(|x| x.as_bool()) {
|
||||
cfg.dump = b;
|
||||
}
|
||||
if let Some(n) = v.get("threshold").and_then(|x| x.as_u64()) {
|
||||
cfg.threshold = Some(n as u32);
|
||||
}
|
||||
if let Some(b) = v.get("phi_min").and_then(|x| x.as_bool()) {
|
||||
cfg.phi_min = b;
|
||||
}
|
||||
if let Some(b) = v.get("hostcall").and_then(|x| x.as_bool()) {
|
||||
cfg.hostcall = b;
|
||||
}
|
||||
if let Some(b) = v.get("handle_debug").and_then(|x| x.as_bool()) {
|
||||
cfg.handle_debug = b;
|
||||
}
|
||||
if let Some(b) = v.get("native_f64").and_then(|x| x.as_bool()) {
|
||||
cfg.native_f64 = b;
|
||||
}
|
||||
if let Some(b) = v.get("native_bool").and_then(|x| x.as_bool()) {
|
||||
cfg.native_bool = b;
|
||||
}
|
||||
if let Some(b) = v.get("native_bool_abi").and_then(|x| x.as_bool()) {
|
||||
cfg.native_bool_abi = b;
|
||||
}
|
||||
if let Some(b) = v.get("ret_bool_b1").and_then(|x| x.as_bool()) {
|
||||
cfg.ret_bool_b1 = b;
|
||||
}
|
||||
if let Some(b) = v.get("relax_numeric").and_then(|x| x.as_bool()) {
|
||||
cfg.relax_numeric = b;
|
||||
}
|
||||
Ok(Box::new(VoidBox::new()))
|
||||
}
|
||||
pub fn apply(&self) -> Box<dyn NyashBox> {
|
||||
@ -124,20 +173,41 @@ impl JitConfigBox {
|
||||
}
|
||||
|
||||
impl BoxCore for JitConfigBox {
|
||||
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, "JitConfigBox") }
|
||||
fn as_any(&self) -> &dyn Any { self }
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any { self }
|
||||
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, "JitConfigBox")
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl NyashBox for JitConfigBox {
|
||||
fn to_string_box(&self) -> StringBox { StringBox::new(self.summary().to_string_box().value) }
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<JitConfigBox>()) }
|
||||
fn type_name(&self) -> &'static str { "JitConfigBox" }
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new(self.summary().to_string_box().value)
|
||||
}
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
BoolBox::new(other.as_any().is::<JitConfigBox>())
|
||||
}
|
||||
fn type_name(&self) -> &'static str {
|
||||
"JitConfigBox"
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
let cfg = self.config.read().unwrap().clone();
|
||||
Box::new(JitConfigBox { base: self.base.clone(), config: RwLock::new(cfg) })
|
||||
Box::new(JitConfigBox {
|
||||
base: self.base.clone(),
|
||||
config: RwLock::new(cfg),
|
||||
})
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||
}
|
||||
|
||||
@ -1,25 +1,55 @@
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, VoidBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox, VoidBox};
|
||||
use std::any::Any;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct JitEventsBox { base: BoxBase }
|
||||
pub struct JitEventsBox {
|
||||
base: BoxBase,
|
||||
}
|
||||
|
||||
impl JitEventsBox { pub fn new() -> Self { Self { base: BoxBase::new() } } }
|
||||
impl JitEventsBox {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BoxCore for JitEventsBox {
|
||||
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, "JitEventsBox") }
|
||||
fn as_any(&self) -> &dyn Any { self }
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any { self }
|
||||
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, "JitEventsBox")
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl NyashBox for JitEventsBox {
|
||||
fn to_string_box(&self) -> StringBox { StringBox::new("JitEventsBox") }
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<JitEventsBox>()) }
|
||||
fn type_name(&self) -> &'static str { "JitEventsBox" }
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self { base: self.base.clone() }) }
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new("JitEventsBox")
|
||||
}
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
BoolBox::new(other.as_any().is::<JitEventsBox>())
|
||||
}
|
||||
fn type_name(&self) -> &'static str {
|
||||
"JitEventsBox"
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(Self {
|
||||
base: self.base.clone(),
|
||||
})
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
}
|
||||
|
||||
impl JitEventsBox {
|
||||
@ -28,14 +58,17 @@ impl JitEventsBox {
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn enable(&self, on: bool) -> Box<dyn NyashBox> {
|
||||
if on { std::env::set_var("NYASH_JIT_EVENTS", "1"); }
|
||||
else { std::env::remove_var("NYASH_JIT_EVENTS"); }
|
||||
if on {
|
||||
std::env::set_var("NYASH_JIT_EVENTS", "1");
|
||||
} else {
|
||||
std::env::remove_var("NYASH_JIT_EVENTS");
|
||||
}
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn log(&self, kind: &str, function: &str, note_json: &str) -> Box<dyn NyashBox> {
|
||||
let extra = serde_json::from_str::<serde_json::Value>(note_json).unwrap_or_else(|_| serde_json::json!({"note": note_json}));
|
||||
let extra = serde_json::from_str::<serde_json::Value>(note_json)
|
||||
.unwrap_or_else(|_| serde_json::json!({"note": note_json}));
|
||||
crate::jit::events::emit(kind, function, None, None, extra);
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,17 +1,35 @@
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, VoidBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox, VoidBox};
|
||||
use std::any::Any;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct JitHostcallRegistryBox { base: BoxBase }
|
||||
pub struct JitHostcallRegistryBox {
|
||||
base: BoxBase,
|
||||
}
|
||||
|
||||
impl JitHostcallRegistryBox { pub fn new() -> Self { Self { base: BoxBase::new() } } }
|
||||
impl JitHostcallRegistryBox {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BoxCore for JitHostcallRegistryBox {
|
||||
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, "JitHostcallRegistryBox") }
|
||||
fn as_any(&self) -> &dyn Any { self }
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any { self }
|
||||
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, "JitHostcallRegistryBox")
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl NyashBox for JitHostcallRegistryBox {
|
||||
@ -20,18 +38,41 @@ impl NyashBox for JitHostcallRegistryBox {
|
||||
let payload = serde_json::json!({ "readonly": ro, "mutating": mu });
|
||||
StringBox::new(payload.to_string())
|
||||
}
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<JitHostcallRegistryBox>()) }
|
||||
fn type_name(&self) -> &'static str { "JitHostcallRegistryBox" }
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self { base: self.base.clone() }) }
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
BoolBox::new(other.as_any().is::<JitHostcallRegistryBox>())
|
||||
}
|
||||
fn type_name(&self) -> &'static str {
|
||||
"JitHostcallRegistryBox"
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(Self {
|
||||
base: self.base.clone(),
|
||||
})
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
}
|
||||
|
||||
impl JitHostcallRegistryBox {
|
||||
pub fn add_readonly(&self, sym: &str) -> Box<dyn NyashBox> { crate::jit::hostcall_registry::add_readonly(sym); Box::new(VoidBox::new()) }
|
||||
pub fn add_mutating(&self, sym: &str) -> Box<dyn NyashBox> { crate::jit::hostcall_registry::add_mutating(sym); Box::new(VoidBox::new()) }
|
||||
pub fn set_from_csv(&self, ro_csv: &str, mu_csv: &str) -> Box<dyn NyashBox> { crate::jit::hostcall_registry::set_from_csv(ro_csv, mu_csv); Box::new(VoidBox::new()) }
|
||||
pub fn add_readonly(&self, sym: &str) -> Box<dyn NyashBox> {
|
||||
crate::jit::hostcall_registry::add_readonly(sym);
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn add_mutating(&self, sym: &str) -> Box<dyn NyashBox> {
|
||||
crate::jit::hostcall_registry::add_mutating(sym);
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn set_from_csv(&self, ro_csv: &str, mu_csv: &str) -> Box<dyn NyashBox> {
|
||||
crate::jit::hostcall_registry::set_from_csv(ro_csv, mu_csv);
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn add_signature(&self, sym: &str, args_csv: &str, ret_str: &str) -> Box<dyn NyashBox> {
|
||||
let ok = crate::jit::hostcall_registry::set_signature_csv(sym, args_csv, ret_str);
|
||||
if ok { Box::new(VoidBox::new()) } else { Box::new(StringBox::new("Invalid signature")) }
|
||||
if ok {
|
||||
Box::new(VoidBox::new())
|
||||
} else {
|
||||
Box::new(StringBox::new("Invalid signature"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,29 +1,61 @@
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, VoidBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox, VoidBox};
|
||||
use std::any::Any;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct JitPolicyBox { base: BoxBase }
|
||||
pub struct JitPolicyBox {
|
||||
base: BoxBase,
|
||||
}
|
||||
|
||||
impl JitPolicyBox { pub fn new() -> Self { Self { base: BoxBase::new() } } }
|
||||
impl JitPolicyBox {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BoxCore for JitPolicyBox {
|
||||
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, "JitPolicyBox") }
|
||||
fn as_any(&self) -> &dyn Any { self }
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any { self }
|
||||
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, "JitPolicyBox")
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl NyashBox for JitPolicyBox {
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
let p = crate::jit::policy::current();
|
||||
let s = format!("read_only={} whitelist={}", p.read_only, p.hostcall_whitelist.join(","));
|
||||
let s = format!(
|
||||
"read_only={} whitelist={}",
|
||||
p.read_only,
|
||||
p.hostcall_whitelist.join(",")
|
||||
);
|
||||
StringBox::new(s)
|
||||
}
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<JitPolicyBox>()) }
|
||||
fn type_name(&self) -> &'static str { "JitPolicyBox" }
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self { base: self.base.clone() }) }
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
BoolBox::new(other.as_any().is::<JitPolicyBox>())
|
||||
}
|
||||
fn type_name(&self) -> &'static str {
|
||||
"JitPolicyBox"
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(Self {
|
||||
base: self.base.clone(),
|
||||
})
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
}
|
||||
|
||||
// Methods (exposed via VM dispatch):
|
||||
@ -32,19 +64,26 @@ impl JitPolicyBox {
|
||||
let mut cur = crate::jit::policy::current();
|
||||
match name {
|
||||
"read_only" | "readonly" => cur.read_only = on,
|
||||
_ => return Box::new(StringBox::new(format!("Unknown flag: {}", name)))
|
||||
_ => return Box::new(StringBox::new(format!("Unknown flag: {}", name))),
|
||||
}
|
||||
crate::jit::policy::set_current(cur);
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn get_flag(&self, name: &str) -> Box<dyn NyashBox> {
|
||||
let cur = crate::jit::policy::current();
|
||||
let v = match name { "read_only" | "readonly" => cur.read_only, _ => false };
|
||||
let v = match name {
|
||||
"read_only" | "readonly" => cur.read_only,
|
||||
_ => false,
|
||||
};
|
||||
Box::new(BoolBox::new(v))
|
||||
}
|
||||
pub fn set_whitelist_csv(&self, csv: &str) -> Box<dyn NyashBox> {
|
||||
let mut cur = crate::jit::policy::current();
|
||||
cur.hostcall_whitelist = csv.split(',').map(|t| t.trim().to_string()).filter(|s| !s.is_empty()).collect();
|
||||
cur.hostcall_whitelist = csv
|
||||
.split(',')
|
||||
.map(|t| t.trim().to_string())
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect();
|
||||
crate::jit::policy::set_current(cur);
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
@ -89,9 +128,15 @@ impl JitPolicyBox {
|
||||
crate::jit::r#extern::collections::SYM_ARRAY_SET_H,
|
||||
crate::jit::r#extern::collections::SYM_MAP_SET_H,
|
||||
];
|
||||
for id in ids { if !cur.hostcall_whitelist.iter().any(|s| s == id) { cur.hostcall_whitelist.push(id.to_string()); } }
|
||||
for id in ids {
|
||||
if !cur.hostcall_whitelist.iter().any(|s| s == id) {
|
||||
cur.hostcall_whitelist.push(id.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Box::new(StringBox::new(format!("Unknown preset: {}", name)));
|
||||
}
|
||||
_ => { return Box::new(StringBox::new(format!("Unknown preset: {}", name))); }
|
||||
}
|
||||
crate::jit::policy::set_current(cur);
|
||||
Box::new(VoidBox::new())
|
||||
|
||||
@ -1,15 +1,25 @@
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
|
||||
use std::any::Any;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct JitStatsBox { base: BoxBase }
|
||||
pub struct JitStatsBox {
|
||||
base: BoxBase,
|
||||
}
|
||||
|
||||
impl JitStatsBox {
|
||||
pub fn new() -> Self { Self { base: BoxBase::new() } }
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
pub fn to_json(&self) -> Box<dyn NyashBox> {
|
||||
let cfg = crate::jit::config::current();
|
||||
let caps = crate::jit::config::probe_capabilities();
|
||||
let mode = if cfg.native_bool_abi && caps.supports_b1_sig { "b1_bool" } else { "i64_bool" };
|
||||
let mode = if cfg.native_bool_abi && caps.supports_b1_sig {
|
||||
"b1_bool"
|
||||
} else {
|
||||
"i64_bool"
|
||||
};
|
||||
let payload = serde_json::json!({
|
||||
"version": 1,
|
||||
"abi_mode": mode,
|
||||
@ -25,17 +35,37 @@ impl JitStatsBox {
|
||||
}
|
||||
|
||||
impl BoxCore for JitStatsBox {
|
||||
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, "JitStatsBox") }
|
||||
fn as_any(&self) -> &dyn Any { self }
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any { self }
|
||||
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, "JitStatsBox")
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl NyashBox for JitStatsBox {
|
||||
fn to_string_box(&self) -> StringBox { StringBox::new(self.to_json().to_string_box().value) }
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<JitStatsBox>()) }
|
||||
fn type_name(&self) -> &'static str { "JitStatsBox" }
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(self.clone()) }
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new(self.to_json().to_string_box().value)
|
||||
}
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
BoolBox::new(other.as_any().is::<JitStatsBox>())
|
||||
}
|
||||
fn type_name(&self) -> &'static str {
|
||||
"JitStatsBox"
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,25 +1,55 @@
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, VoidBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox, VoidBox};
|
||||
use std::any::Any;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct JitStrictBox { base: BoxBase }
|
||||
pub struct JitStrictBox {
|
||||
base: BoxBase,
|
||||
}
|
||||
|
||||
impl JitStrictBox { pub fn new() -> Self { Self { base: BoxBase::new() } } }
|
||||
impl JitStrictBox {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BoxCore for JitStrictBox {
|
||||
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, "JitStrictBox") }
|
||||
fn as_any(&self) -> &dyn Any { self }
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any { self }
|
||||
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, "JitStrictBox")
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl NyashBox for JitStrictBox {
|
||||
fn to_string_box(&self) -> StringBox { StringBox::new("JitStrictBox".to_string()) }
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<JitStrictBox>()) }
|
||||
fn type_name(&self) -> &'static str { "JitStrictBox" }
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self { base: self.base.clone() }) }
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new("JitStrictBox".to_string())
|
||||
}
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
BoolBox::new(other.as_any().is::<JitStrictBox>())
|
||||
}
|
||||
fn type_name(&self) -> &'static str {
|
||||
"JitStrictBox"
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(Self {
|
||||
base: self.base.clone(),
|
||||
})
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
}
|
||||
|
||||
impl JitStrictBox {
|
||||
@ -27,8 +57,12 @@ impl JitStrictBox {
|
||||
pub fn enable(&self, on: bool) -> Box<dyn NyashBox> {
|
||||
if on {
|
||||
std::env::set_var("NYASH_JIT_STRICT", "1");
|
||||
if std::env::var("NYASH_JIT_ONLY").ok().is_none() { std::env::set_var("NYASH_JIT_ONLY", "1"); }
|
||||
if std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().is_none() { std::env::set_var("NYASH_JIT_ARGS_HANDLE_ONLY", "1"); }
|
||||
if std::env::var("NYASH_JIT_ONLY").ok().is_none() {
|
||||
std::env::set_var("NYASH_JIT_ONLY", "1");
|
||||
}
|
||||
if std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().is_none() {
|
||||
std::env::set_var("NYASH_JIT_ARGS_HANDLE_ONLY", "1");
|
||||
}
|
||||
} else {
|
||||
std::env::remove_var("NYASH_JIT_STRICT");
|
||||
}
|
||||
@ -36,11 +70,19 @@ impl JitStrictBox {
|
||||
}
|
||||
|
||||
pub fn set_only(&self, on: bool) -> Box<dyn NyashBox> {
|
||||
if on { std::env::set_var("NYASH_JIT_ONLY", "1"); } else { std::env::remove_var("NYASH_JIT_ONLY"); }
|
||||
if on {
|
||||
std::env::set_var("NYASH_JIT_ONLY", "1");
|
||||
} else {
|
||||
std::env::remove_var("NYASH_JIT_ONLY");
|
||||
}
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn set_handle_only(&self, on: bool) -> Box<dyn NyashBox> {
|
||||
if on { std::env::set_var("NYASH_JIT_ARGS_HANDLE_ONLY", "1"); } else { std::env::remove_var("NYASH_JIT_ARGS_HANDLE_ONLY"); }
|
||||
if on {
|
||||
std::env::set_var("NYASH_JIT_ARGS_HANDLE_ONLY", "1");
|
||||
} else {
|
||||
std::env::remove_var("NYASH_JIT_ARGS_HANDLE_ONLY");
|
||||
}
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
|
||||
@ -60,4 +102,3 @@ impl JitStrictBox {
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2,12 +2,12 @@
|
||||
// Nyashの箱システムによるJSON解析・生成を提供します。
|
||||
// 参考: 既存Boxの設計思想
|
||||
|
||||
use crate::box_trait::{NyashBox, BoxCore, BoxBase, StringBox, BoolBox, IntegerBox};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox};
|
||||
use crate::boxes::array::ArrayBox;
|
||||
use crate::boxes::map_box::MapBox;
|
||||
use serde_json::{Error, Value};
|
||||
use std::any::Any;
|
||||
use std::sync::RwLock;
|
||||
use serde_json::{Value, Error};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct JSONBox {
|
||||
@ -18,7 +18,7 @@ pub struct JSONBox {
|
||||
impl Clone for JSONBox {
|
||||
fn clone(&self) -> Self {
|
||||
let value_clone = self.value.read().unwrap().clone();
|
||||
|
||||
|
||||
Self {
|
||||
value: RwLock::new(value_clone),
|
||||
base: BoxBase::new(), // New unique ID for clone
|
||||
@ -29,24 +29,24 @@ impl Clone for JSONBox {
|
||||
impl JSONBox {
|
||||
pub fn from_str(s: &str) -> Result<Self, Error> {
|
||||
let value = serde_json::from_str(s)?;
|
||||
Ok(JSONBox {
|
||||
value: RwLock::new(value),
|
||||
base: BoxBase::new()
|
||||
Ok(JSONBox {
|
||||
value: RwLock::new(value),
|
||||
base: BoxBase::new(),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
pub fn new(value: Value) -> Self {
|
||||
JSONBox {
|
||||
value: RwLock::new(value),
|
||||
base: BoxBase::new()
|
||||
JSONBox {
|
||||
value: RwLock::new(value),
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn to_string(&self) -> String {
|
||||
let value = self.value.read().unwrap();
|
||||
value.to_string()
|
||||
}
|
||||
|
||||
|
||||
/// JSONパース
|
||||
pub fn parse(data: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let json_str = data.to_string_box().value;
|
||||
@ -55,17 +55,17 @@ impl JSONBox {
|
||||
Err(e) => Box::new(StringBox::new(&format!("Error parsing JSON: {}", e))),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// JSON文字列化
|
||||
pub fn stringify(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(StringBox::new(&self.to_string()))
|
||||
}
|
||||
|
||||
|
||||
/// 値取得
|
||||
pub fn get(&self, key: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let key_str = key.to_string_box().value;
|
||||
let value = self.value.read().unwrap();
|
||||
|
||||
|
||||
if let Some(obj) = value.as_object() {
|
||||
if let Some(val) = obj.get(&key_str) {
|
||||
json_value_to_nyash_box(val)
|
||||
@ -86,14 +86,14 @@ impl JSONBox {
|
||||
Box::new(crate::boxes::null_box::NullBox::new())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 値設定
|
||||
pub fn set(&self, key: Box<dyn NyashBox>, new_value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let key_str = key.to_string_box().value;
|
||||
let mut value = self.value.write().unwrap();
|
||||
|
||||
|
||||
let json_value = nyash_box_to_json_value(new_value);
|
||||
|
||||
|
||||
if let Some(obj) = value.as_object_mut() {
|
||||
obj.insert(key_str, json_value);
|
||||
Box::new(StringBox::new("ok"))
|
||||
@ -101,31 +101,31 @@ impl JSONBox {
|
||||
Box::new(StringBox::new("Error: JSONBox is not an object"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// キー存在チェック
|
||||
pub fn has(&self, key: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let key_str = key.to_string_box().value;
|
||||
let value = self.value.read().unwrap();
|
||||
|
||||
|
||||
if let Some(obj) = value.as_object() {
|
||||
Box::new(BoolBox::new(obj.contains_key(&key_str)))
|
||||
} else {
|
||||
Box::new(BoolBox::new(false))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// すべてのキーを取得
|
||||
pub fn keys(&self) -> Box<dyn NyashBox> {
|
||||
let value = self.value.read().unwrap();
|
||||
let array = ArrayBox::new();
|
||||
|
||||
|
||||
if let Some(obj) = value.as_object() {
|
||||
for key in obj.keys() {
|
||||
// ArrayBoxのpushメソッドは&selfなので、直接呼び出し可能
|
||||
let _ = array.push(Box::new(StringBox::new(key)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Box::new(array)
|
||||
}
|
||||
}
|
||||
@ -134,11 +134,11 @@ impl BoxCore for JSONBox {
|
||||
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 {
|
||||
let value = self.value.read().unwrap();
|
||||
let json_type = match *value {
|
||||
@ -148,18 +148,18 @@ impl BoxCore for JSONBox {
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
@ -175,7 +175,7 @@ impl NyashBox for JSONBox {
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
@ -186,12 +186,10 @@ impl NyashBox for JSONBox {
|
||||
StringBox::new(value.to_string())
|
||||
}
|
||||
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
"JSONBox"
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(other_json) = other.as_any().downcast_ref::<JSONBox>() {
|
||||
let self_value = self.value.read().unwrap();
|
||||
@ -230,10 +228,7 @@ fn json_value_to_nyash_box(value: &Value) -> Box<dyn NyashBox> {
|
||||
Value::Object(obj) => {
|
||||
let map_box = MapBox::new();
|
||||
for (key, val) in obj {
|
||||
map_box.set(
|
||||
Box::new(StringBox::new(key)),
|
||||
json_value_to_nyash_box(val)
|
||||
);
|
||||
map_box.set(Box::new(StringBox::new(key)), json_value_to_nyash_box(val));
|
||||
}
|
||||
Box::new(map_box)
|
||||
}
|
||||
@ -242,7 +237,11 @@ fn json_value_to_nyash_box(value: &Value) -> Box<dyn NyashBox> {
|
||||
|
||||
/// NyashBox を JSON Value に変換
|
||||
fn nyash_box_to_json_value(value: Box<dyn NyashBox>) -> Value {
|
||||
if value.as_any().downcast_ref::<crate::boxes::null_box::NullBox>().is_some() {
|
||||
if value
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::null_box::NullBox>()
|
||||
.is_some()
|
||||
{
|
||||
Value::Null
|
||||
} else if let Some(bool_box) = value.as_any().downcast_ref::<BoolBox>() {
|
||||
Value::Bool(bool_box.value)
|
||||
@ -259,7 +258,8 @@ fn nyash_box_to_json_value(value: Box<dyn NyashBox>) -> Value {
|
||||
Value::String(string_box.value.clone())
|
||||
} else if let Some(array_box) = value.as_any().downcast_ref::<ArrayBox>() {
|
||||
let items = array_box.items.read().unwrap();
|
||||
let arr: Vec<Value> = items.iter()
|
||||
let arr: Vec<Value> = items
|
||||
.iter()
|
||||
.map(|item| nyash_box_to_json_value(item.clone_box()))
|
||||
.collect();
|
||||
Value::Array(arr)
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
/*! 🗄️ MapBox - キー値ストレージBox
|
||||
*
|
||||
*
|
||||
* ## 📝 概要
|
||||
* 高性能キー値ストレージを提供するBox。
|
||||
* JavaScript Map、Python dict、C# Dictionaryと同等機能。
|
||||
* 動的データ管理やキャッシュ実装に最適。
|
||||
*
|
||||
*
|
||||
* ## 🛠️ 利用可能メソッド
|
||||
* - `set(key, value)` - キー値ペア設定
|
||||
* - `get(key)` - 値取得
|
||||
@ -15,21 +15,21 @@
|
||||
* - `values()` - 全値取得
|
||||
* - `size()` - データ数取得
|
||||
* - `isEmpty()` - 空判定
|
||||
*
|
||||
*
|
||||
* ## 💡 使用例
|
||||
* ```nyash
|
||||
* local map, result
|
||||
* map = new MapBox()
|
||||
*
|
||||
*
|
||||
* // データ設定
|
||||
* map.set("name", "Alice")
|
||||
* map.set("age", 25)
|
||||
* map.set("active", true)
|
||||
*
|
||||
*
|
||||
* // データ取得
|
||||
* result = map.get("name") // "Alice"
|
||||
* print("User: " + result)
|
||||
*
|
||||
*
|
||||
* // 存在確認
|
||||
* if (map.has("email")) {
|
||||
* print("Email: " + map.get("email"))
|
||||
@ -37,7 +37,7 @@
|
||||
* print("No email registered")
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* ## 🎮 実用例 - ゲーム設定管理
|
||||
* ```nyash
|
||||
* static box GameConfig {
|
||||
@ -68,7 +68,7 @@
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* ## 🔍 キャッシュ実装例
|
||||
* ```nyash
|
||||
* static box APICache {
|
||||
@ -95,7 +95,7 @@
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* ## ⚠️ 注意
|
||||
* - キーは自動的に文字列変換される
|
||||
* - スレッドセーフ (Arc<RwLock>使用)
|
||||
@ -103,41 +103,45 @@
|
||||
* - 存在しないキーの取得は "Key not found" メッセージ返却
|
||||
*/
|
||||
|
||||
use crate::box_trait::{BoxCore, BoxBase, NyashBox, StringBox, IntegerBox, BoolBox};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox};
|
||||
use crate::boxes::ArrayBox;
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, RwLock}; // Arc追加
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::sync::{Arc, RwLock}; // Arc追加
|
||||
|
||||
/// キーバリューストアを表すBox
|
||||
pub struct MapBox {
|
||||
data: Arc<RwLock<HashMap<String, Box<dyn NyashBox>>>>, // Arc追加
|
||||
data: Arc<RwLock<HashMap<String, Box<dyn NyashBox>>>>, // Arc追加
|
||||
base: BoxBase,
|
||||
}
|
||||
|
||||
impl MapBox {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
data: Arc::new(RwLock::new(HashMap::new())), // Arc::new追加
|
||||
data: Arc::new(RwLock::new(HashMap::new())), // Arc::new追加
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 値を設定
|
||||
pub fn set(&self, key: Box<dyn NyashBox>, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let key_str = key.to_string_box().value;
|
||||
self.data.write().unwrap().insert(key_str.clone(), value);
|
||||
Box::new(StringBox::new(&format!("Set key: {}", key_str)))
|
||||
}
|
||||
|
||||
|
||||
/// 値を取得
|
||||
pub fn get(&self, key: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let key_str = key.to_string_box().value;
|
||||
match self.data.read().unwrap().get(&key_str) {
|
||||
Some(value) => {
|
||||
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||
if value.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some() {
|
||||
if value
|
||||
.as_any()
|
||||
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
|
||||
.is_some()
|
||||
{
|
||||
return value.share_box();
|
||||
}
|
||||
value.clone_box()
|
||||
@ -145,13 +149,15 @@ impl MapBox {
|
||||
None => Box::new(StringBox::new(&format!("Key not found: {}", key_str))),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// キーが存在するかチェック
|
||||
pub fn has(&self, key: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let key_str = key.to_string_box().value;
|
||||
Box::new(BoolBox::new(self.data.read().unwrap().contains_key(&key_str)))
|
||||
Box::new(BoolBox::new(
|
||||
self.data.read().unwrap().contains_key(&key_str),
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
/// キーを削除
|
||||
pub fn delete(&self, key: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let key_str = key.to_string_box().value;
|
||||
@ -160,7 +166,7 @@ impl MapBox {
|
||||
None => Box::new(StringBox::new(&format!("Key not found: {}", key_str))),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 全てのキーを取得
|
||||
pub fn keys(&self) -> Box<dyn NyashBox> {
|
||||
let keys: Vec<String> = self.data.read().unwrap().keys().cloned().collect();
|
||||
@ -170,10 +176,13 @@ impl MapBox {
|
||||
}
|
||||
Box::new(array)
|
||||
}
|
||||
|
||||
|
||||
/// 全ての値を取得
|
||||
pub fn values(&self) -> Box<dyn NyashBox> {
|
||||
let values: Vec<Box<dyn NyashBox>> = self.data.read().unwrap()
|
||||
let values: Vec<Box<dyn NyashBox>> = self
|
||||
.data
|
||||
.read()
|
||||
.unwrap()
|
||||
.values()
|
||||
.map(|v| v.clone_box())
|
||||
.collect();
|
||||
@ -183,45 +192,46 @@ impl MapBox {
|
||||
}
|
||||
Box::new(array)
|
||||
}
|
||||
|
||||
|
||||
/// サイズを取得
|
||||
pub fn size(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(IntegerBox::new(self.data.read().unwrap().len() as i64))
|
||||
}
|
||||
|
||||
|
||||
/// 全てクリア
|
||||
pub fn clear(&self) -> Box<dyn NyashBox> {
|
||||
self.data.write().unwrap().clear();
|
||||
Box::new(StringBox::new("Map cleared"))
|
||||
}
|
||||
|
||||
|
||||
/// 各要素に対して関数を実行
|
||||
pub fn forEach(&self, _callback: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
// 簡易実装:callbackの実行はスキップ
|
||||
let count = self.data.read().unwrap().len();
|
||||
Box::new(StringBox::new(&format!("Iterated over {} items", count)))
|
||||
}
|
||||
|
||||
|
||||
/// JSON文字列に変換
|
||||
pub fn toJSON(&self) -> Box<dyn NyashBox> {
|
||||
let data = self.data.read().unwrap();
|
||||
let mut json_parts = Vec::new();
|
||||
|
||||
|
||||
for (key, value) in data.iter() {
|
||||
let value_str = value.to_string_box().value;
|
||||
// 値が数値の場合はそのまま、文字列の場合は引用符で囲む
|
||||
let formatted_value = if value.as_any().downcast_ref::<IntegerBox>().is_some()
|
||||
|| value.as_any().downcast_ref::<BoolBox>().is_some() {
|
||||
let formatted_value = if value.as_any().downcast_ref::<IntegerBox>().is_some()
|
||||
|| value.as_any().downcast_ref::<BoolBox>().is_some()
|
||||
{
|
||||
value_str
|
||||
} else {
|
||||
format!("\"{}\"", value_str.replace("\"", "\\\""))
|
||||
};
|
||||
json_parts.push(format!("\"{}\":{}", key, formatted_value));
|
||||
}
|
||||
|
||||
|
||||
Box::new(StringBox::new(&format!("{{{}}}", json_parts.join(","))))
|
||||
}
|
||||
|
||||
|
||||
/// 内部データへのアクセス(JSONBox用)
|
||||
pub fn get_data(&self) -> &RwLock<HashMap<String, Box<dyn NyashBox>>> {
|
||||
&self.data
|
||||
@ -233,11 +243,12 @@ impl Clone for MapBox {
|
||||
fn clone(&self) -> Self {
|
||||
// ディープコピー(独立インスタンス)
|
||||
let data_guard = self.data.read().unwrap();
|
||||
let cloned_data: HashMap<String, Box<dyn NyashBox>> = data_guard.iter()
|
||||
.map(|(k, v)| (k.clone(), v.clone_box())) // 要素もディープコピー
|
||||
let cloned_data: HashMap<String, Box<dyn NyashBox>> = data_guard
|
||||
.iter()
|
||||
.map(|(k, v)| (k.clone(), v.clone_box())) // 要素もディープコピー
|
||||
.collect();
|
||||
MapBox {
|
||||
data: Arc::new(RwLock::new(cloned_data)), // 新しいArc
|
||||
data: Arc::new(RwLock::new(cloned_data)), // 新しいArc
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
@ -247,50 +258,51 @@ impl BoxCore for MapBox {
|
||||
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 {
|
||||
let size = self.data.read().unwrap().len();
|
||||
write!(f, "MapBox(size={})", size)
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl NyashBox for MapBox {
|
||||
fn is_identity(&self) -> bool { true }
|
||||
fn is_identity(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn type_name(&self) -> &'static str {
|
||||
"MapBox"
|
||||
}
|
||||
|
||||
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
let size = self.data.read().unwrap().len();
|
||||
StringBox::new(&format!("MapBox(size={})", size))
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 🎯 状態共有の核心実装
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
let new_instance = MapBox {
|
||||
data: Arc::clone(&self.data), // Arcクローンで状態共有
|
||||
base: BoxBase::new(), // 新しいID
|
||||
data: Arc::clone(&self.data), // Arcクローンで状態共有
|
||||
base: BoxBase::new(), // 新しいID
|
||||
};
|
||||
Box::new(new_instance)
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(other_map) = other.as_any().downcast_ref::<MapBox>() {
|
||||
// 同じインスタンスかチェック(データの共有を考慮)
|
||||
@ -299,7 +311,6 @@ impl NyashBox for MapBox {
|
||||
BoolBox::new(false)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Display for MapBox {
|
||||
|
||||
@ -1,54 +1,54 @@
|
||||
/*! 🧮 MathBox - 数学計算Box
|
||||
*
|
||||
*
|
||||
* ## 📝 概要
|
||||
* 高度な数学演算を提供するBox。Python mathモジュールや
|
||||
* JavaScript Math オブジェクトと同様の機能を提供。
|
||||
*
|
||||
*
|
||||
* ## 🛠️ 利用可能メソッド
|
||||
*
|
||||
*
|
||||
* ### 🔢 基本計算
|
||||
* - `abs(value)` - 絶対値
|
||||
* - `max(a, b)` - 最大値
|
||||
* - `min(a, b)` - 最小値
|
||||
* - `pow(base, exp)` - 累乗 (base^exp)
|
||||
* - `sqrt(value)` - 平方根
|
||||
*
|
||||
*
|
||||
* ### 📐 三角関数
|
||||
* - `sin(radians)` - 正弦
|
||||
* - `cos(radians)` - 余弦
|
||||
* - `tan(radians)` - 正接
|
||||
*
|
||||
*
|
||||
* ### 📊 対数・指数関数
|
||||
* - `log(value)` - 自然対数 (ln)
|
||||
* - `log10(value)` - 常用対数
|
||||
* - `exp(value)` - 指数関数 (e^x)
|
||||
*
|
||||
*
|
||||
* ### 🔄 丸め関数
|
||||
* - `floor(value)` - 切り下げ
|
||||
* - `ceil(value)` - 切り上げ
|
||||
* - `round(value)` - 四捨五入
|
||||
*
|
||||
*
|
||||
* ### 📏 定数取得
|
||||
* - `getPi()` - 円周率π (3.14159...)
|
||||
* - `getE()` - 自然対数の底e (2.71828...)
|
||||
*
|
||||
*
|
||||
* ## 💡 使用例
|
||||
* ```nyash
|
||||
* local math, result
|
||||
* math = new MathBox()
|
||||
*
|
||||
*
|
||||
* result = math.abs(-42) // 42
|
||||
* result = math.max(10, 25) // 25
|
||||
* result = math.sqrt(16) // 4.0
|
||||
* result = math.pow(2, 3) // 8.0
|
||||
* result = math.sin(math.getPi() / 2) // 1.0
|
||||
*
|
||||
*
|
||||
* // 計算例
|
||||
* local pi, area
|
||||
* pi = math.getPi()
|
||||
* area = pi * math.pow(5, 2) // 半径5の円の面積
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* ## ⚠️ 注意
|
||||
* - 三角関数の引数はラジアン
|
||||
* - 負数の平方根・対数はエラー
|
||||
@ -56,9 +56,9 @@
|
||||
* - 整数演算は自動でFloatBoxに変換される場合あり
|
||||
*/
|
||||
|
||||
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, BoxCore, BoxBase};
|
||||
use std::fmt::{Debug, Display};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox};
|
||||
use std::any::Any;
|
||||
use std::fmt::{Debug, Display};
|
||||
|
||||
/// 数学演算を提供するBox
|
||||
#[derive(Debug, Clone)]
|
||||
@ -68,11 +68,11 @@ pub struct MathBox {
|
||||
|
||||
impl MathBox {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
base: BoxBase::new()
|
||||
Self {
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 絶対値を計算
|
||||
pub fn abs(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
|
||||
@ -83,46 +83,46 @@ impl MathBox {
|
||||
Box::new(StringBox::new("Error: abs() requires numeric input"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 最大値を返す
|
||||
pub fn max(&self, a: Box<dyn NyashBox>, b: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let (Some(a_int), Some(b_int)) = (
|
||||
a.as_any().downcast_ref::<IntegerBox>(),
|
||||
b.as_any().downcast_ref::<IntegerBox>()
|
||||
b.as_any().downcast_ref::<IntegerBox>(),
|
||||
) {
|
||||
Box::new(IntegerBox::new(a_int.value.max(b_int.value)))
|
||||
} else if let (Some(a_float), Some(b_float)) = (
|
||||
a.as_any().downcast_ref::<FloatBox>(),
|
||||
b.as_any().downcast_ref::<FloatBox>()
|
||||
b.as_any().downcast_ref::<FloatBox>(),
|
||||
) {
|
||||
Box::new(FloatBox::new(a_float.value.max(b_float.value)))
|
||||
} else {
|
||||
Box::new(StringBox::new("Error: max() requires numeric inputs"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 最小値を返す
|
||||
pub fn min(&self, a: Box<dyn NyashBox>, b: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let (Some(a_int), Some(b_int)) = (
|
||||
a.as_any().downcast_ref::<IntegerBox>(),
|
||||
b.as_any().downcast_ref::<IntegerBox>()
|
||||
b.as_any().downcast_ref::<IntegerBox>(),
|
||||
) {
|
||||
Box::new(IntegerBox::new(a_int.value.min(b_int.value)))
|
||||
} else if let (Some(a_float), Some(b_float)) = (
|
||||
a.as_any().downcast_ref::<FloatBox>(),
|
||||
b.as_any().downcast_ref::<FloatBox>()
|
||||
b.as_any().downcast_ref::<FloatBox>(),
|
||||
) {
|
||||
Box::new(FloatBox::new(a_float.value.min(b_float.value)))
|
||||
} else {
|
||||
Box::new(StringBox::new("Error: min() requires numeric inputs"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 累乗を計算
|
||||
pub fn pow(&self, base: Box<dyn NyashBox>, exp: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let (Some(base_int), Some(exp_int)) = (
|
||||
base.as_any().downcast_ref::<IntegerBox>(),
|
||||
exp.as_any().downcast_ref::<IntegerBox>()
|
||||
exp.as_any().downcast_ref::<IntegerBox>(),
|
||||
) {
|
||||
if exp_int.value >= 0 {
|
||||
let result = (base_int.value as f64).powi(exp_int.value as i32);
|
||||
@ -134,7 +134,7 @@ impl MathBox {
|
||||
Box::new(StringBox::new("Error: pow() requires numeric inputs"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 平方根を計算
|
||||
pub fn sqrt(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
|
||||
@ -153,19 +153,19 @@ impl MathBox {
|
||||
Box::new(StringBox::new("Error: sqrt() requires numeric input"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 円周率πを返す
|
||||
#[allow(non_snake_case)]
|
||||
pub fn getPi(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(FloatBox::new(std::f64::consts::PI))
|
||||
}
|
||||
|
||||
|
||||
/// 自然対数の底eを返す
|
||||
#[allow(non_snake_case)]
|
||||
pub fn getE(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(FloatBox::new(std::f64::consts::E))
|
||||
}
|
||||
|
||||
|
||||
/// サイン(正弦)
|
||||
pub fn sin(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
|
||||
@ -176,7 +176,7 @@ impl MathBox {
|
||||
Box::new(StringBox::new("Error: sin() requires numeric input"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// コサイン(余弦)
|
||||
pub fn cos(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
|
||||
@ -187,7 +187,7 @@ impl MathBox {
|
||||
Box::new(StringBox::new("Error: cos() requires numeric input"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// タンジェント(正接)
|
||||
pub fn tan(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
|
||||
@ -198,7 +198,7 @@ impl MathBox {
|
||||
Box::new(StringBox::new("Error: tan() requires numeric input"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 自然対数
|
||||
pub fn log(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
|
||||
@ -217,7 +217,7 @@ impl MathBox {
|
||||
Box::new(StringBox::new("Error: log() requires numeric input"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 常用対数(底10)
|
||||
pub fn log10(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
|
||||
@ -236,7 +236,7 @@ impl MathBox {
|
||||
Box::new(StringBox::new("Error: log10() requires numeric input"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 指数関数(e^x)
|
||||
pub fn exp(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
|
||||
@ -247,33 +247,33 @@ impl MathBox {
|
||||
Box::new(StringBox::new("Error: exp() requires numeric input"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 床関数(切り下げ)
|
||||
pub fn floor(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
|
||||
Box::new(IntegerBox::new(int_box.value)) // 整数はそのまま
|
||||
Box::new(IntegerBox::new(int_box.value)) // 整数はそのまま
|
||||
} else if let Some(float_box) = value.as_any().downcast_ref::<FloatBox>() {
|
||||
Box::new(IntegerBox::new(float_box.value.floor() as i64))
|
||||
} else {
|
||||
Box::new(StringBox::new("Error: floor() requires numeric input"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 天井関数(切り上げ)
|
||||
pub fn ceil(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
|
||||
Box::new(IntegerBox::new(int_box.value)) // 整数はそのまま
|
||||
Box::new(IntegerBox::new(int_box.value)) // 整数はそのまま
|
||||
} else if let Some(float_box) = value.as_any().downcast_ref::<FloatBox>() {
|
||||
Box::new(IntegerBox::new(float_box.value.ceil() as i64))
|
||||
} else {
|
||||
Box::new(StringBox::new("Error: ceil() requires numeric input"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 四捨五入
|
||||
pub fn round(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
|
||||
Box::new(IntegerBox::new(int_box.value)) // 整数はそのまま
|
||||
Box::new(IntegerBox::new(int_box.value)) // 整数はそのまま
|
||||
} else if let Some(float_box) = value.as_any().downcast_ref::<FloatBox>() {
|
||||
Box::new(IntegerBox::new(float_box.value.round() as i64))
|
||||
} else {
|
||||
@ -286,19 +286,19 @@ impl BoxCore for MathBox {
|
||||
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, "MathBox()")
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
@ -308,20 +308,20 @@ impl NyashBox for MathBox {
|
||||
fn type_name(&self) -> &'static str {
|
||||
"MathBox"
|
||||
}
|
||||
|
||||
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new("MathBox()")
|
||||
}
|
||||
|
||||
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(other_math) = other.as_any().downcast_ref::<MathBox>() {
|
||||
BoolBox::new(self.box_id() == other_math.box_id())
|
||||
@ -329,7 +329,6 @@ impl NyashBox for MathBox {
|
||||
BoolBox::new(false)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Display for MathBox {
|
||||
@ -347,9 +346,9 @@ pub struct FloatBox {
|
||||
|
||||
impl FloatBox {
|
||||
pub fn new(value: f64) -> Self {
|
||||
Self {
|
||||
value,
|
||||
base: BoxBase::new()
|
||||
Self {
|
||||
value,
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -358,19 +357,19 @@ impl BoxCore for FloatBox {
|
||||
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
|
||||
}
|
||||
@ -380,20 +379,20 @@ impl NyashBox for FloatBox {
|
||||
fn type_name(&self) -> &'static str {
|
||||
"FloatBox"
|
||||
}
|
||||
|
||||
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new(&self.value.to_string())
|
||||
}
|
||||
|
||||
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(other_float) = other.as_any().downcast_ref::<FloatBox>() {
|
||||
BoolBox::new((self.value - other_float.value).abs() < f64::EPSILON)
|
||||
@ -403,7 +402,6 @@ impl NyashBox for FloatBox {
|
||||
BoolBox::new(false)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Display for FloatBox {
|
||||
@ -423,19 +421,19 @@ pub struct RangeBox {
|
||||
|
||||
impl RangeBox {
|
||||
pub fn new(start: i64, end: i64, step: i64) -> Self {
|
||||
Self {
|
||||
start,
|
||||
end,
|
||||
step,
|
||||
base: BoxBase::new()
|
||||
Self {
|
||||
start,
|
||||
end,
|
||||
step,
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// イテレータとして値を生成
|
||||
pub fn iter(&self) -> Vec<i64> {
|
||||
let mut result = Vec::new();
|
||||
let mut current = self.start;
|
||||
|
||||
|
||||
if self.step > 0 {
|
||||
while current < self.end {
|
||||
result.push(current);
|
||||
@ -447,7 +445,7 @@ impl RangeBox {
|
||||
current += self.step;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
@ -456,19 +454,19 @@ impl BoxCore for RangeBox {
|
||||
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, "Range({}, {}, {})", self.start, self.end, self.step)
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
@ -478,36 +476,38 @@ impl NyashBox for RangeBox {
|
||||
fn type_name(&self) -> &'static str {
|
||||
"RangeBox"
|
||||
}
|
||||
|
||||
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new(&format!("Range({}, {}, {})", self.start, self.end, self.step))
|
||||
StringBox::new(&format!(
|
||||
"Range({}, {}, {})",
|
||||
self.start, self.end, self.step
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(other_range) = other.as_any().downcast_ref::<RangeBox>() {
|
||||
BoolBox::new(
|
||||
self.start == other_range.start &&
|
||||
self.end == other_range.end &&
|
||||
self.step == other_range.step
|
||||
self.start == other_range.start
|
||||
&& self.end == other_range.end
|
||||
&& self.step == other_range.step,
|
||||
)
|
||||
} else {
|
||||
BoolBox::new(false)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Display for RangeBox {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.fmt_box(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
128
src/boxes/mod.rs
128
src/boxes/mod.rs
@ -1,38 +1,38 @@
|
||||
/*! 🎯 Nyash Box実装モジュール
|
||||
* Everything is Box哲学に基づく各Box型の実装
|
||||
*
|
||||
*
|
||||
* ## 📦 利用可能なBox一覧
|
||||
*
|
||||
*
|
||||
* ### 🔤 基本データ型Box
|
||||
* - **StringBox**: 文字列操作 - `"Hello".length()`, `str.split(",")`
|
||||
* - **IntegerBox**: 整数計算 - `42.add(8)`, `num.toString()`
|
||||
* - **BoolBox**: 真偽値 - `true.not()`, `flag.toString()`
|
||||
*
|
||||
*
|
||||
* ### 🧮 計算・ユーティリティBox
|
||||
* - **MathBox**: 数学関数 - `Math.sin(x)`, `Math.random()`
|
||||
* - **TimeBox**: 時間操作 - `Time.now()`, `time.format()`
|
||||
* - **RandomBox**: 乱数生成 - `Random.int(10)`, `Random.choice(array)`
|
||||
*
|
||||
*
|
||||
* ### 🖥️ システム・IO Box
|
||||
* - **ConsoleBox**: コンソール出力 - `console.log()`, `console.error()`
|
||||
* - **DebugBox**: デバッグ支援 - `debug.trace()`, `debug.memory()`
|
||||
* - **SoundBox**: 音声再生 - `sound.beep()`, `sound.play(file)`
|
||||
*
|
||||
*
|
||||
* ### 🗄️ コレクション・データBox
|
||||
* - **MapBox**: キー値ストレージ - `map.set(key, val)`, `map.get(key)`
|
||||
* - **NullBox**: NULL値表現 - `null.toString()` → "void"
|
||||
*
|
||||
*
|
||||
* ### 🖼️ GUI・グラフィックBox
|
||||
* - **EguiBox**: デスクトップGUI - `gui.setTitle()`, `gui.run()`
|
||||
*
|
||||
*
|
||||
* ### 🌐 Web専用Box (WASM環境)
|
||||
* - **WebDisplayBox**: HTML表示 - `display.show(html)`
|
||||
* - **WebConsoleBox**: ブラウザコンソール - `webConsole.log()`
|
||||
* - **WebCanvasBox**: Canvas描画 - `canvas.drawRect()`
|
||||
*
|
||||
*
|
||||
* ### 🔗 通信・ネットワークBox
|
||||
* - **SimpleIntentBox**: P2P通信 - `intent.send()`, `intent.on()`
|
||||
*
|
||||
*
|
||||
* ## 💡 使用例
|
||||
* ```nyash
|
||||
* // 基本的な使い方
|
||||
@ -40,7 +40,7 @@
|
||||
* str = "Nyash"
|
||||
* num = 42
|
||||
* result = str.concat(" v") + num.toString()
|
||||
*
|
||||
*
|
||||
* // GUIアプリ作成
|
||||
* local app
|
||||
* app = new EguiBox()
|
||||
@ -53,41 +53,41 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
// 各Boxモジュールを宣言
|
||||
pub mod string_box;
|
||||
pub mod integer_box;
|
||||
pub mod bool_box;
|
||||
pub mod math_box;
|
||||
pub mod time_box;
|
||||
pub mod debug_box;
|
||||
pub mod integer_box;
|
||||
pub mod math_box;
|
||||
pub mod random_box;
|
||||
pub mod string_box;
|
||||
pub mod time_box;
|
||||
// These boxes use web APIs that require special handling in WASM
|
||||
pub mod aot_compiler_box;
|
||||
pub mod aot_config_box;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod timer_box;
|
||||
pub mod audio_box;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod canvas_event_box;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod canvas_loop_box;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod audio_box;
|
||||
pub mod console_box;
|
||||
pub mod debug_config_box;
|
||||
pub mod function_box;
|
||||
pub mod gc_config_box;
|
||||
pub mod jit_config_box;
|
||||
pub mod jit_events_box;
|
||||
pub mod jit_hostcall_registry_box;
|
||||
pub mod jit_policy_box;
|
||||
pub mod jit_stats_box;
|
||||
pub mod jit_strict_box;
|
||||
pub mod map_box;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod qr_box;
|
||||
pub mod sound_box;
|
||||
pub mod map_box;
|
||||
pub mod console_box;
|
||||
pub mod jit_config_box;
|
||||
pub mod jit_stats_box;
|
||||
pub mod jit_policy_box;
|
||||
pub mod jit_events_box;
|
||||
pub mod jit_strict_box;
|
||||
pub mod jit_hostcall_registry_box;
|
||||
pub mod debug_config_box;
|
||||
pub mod gc_config_box;
|
||||
pub mod aot_config_box;
|
||||
pub mod aot_compiler_box;
|
||||
pub mod task_group_box;
|
||||
pub mod token_box;
|
||||
pub mod function_box;
|
||||
pub mod ref_cell_box;
|
||||
pub mod sound_box;
|
||||
pub mod task_group_box;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod timer_box;
|
||||
pub mod token_box;
|
||||
|
||||
// Web専用Box群(ブラウザ環境でのみ利用可能)
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
@ -99,34 +99,34 @@ pub mod egui_box;
|
||||
|
||||
// 共通で使う型とトレイトを再エクスポート
|
||||
// pub use string_box::StringBox; // レガシー実装、box_trait::StringBoxを使用すること
|
||||
// pub use integer_box::IntegerBox; // レガシー実装、box_trait::IntegerBoxを使用すること
|
||||
// pub use integer_box::IntegerBox; // レガシー実装、box_trait::IntegerBoxを使用すること
|
||||
// pub use bool_box::BoolBox; // レガシー実装、box_trait::BoolBoxを使用すること
|
||||
pub use math_box::{MathBox, FloatBox};
|
||||
pub use time_box::{TimeBox, DateTimeBox};
|
||||
pub use debug_box::DebugBox;
|
||||
pub use random_box::RandomBox;
|
||||
pub use aot_compiler_box::AotCompilerBox;
|
||||
pub use aot_config_box::AotConfigBox;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub use timer_box::TimerBox;
|
||||
pub use audio_box::AudioBox;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub use canvas_event_box::CanvasEventBox;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub use canvas_loop_box::CanvasLoopBox;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub use audio_box::AudioBox;
|
||||
pub use console_box::ConsoleBox;
|
||||
pub use debug_box::DebugBox;
|
||||
pub use jit_config_box::JitConfigBox;
|
||||
pub use jit_events_box::JitEventsBox;
|
||||
pub use jit_hostcall_registry_box::JitHostcallRegistryBox;
|
||||
pub use jit_policy_box::JitPolicyBox;
|
||||
pub use jit_stats_box::JitStatsBox;
|
||||
pub use jit_strict_box::JitStrictBox;
|
||||
pub use map_box::MapBox;
|
||||
pub use math_box::{FloatBox, MathBox};
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub use qr_box::QRBox;
|
||||
pub use random_box::RandomBox;
|
||||
pub use sound_box::SoundBox;
|
||||
pub use map_box::MapBox;
|
||||
pub use console_box::ConsoleBox;
|
||||
pub use jit_config_box::JitConfigBox;
|
||||
pub use jit_stats_box::JitStatsBox;
|
||||
pub use jit_policy_box::JitPolicyBox;
|
||||
pub use jit_events_box::JitEventsBox;
|
||||
pub use jit_strict_box::JitStrictBox;
|
||||
pub use jit_hostcall_registry_box::JitHostcallRegistryBox;
|
||||
pub use aot_config_box::AotConfigBox;
|
||||
pub use aot_compiler_box::AotCompilerBox;
|
||||
pub use task_group_box::TaskGroupBox;
|
||||
pub use time_box::{DateTimeBox, TimeBox};
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub use timer_box::TimerBox;
|
||||
pub use token_box::TokenBox;
|
||||
|
||||
// EguiBoxの再エクスポート(非WASM環境のみ)
|
||||
@ -135,7 +135,7 @@ pub use egui_box::EguiBox;
|
||||
|
||||
// Web Box群の再エクスポート(WASM環境のみ)
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub use web::{WebDisplayBox, WebConsoleBox, WebCanvasBox};
|
||||
pub use web::{WebCanvasBox, WebConsoleBox, WebDisplayBox};
|
||||
|
||||
pub mod null_box;
|
||||
|
||||
@ -144,35 +144,35 @@ pub mod array;
|
||||
pub mod buffer;
|
||||
pub mod file;
|
||||
pub mod future;
|
||||
pub mod json;
|
||||
pub mod result;
|
||||
pub mod http;
|
||||
pub mod stream;
|
||||
pub mod regex;
|
||||
pub mod socket_box;
|
||||
pub mod http_message_box;
|
||||
pub mod http_server_box;
|
||||
pub mod json;
|
||||
pub mod regex;
|
||||
pub mod result;
|
||||
pub mod socket_box;
|
||||
pub mod stream;
|
||||
|
||||
// P2P通信Box群 (NEW! - Completely rewritten)
|
||||
pub mod intent_box;
|
||||
pub mod p2p_box;
|
||||
|
||||
// null関数も再エクスポート
|
||||
pub use null_box::{NullBox, null};
|
||||
pub use null_box::{null, NullBox};
|
||||
|
||||
// High-priority Box types re-export
|
||||
pub use array::ArrayBox;
|
||||
pub use buffer::BufferBox;
|
||||
pub use file::FileBox;
|
||||
pub use future::{NyashFutureBox, FutureBox, FutureWeak};
|
||||
pub use json::JSONBox;
|
||||
pub use result::{NyashResultBox, ResultBox};
|
||||
pub use future::{FutureBox, FutureWeak, NyashFutureBox};
|
||||
pub use http::HttpClientBox;
|
||||
pub use stream::{NyashStreamBox, StreamBox};
|
||||
pub use regex::RegexBox;
|
||||
pub use socket_box::SocketBox;
|
||||
pub use http_message_box::{HTTPRequestBox, HTTPResponseBox};
|
||||
pub use http_server_box::HTTPServerBox;
|
||||
pub use json::JSONBox;
|
||||
pub use regex::RegexBox;
|
||||
pub use result::{NyashResultBox, ResultBox};
|
||||
pub use socket_box::SocketBox;
|
||||
pub use stream::{NyashStreamBox, StreamBox};
|
||||
|
||||
// P2P通信Boxの再エクスポート
|
||||
pub use intent_box::IntentBox;
|
||||
|
||||
@ -1,37 +1,37 @@
|
||||
/*! 🚫 NullBox - NULL値表現Box
|
||||
*
|
||||
*
|
||||
* ## 📝 概要
|
||||
* null/void値を表現する特別なBox。
|
||||
* JavaScript null、Python None、C# nullと同等の機能を提供。
|
||||
* NULL安全プログラミングをサポート。
|
||||
*
|
||||
*
|
||||
* ## 🛠️ 利用可能メソッド
|
||||
* - `isNull()` - null判定 (常にtrue)
|
||||
* - `isNotNull()` - 非null判定 (常にfalse)
|
||||
* - `toString()` - 文字列変換 ("null")
|
||||
* - `equals(other)` - 等価比較 (他のnullとのみtrue)
|
||||
*
|
||||
*
|
||||
* ## 🛡️ 静的メソッド (null安全機能)
|
||||
* - `NullBox.checkNull(value)` - 値のnull判定
|
||||
* - `NullBox.checkNotNull(value)` - 値の非null判定
|
||||
* - `NullBox.getOrDefault(value, default)` - null時デフォルト値取得
|
||||
*
|
||||
*
|
||||
* ## 💡 使用例
|
||||
* ```nyash
|
||||
* local user, name, default_name
|
||||
*
|
||||
*
|
||||
* // null値の作成と判定
|
||||
* user = null
|
||||
* if (user == null) {
|
||||
* print("User is null")
|
||||
* }
|
||||
*
|
||||
*
|
||||
* // null安全な値取得
|
||||
* name = getUsername() // null の可能性
|
||||
* default_name = NullBox.getOrDefault(name, "Anonymous")
|
||||
* print("Hello, " + default_name)
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* ## 🎮 実用例 - null安全プログラミング
|
||||
* ```nyash
|
||||
* static box UserManager {
|
||||
@ -62,12 +62,12 @@
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* ## 🔍 デバッグ活用
|
||||
* ```nyash
|
||||
* local data, result
|
||||
* data = fetchDataFromAPI() // null になる可能性
|
||||
*
|
||||
*
|
||||
* // null チェック付きデバッグ
|
||||
* if (NullBox.checkNull(data)) {
|
||||
* print("Warning: API returned null data")
|
||||
@ -76,7 +76,7 @@
|
||||
* result = data.process()
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* ## ⚠️ 重要な特徴
|
||||
* - `null == null` は常にtrue
|
||||
* - `null.toString()` は "null"
|
||||
@ -84,9 +84,9 @@
|
||||
* - メソッド呼び出し時のnullチェックでNullPointerException防止
|
||||
*/
|
||||
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
|
||||
use std::fmt::{Debug, Display};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
|
||||
use std::any::Any;
|
||||
use std::fmt::{Debug, Display};
|
||||
|
||||
/// null値を表現するBox
|
||||
#[derive(Debug, Clone)]
|
||||
@ -96,36 +96,33 @@ pub struct NullBox {
|
||||
|
||||
impl NullBox {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
base: BoxBase::new()
|
||||
Self {
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// null値かどうかを判定
|
||||
pub fn is_null(&self) -> bool {
|
||||
true // NullBoxは常にnull
|
||||
true // NullBoxは常にnull
|
||||
}
|
||||
|
||||
|
||||
/// 値がnullでないかを判定
|
||||
pub fn is_not_null(&self) -> bool {
|
||||
false // NullBoxは常にnull
|
||||
false // NullBoxは常にnull
|
||||
}
|
||||
|
||||
|
||||
/// 他の値がnullかどうかを判定
|
||||
pub fn check_null(value: &dyn NyashBox) -> bool {
|
||||
value.as_any().downcast_ref::<NullBox>().is_some()
|
||||
}
|
||||
|
||||
|
||||
/// 他の値がnullでないかを判定
|
||||
pub fn check_not_null(value: &dyn NyashBox) -> bool {
|
||||
!Self::check_null(value)
|
||||
}
|
||||
|
||||
|
||||
/// null安全な値の取得
|
||||
pub fn get_or_default(
|
||||
value: &dyn NyashBox,
|
||||
default: Box<dyn NyashBox>
|
||||
) -> Box<dyn NyashBox> {
|
||||
pub fn get_or_default(value: &dyn NyashBox, default: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if Self::check_null(value) {
|
||||
default
|
||||
} else {
|
||||
@ -138,19 +135,19 @@ impl BoxCore for NullBox {
|
||||
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, "null")
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
@ -160,25 +157,24 @@ impl NyashBox for NullBox {
|
||||
fn type_name(&self) -> &'static str {
|
||||
"NullBox"
|
||||
}
|
||||
|
||||
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new("null")
|
||||
}
|
||||
|
||||
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
// すべてのNullBoxは等しい
|
||||
BoolBox::new(other.as_any().downcast_ref::<NullBox>().is_some())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Display for NullBox {
|
||||
@ -196,7 +192,7 @@ pub fn null() -> Box<dyn NyashBox> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::box_trait::IntegerBox;
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_null_creation() {
|
||||
let null_box = NullBox::new();
|
||||
@ -204,41 +200,41 @@ mod tests {
|
||||
assert!(!null_box.is_not_null());
|
||||
assert_eq!(null_box.to_string_box().value, "null");
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_null_check() {
|
||||
let null_box = null();
|
||||
let int_box = Box::new(IntegerBox::new(42));
|
||||
|
||||
|
||||
assert!(NullBox::check_null(null_box.as_ref()));
|
||||
assert!(!NullBox::check_null(int_box.as_ref()));
|
||||
|
||||
|
||||
assert!(!NullBox::check_not_null(null_box.as_ref()));
|
||||
assert!(NullBox::check_not_null(int_box.as_ref()));
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_null_equality() {
|
||||
let null1 = NullBox::new();
|
||||
let null2 = NullBox::new();
|
||||
let int_box = IntegerBox::new(42);
|
||||
|
||||
|
||||
assert!(null1.equals(&null2).value);
|
||||
assert!(!null1.equals(&int_box).value);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_get_or_default() {
|
||||
let null_box = null();
|
||||
let default_value = Box::new(IntegerBox::new(100));
|
||||
let actual_value = Box::new(IntegerBox::new(42));
|
||||
|
||||
|
||||
// nullの場合はデフォルト値を返す
|
||||
let result1 = NullBox::get_or_default(null_box.as_ref(), default_value.clone());
|
||||
assert_eq!(result1.to_string_box().value, "100");
|
||||
|
||||
|
||||
// null以外の場合は元の値を返す
|
||||
let result2 = NullBox::get_or_default(actual_value.as_ref(), default_value);
|
||||
assert_eq!(result2.to_string_box().value, "42");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,49 +1,49 @@
|
||||
/*! 📡 P2PBox - Modern P2P Communication Node
|
||||
*
|
||||
*
|
||||
* ## 📝 概要
|
||||
* P2PBoxは現代的なP2P通信ノードを表現するBoxです。
|
||||
* 新しいアーキテクチャ(IntentBox + MessageBus + Transport)を使用し、
|
||||
* 構造化メッセージによる安全で明示的な通信を実現します。
|
||||
*
|
||||
*
|
||||
* ## 🎯 AI大会議決定事項準拠
|
||||
* - **個別送信のみ**: `send(to, message)` 固定API
|
||||
* - **ブロードキャスト除外**: 安全性のため完全除外
|
||||
* - **明示的API**: 関数オーバーロード不採用
|
||||
* - **構造化メッセージ**: IntentBox (name + payload) 使用
|
||||
*
|
||||
*
|
||||
* ## 🛠️ 利用可能メソッド
|
||||
* - `new(node_id, transport)` - ノードを作成
|
||||
* - `send(to, intent)` - 特定ノードにメッセージ送信
|
||||
* - `on(intent_name, handler)` - イベントリスナー登録
|
||||
* - `getNodeId()` - ノードID取得
|
||||
* - `isReachable(node_id)` - ノード到達可能性確認
|
||||
*
|
||||
*
|
||||
* ## 💡 使用例
|
||||
* ```nyash
|
||||
* // ノード作成
|
||||
* local alice = new P2PBox("alice", "inprocess")
|
||||
* local bob = new P2PBox("bob", "inprocess")
|
||||
*
|
||||
*
|
||||
* // 受信ハンドラ登録
|
||||
* bob.on("chat.message", function(intent, from) {
|
||||
* print("From " + from + ": " + intent.payload.text)
|
||||
* })
|
||||
*
|
||||
*
|
||||
* // メッセージ送信
|
||||
* local msg = new IntentBox("chat.message", { text: "Hello P2P!" })
|
||||
* alice.send("bob", msg)
|
||||
* ```
|
||||
*/
|
||||
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
|
||||
use crate::boxes::result::ResultBox;
|
||||
use crate::boxes::IntentBox;
|
||||
use crate::method_box::MethodBox;
|
||||
use crate::boxes::result::ResultBox;
|
||||
use crate::transport::{Transport, InProcessTransport};
|
||||
use crate::transport::{InProcessTransport, Transport};
|
||||
use std::any::Any;
|
||||
use std::sync::{RwLock, Arc};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
/// P2PBox - P2P通信ノード (RwLock pattern)
|
||||
#[derive(Debug)]
|
||||
@ -72,7 +72,7 @@ impl Clone for P2PBox {
|
||||
let handlers_val = HashMap::new(); // Start fresh for cloned instance
|
||||
let last_from_val = self.last_from.read().unwrap().clone();
|
||||
let last_intent_val = self.last_intent_name.read().unwrap().clone();
|
||||
|
||||
|
||||
Self {
|
||||
base: BoxBase::new(), // New unique ID for clone
|
||||
node_id: RwLock::new(node_id_val),
|
||||
@ -93,7 +93,7 @@ pub enum TransportKind {
|
||||
|
||||
impl std::str::FromStr for TransportKind {
|
||||
type Err = String;
|
||||
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s.to_lowercase().as_str() {
|
||||
"inprocess" => Ok(TransportKind::InProcess),
|
||||
@ -136,28 +136,38 @@ impl P2PBox {
|
||||
{
|
||||
if let Ok(mut t) = transport_arc_outer.write() {
|
||||
let transport_arc_for_cb = Arc::clone(&transport_arc_outer);
|
||||
t.register_intent_handler("sys.ping", Box::new(move |env| {
|
||||
if let Ok(mut lf) = last_from.write() { *lf = Some(env.from.clone()); }
|
||||
if let Ok(mut li) = last_intent.write() { *li = Some(env.intent.get_name().to_string_box().value); }
|
||||
// Reply asynchronously to avoid deep call stacks
|
||||
let to = env.from.clone();
|
||||
let reply = crate::boxes::IntentBox::new("sys.pong".to_string(), serde_json::json!({}));
|
||||
let transport_arc = Arc::clone(&transport_arc_for_cb);
|
||||
std::thread::spawn(move || {
|
||||
// slight delay to avoid lock contention and ordering races
|
||||
std::thread::sleep(std::time::Duration::from_millis(3));
|
||||
if let Ok(transport) = transport_arc.read() {
|
||||
let _ = transport.send(&to, reply, Default::default());
|
||||
t.register_intent_handler(
|
||||
"sys.ping",
|
||||
Box::new(move |env| {
|
||||
if let Ok(mut lf) = last_from.write() {
|
||||
*lf = Some(env.from.clone());
|
||||
}
|
||||
});
|
||||
}));
|
||||
if let Ok(mut li) = last_intent.write() {
|
||||
*li = Some(env.intent.get_name().to_string_box().value);
|
||||
}
|
||||
// Reply asynchronously to avoid deep call stacks
|
||||
let to = env.from.clone();
|
||||
let reply = crate::boxes::IntentBox::new(
|
||||
"sys.pong".to_string(),
|
||||
serde_json::json!({}),
|
||||
);
|
||||
let transport_arc = Arc::clone(&transport_arc_for_cb);
|
||||
std::thread::spawn(move || {
|
||||
// slight delay to avoid lock contention and ordering races
|
||||
std::thread::sleep(std::time::Duration::from_millis(3));
|
||||
if let Ok(transport) = transport_arc.read() {
|
||||
let _ = transport.send(&to, reply, Default::default());
|
||||
}
|
||||
});
|
||||
}),
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
p2p
|
||||
}
|
||||
|
||||
|
||||
/// ノードIDを取得
|
||||
pub fn get_node_id(&self) -> Box<dyn NyashBox> {
|
||||
let node_id = self.node_id.read().unwrap().clone();
|
||||
@ -177,14 +187,17 @@ impl P2PBox {
|
||||
|
||||
// Register temporary transport-level handler for sys.pong
|
||||
if let Ok(mut t) = self.transport.write() {
|
||||
t.register_intent_handler("sys.pong", Box::new(move |env| {
|
||||
if active_cb.load(Ordering::SeqCst) {
|
||||
// record last receive for visibility
|
||||
// Note: we cannot access self here safely; rely on tx notify only
|
||||
let _ = env; // suppress unused
|
||||
let _ = tx.send(());
|
||||
}
|
||||
}));
|
||||
t.register_intent_handler(
|
||||
"sys.pong",
|
||||
Box::new(move |env| {
|
||||
if active_cb.load(Ordering::SeqCst) {
|
||||
// record last receive for visibility
|
||||
// Note: we cannot access self here safely; rely on tx notify only
|
||||
let _ = env; // suppress unused
|
||||
let _ = tx.send(());
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
// Send sys.ping
|
||||
let ping = IntentBox::new("sys.ping".to_string(), serde_json::json!({}));
|
||||
@ -199,7 +212,9 @@ impl P2PBox {
|
||||
}
|
||||
|
||||
// Wait for pong with timeout
|
||||
let ok = rx.recv_timeout(std::time::Duration::from_millis(timeout_ms)).is_ok();
|
||||
let ok = rx
|
||||
.recv_timeout(std::time::Duration::from_millis(timeout_ms))
|
||||
.is_ok();
|
||||
active.store(false, Ordering::SeqCst);
|
||||
Box::new(BoolBox::new(ok))
|
||||
}
|
||||
@ -208,11 +223,11 @@ impl P2PBox {
|
||||
pub fn ping(&self, to: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
self.ping_with_timeout(to, 300)
|
||||
}
|
||||
|
||||
|
||||
/// 特定ノードにメッセージを送信
|
||||
pub fn send(&self, to: Box<dyn NyashBox>, intent: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let to_str = to.to_string_box().value;
|
||||
|
||||
|
||||
// Extract IntentBox from the generic Box
|
||||
if let Some(intent_box) = intent.as_any().downcast_ref::<IntentBox>() {
|
||||
let transport = self.transport.read().unwrap();
|
||||
@ -221,20 +236,34 @@ impl P2PBox {
|
||||
// Minimal loopback trace without relying on transport callbacks
|
||||
let self_id = self.node_id.read().unwrap().clone();
|
||||
if to_str == self_id {
|
||||
if let Ok(mut lf) = self.last_from.write() { *lf = Some(self_id.clone()); }
|
||||
if let Ok(mut li) = self.last_intent_name.write() { *li = Some(intent_box.get_name().to_string_box().value); }
|
||||
if let Ok(mut lf) = self.last_from.write() {
|
||||
*lf = Some(self_id.clone());
|
||||
}
|
||||
if let Ok(mut li) = self.last_intent_name.write() {
|
||||
*li = Some(intent_box.get_name().to_string_box().value);
|
||||
}
|
||||
}
|
||||
Box::new(ResultBox::new_ok(Box::new(BoolBox::new(true))))
|
||||
},
|
||||
Err(e) => Box::new(ResultBox::new_err(Box::new(StringBox::new(format!("{:?}", e))))),
|
||||
}
|
||||
Err(e) => Box::new(ResultBox::new_err(Box::new(StringBox::new(format!(
|
||||
"{:?}",
|
||||
e
|
||||
))))),
|
||||
}
|
||||
} else {
|
||||
Box::new(ResultBox::new_err(Box::new(StringBox::new("Second argument must be IntentBox"))))
|
||||
Box::new(ResultBox::new_err(Box::new(StringBox::new(
|
||||
"Second argument must be IntentBox",
|
||||
))))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// イベントハンドラーを登録
|
||||
fn register_handler_internal(&self, intent_str: &str, handler: &Box<dyn NyashBox>, once: bool) -> Box<dyn NyashBox> {
|
||||
fn register_handler_internal(
|
||||
&self,
|
||||
intent_str: &str,
|
||||
handler: &Box<dyn NyashBox>,
|
||||
once: bool,
|
||||
) -> Box<dyn NyashBox> {
|
||||
// 保存
|
||||
{
|
||||
let mut handlers = self.handlers.write().unwrap();
|
||||
@ -245,7 +274,10 @@ impl P2PBox {
|
||||
let flag = Arc::new(AtomicBool::new(true));
|
||||
{
|
||||
let mut flags = self.handler_flags.write().unwrap();
|
||||
flags.entry(intent_str.to_string()).or_default().push(flag.clone());
|
||||
flags
|
||||
.entry(intent_str.to_string())
|
||||
.or_default()
|
||||
.push(flag.clone());
|
||||
}
|
||||
// once情報を記録
|
||||
{
|
||||
@ -265,82 +297,111 @@ impl P2PBox {
|
||||
// capture flags map to allow removal on once
|
||||
let flags_arc = Arc::clone(&self.handler_flags);
|
||||
let intent_name_closure = intent_name.clone();
|
||||
t.register_intent_handler(&intent_name, Box::new(move |env| {
|
||||
if flag.load(Ordering::SeqCst) {
|
||||
if let Ok(mut lf) = last_from.write() { *lf = Some(env.from.clone()); }
|
||||
if let Ok(mut li) = last_intent.write() { *li = Some(env.intent.get_name().to_string_box().value); }
|
||||
let _ = method_clone.invoke(vec![
|
||||
Box::new(env.intent.clone()),
|
||||
Box::new(StringBox::new(env.from.clone())),
|
||||
]);
|
||||
if once {
|
||||
flag.store(false, Ordering::SeqCst);
|
||||
if let Ok(mut flags) = flags_arc.write() {
|
||||
if let Some(v) = flags.get_mut(&intent_name_closure) { v.clear(); }
|
||||
t.register_intent_handler(
|
||||
&intent_name,
|
||||
Box::new(move |env| {
|
||||
if flag.load(Ordering::SeqCst) {
|
||||
if let Ok(mut lf) = last_from.write() {
|
||||
*lf = Some(env.from.clone());
|
||||
}
|
||||
if let Ok(mut li) = last_intent.write() {
|
||||
*li = Some(env.intent.get_name().to_string_box().value);
|
||||
}
|
||||
let _ = method_clone.invoke(vec![
|
||||
Box::new(env.intent.clone()),
|
||||
Box::new(StringBox::new(env.from.clone())),
|
||||
]);
|
||||
if once {
|
||||
flag.store(false, Ordering::SeqCst);
|
||||
if let Ok(mut flags) = flags_arc.write() {
|
||||
if let Some(v) = flags.get_mut(&intent_name_closure) {
|
||||
v.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
}),
|
||||
);
|
||||
// FunctionBox ハンドラー(関数値)
|
||||
} else if let Some(func_box) = handler.as_any().downcast_ref::<crate::boxes::function_box::FunctionBox>() {
|
||||
} else if let Some(func_box) = handler
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::function_box::FunctionBox>()
|
||||
{
|
||||
let func_clone = func_box.clone();
|
||||
let intent_name = intent_str.to_string();
|
||||
let last_from = Arc::clone(&self.last_from);
|
||||
let last_intent = Arc::clone(&self.last_intent_name);
|
||||
let flags_arc = Arc::clone(&self.handler_flags);
|
||||
let intent_name_closure = intent_name.clone();
|
||||
t.register_intent_handler(&intent_name, Box::new(move |env| {
|
||||
if flag.load(Ordering::SeqCst) {
|
||||
if let Ok(mut lf) = last_from.write() { *lf = Some(env.from.clone()); }
|
||||
if let Ok(mut li) = last_intent.write() { *li = Some(env.intent.get_name().to_string_box().value); }
|
||||
// 最小インタープリタで FunctionBox を実行
|
||||
let mut interp = crate::interpreter::NyashInterpreter::new();
|
||||
// キャプチャ注入
|
||||
for (k, v) in func_clone.env.captures.iter() {
|
||||
interp.declare_local_variable(k, v.clone_or_share());
|
||||
}
|
||||
if let Some(me_w) = &func_clone.env.me_value {
|
||||
if let Some(me_arc) = me_w.upgrade() {
|
||||
interp.declare_local_variable("me", (*me_arc).clone_or_share());
|
||||
t.register_intent_handler(
|
||||
&intent_name,
|
||||
Box::new(move |env| {
|
||||
if flag.load(Ordering::SeqCst) {
|
||||
if let Ok(mut lf) = last_from.write() {
|
||||
*lf = Some(env.from.clone());
|
||||
}
|
||||
if let Ok(mut li) = last_intent.write() {
|
||||
*li = Some(env.intent.get_name().to_string_box().value);
|
||||
}
|
||||
// 最小インタープリタで FunctionBox を実行
|
||||
let mut interp = crate::interpreter::NyashInterpreter::new();
|
||||
// キャプチャ注入
|
||||
for (k, v) in func_clone.env.captures.iter() {
|
||||
interp.declare_local_variable(k, v.clone_or_share());
|
||||
}
|
||||
if let Some(me_w) = &func_clone.env.me_value {
|
||||
if let Some(me_arc) = me_w.upgrade() {
|
||||
interp.declare_local_variable("me", (*me_arc).clone_or_share());
|
||||
}
|
||||
}
|
||||
// 引数束縛: intent, from(必要数だけ)
|
||||
let args: Vec<Box<dyn NyashBox>> = vec![
|
||||
Box::new(env.intent.clone()),
|
||||
Box::new(StringBox::new(env.from.clone())),
|
||||
];
|
||||
for (i, p) in func_clone.params.iter().enumerate() {
|
||||
if let Some(av) = args.get(i) {
|
||||
interp.declare_local_variable(p, av.clone_or_share());
|
||||
}
|
||||
}
|
||||
// 本体実行
|
||||
crate::runtime::global_hooks::push_task_scope();
|
||||
for st in &func_clone.body {
|
||||
let _ = interp.execute_statement(st);
|
||||
}
|
||||
crate::runtime::global_hooks::pop_task_scope();
|
||||
if once {
|
||||
flag.store(false, Ordering::SeqCst);
|
||||
if let Ok(mut flags) = flags_arc.write() {
|
||||
if let Some(v) = flags.get_mut(&intent_name_closure) {
|
||||
v.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 引数束縛: intent, from(必要数だけ)
|
||||
let args: Vec<Box<dyn NyashBox>> = vec![
|
||||
Box::new(env.intent.clone()),
|
||||
Box::new(StringBox::new(env.from.clone())),
|
||||
];
|
||||
for (i, p) in func_clone.params.iter().enumerate() {
|
||||
if let Some(av) = args.get(i) {
|
||||
interp.declare_local_variable(p, av.clone_or_share());
|
||||
}
|
||||
}
|
||||
// 本体実行
|
||||
crate::runtime::global_hooks::push_task_scope();
|
||||
for st in &func_clone.body {
|
||||
let _ = interp.execute_statement(st);
|
||||
}
|
||||
crate::runtime::global_hooks::pop_task_scope();
|
||||
if once {
|
||||
flag.store(false, Ordering::SeqCst);
|
||||
if let Ok(mut flags) = flags_arc.write() {
|
||||
if let Some(v) = flags.get_mut(&intent_name_closure) { v.clear(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
Box::new(ResultBox::new_ok(Box::new(BoolBox::new(true))))
|
||||
}
|
||||
|
||||
/// イベントハンドラーを登録
|
||||
pub fn on(&self, intent_name: Box<dyn NyashBox>, handler: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
pub fn on(
|
||||
&self,
|
||||
intent_name: Box<dyn NyashBox>,
|
||||
handler: Box<dyn NyashBox>,
|
||||
) -> Box<dyn NyashBox> {
|
||||
let intent_str = intent_name.to_string_box().value;
|
||||
self.register_handler_internal(&intent_str, &handler, false)
|
||||
}
|
||||
|
||||
/// 一度だけのハンドラー登録
|
||||
pub fn on_once(&self, intent_name: Box<dyn NyashBox>, handler: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
pub fn on_once(
|
||||
&self,
|
||||
intent_name: Box<dyn NyashBox>,
|
||||
handler: Box<dyn NyashBox>,
|
||||
) -> Box<dyn NyashBox> {
|
||||
let intent_str = intent_name.to_string_box().value;
|
||||
self.register_handler_internal(&intent_str, &handler, true)
|
||||
}
|
||||
@ -350,7 +411,9 @@ impl P2PBox {
|
||||
let intent_str = intent_name.to_string_box().value;
|
||||
if let Ok(mut flags) = self.handler_flags.write() {
|
||||
if let Some(v) = flags.get_mut(&intent_str) {
|
||||
for f in v.iter() { f.store(false, Ordering::SeqCst); }
|
||||
for f in v.iter() {
|
||||
f.store(false, Ordering::SeqCst);
|
||||
}
|
||||
v.clear();
|
||||
}
|
||||
}
|
||||
@ -364,7 +427,7 @@ impl P2PBox {
|
||||
let transport = self.transport.read().unwrap();
|
||||
Box::new(BoolBox::new(transport.is_reachable(&node_str)))
|
||||
}
|
||||
|
||||
|
||||
/// トランスポート種類を取得
|
||||
pub fn get_transport_type(&self) -> Box<dyn NyashBox> {
|
||||
let transport = self.transport.read().unwrap();
|
||||
@ -396,11 +459,16 @@ impl P2PBox {
|
||||
// once登録かつ直近受信が同名なら 0 を返す(自己送信の安定化用)
|
||||
if let (Ok(once_map), Ok(last)) = (self.handler_once.read(), self.last_intent_name.read()) {
|
||||
if let Some(true) = once_map.get(&name).copied() {
|
||||
if let Some(li) = &*last { if li == &name { return Box::new(crate::box_trait::IntegerBox::new(0)); } }
|
||||
if let Some(li) = &*last {
|
||||
if li == &name {
|
||||
return Box::new(crate::box_trait::IntegerBox::new(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let flags = self.handler_flags.read().unwrap();
|
||||
let cnt = flags.get(&name)
|
||||
let cnt = flags
|
||||
.get(&name)
|
||||
.map(|v| v.iter().filter(|f| f.load(Ordering::SeqCst)).count())
|
||||
.unwrap_or(0);
|
||||
Box::new(crate::box_trait::IntegerBox::new(cnt as i64))
|
||||
@ -414,18 +482,21 @@ impl P2PBox {
|
||||
|
||||
/// 最後に受信したIntent名を取得(ループバック検証用)
|
||||
pub fn get_last_intent_name(&self) -> Box<dyn NyashBox> {
|
||||
let v = self.last_intent_name.read().unwrap().clone().unwrap_or_default();
|
||||
let v = self
|
||||
.last_intent_name
|
||||
.read()
|
||||
.unwrap()
|
||||
.clone()
|
||||
.unwrap_or_default();
|
||||
Box::new(StringBox::new(v))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl NyashBox for P2PBox {
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
// Share underlying transport and state via Arc clones
|
||||
let node_id_val = self.node_id.read().unwrap().clone();
|
||||
@ -446,7 +517,7 @@ impl NyashBox for P2PBox {
|
||||
let transport_type = self.transport.read().unwrap().transport_type().to_string();
|
||||
StringBox::new(format!("P2PBox[{}:{}]", node_id, transport_type))
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(other_p2p) = other.as_any().downcast_ref::<P2PBox>() {
|
||||
BoolBox::new(self.base.id == other_p2p.base.id)
|
||||
@ -464,7 +535,7 @@ impl BoxCore for P2PBox {
|
||||
fn box_id(&self) -> u64 {
|
||||
self.base.id
|
||||
}
|
||||
|
||||
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> {
|
||||
self.base.parent_type_id
|
||||
}
|
||||
@ -474,11 +545,11 @@ impl BoxCore for P2PBox {
|
||||
let transport_type = self.transport.read().unwrap().transport_type().to_string();
|
||||
write!(f, "P2PBox[{}:{}]", node_id, transport_type)
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
@ -498,7 +569,10 @@ mod tests {
|
||||
fn self_ping_sets_last_fields() {
|
||||
let p = P2PBox::new("alice".to_string(), TransportKind::InProcess);
|
||||
let intent = IntentBox::new("ping".to_string(), serde_json::json!({}));
|
||||
let res = p.send(Box::new(StringBox::new("alice".to_string())), Box::new(intent));
|
||||
let res = p.send(
|
||||
Box::new(StringBox::new("alice".to_string())),
|
||||
Box::new(intent),
|
||||
);
|
||||
// Ensure Ok
|
||||
if let Some(r) = res.as_any().downcast_ref::<ResultBox>() {
|
||||
assert!(matches!(r, ResultBox::Ok(_)));
|
||||
@ -506,7 +580,10 @@ mod tests {
|
||||
panic!("send did not return ResultBox");
|
||||
}
|
||||
assert_eq!(p.get_last_from().to_string_box().value, "alice".to_string());
|
||||
assert_eq!(p.get_last_intent_name().to_string_box().value, "ping".to_string());
|
||||
assert_eq!(
|
||||
p.get_last_intent_name().to_string_box().value,
|
||||
"ping".to_string()
|
||||
);
|
||||
}
|
||||
|
||||
/// Internal helper for tests: register raw Rust handler with optional async reply
|
||||
@ -521,22 +598,29 @@ mod tests {
|
||||
// Avoid deep clone (which re-registers transport). Use transport directly for reply.
|
||||
let transport_arc = Arc::clone(&self.transport);
|
||||
let reply_name = reply_intent.map(|s| s.to_string());
|
||||
t.register_intent_handler(&intent_name, Box::new(move |env| {
|
||||
if let Ok(mut lf) = last_from.write() { *lf = Some(env.from.clone()); }
|
||||
if let Ok(mut li) = last_intent.write() { *li = Some(env.intent.get_name().to_string_box().value); }
|
||||
if let Some(rn) = reply_name.clone() {
|
||||
let to = env.from.clone();
|
||||
let transport_arc = Arc::clone(&transport_arc);
|
||||
std::thread::spawn(move || {
|
||||
// slight delay to avoid lock contention
|
||||
std::thread::sleep(std::time::Duration::from_millis(5));
|
||||
let intent = IntentBox::new(rn, serde_json::json!({}));
|
||||
if let Ok(transport) = transport_arc.read() {
|
||||
let _ = transport.send(&to, intent, Default::default());
|
||||
}
|
||||
});
|
||||
}
|
||||
}));
|
||||
t.register_intent_handler(
|
||||
&intent_name,
|
||||
Box::new(move |env| {
|
||||
if let Ok(mut lf) = last_from.write() {
|
||||
*lf = Some(env.from.clone());
|
||||
}
|
||||
if let Ok(mut li) = last_intent.write() {
|
||||
*li = Some(env.intent.get_name().to_string_box().value);
|
||||
}
|
||||
if let Some(rn) = reply_name.clone() {
|
||||
let to = env.from.clone();
|
||||
let transport_arc = Arc::clone(&transport_arc);
|
||||
std::thread::spawn(move || {
|
||||
// slight delay to avoid lock contention
|
||||
std::thread::sleep(std::time::Duration::from_millis(5));
|
||||
let intent = IntentBox::new(rn, serde_json::json!({}));
|
||||
if let Ok(transport) = transport_arc.read() {
|
||||
let _ = transport.send(&to, intent, Default::default());
|
||||
}
|
||||
});
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,64 +1,62 @@
|
||||
/*!
|
||||
* QRBox - QRコード生成・読み取りBox
|
||||
*
|
||||
*
|
||||
* ## 📝 概要
|
||||
* QRコードの生成、読み取り、カスタマイズを統一的に管理するBox。
|
||||
* アプリ間連携、データ共有、認証システムに最適。
|
||||
*
|
||||
*
|
||||
* ## 🛠️ 利用可能メソッド
|
||||
*
|
||||
*
|
||||
* ### 📱 QRコード生成
|
||||
* - `generate(text)` - テキストからQRコード生成
|
||||
* - `generateURL(url)` - URL用QRコード生成
|
||||
* - `generateWiFi(ssid, password, security)` - WiFi設定QR
|
||||
* - `generateContact(name, phone, email)` - 連絡先QR
|
||||
*
|
||||
*
|
||||
* ### 🎨 カスタマイズ
|
||||
* - `setSize(width, height)` - QRコードサイズ設定
|
||||
* - `setColors(fg, bg)` - 前景色・背景色設定
|
||||
* - `setLogo(image)` - ロゴ埋め込み
|
||||
* - `setErrorCorrection(level)` - エラー訂正レベル
|
||||
*
|
||||
*
|
||||
* ### 📷 読み取り
|
||||
* - `scanFromImage(imageData)` - 画像からQR読み取り
|
||||
* - `scanFromCanvas(canvas)` - Canvasから読み取り
|
||||
* - `startCamera()` - カメラ読み取り開始
|
||||
*
|
||||
*
|
||||
* ### 📊 情報取得
|
||||
* - `getDataURL()` - QRコードのData URL取得
|
||||
* - `getImageData()` - ImageData形式で取得
|
||||
* - `getInfo()` - QRコード情報取得
|
||||
*
|
||||
*
|
||||
* ## 💡 使用例
|
||||
* ```nyash
|
||||
* local qr, canvas
|
||||
* qr = new QRBox()
|
||||
* canvas = new WebCanvasBox("qr-canvas", 300, 300)
|
||||
*
|
||||
*
|
||||
* // 基本的なQRコード生成
|
||||
* qr.generate("https://nyash-lang.org")
|
||||
* qr.setSize(200, 200)
|
||||
* qr.setColors("#000000", "#ffffff")
|
||||
*
|
||||
*
|
||||
* // Canvasに描画
|
||||
* local imageData = qr.getImageData()
|
||||
* canvas.putImageData(imageData, 50, 50)
|
||||
*
|
||||
*
|
||||
* // WiFi設定QR
|
||||
* qr.generateWiFi("MyWiFi", "password123", "WPA2")
|
||||
* ```
|
||||
*/
|
||||
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
|
||||
use std::any::Any;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use web_sys::{
|
||||
HtmlCanvasElement, CanvasRenderingContext2d, ImageData
|
||||
};
|
||||
use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement, ImageData};
|
||||
|
||||
/// QRコード管理Box
|
||||
#[derive(Debug, Clone)]
|
||||
@ -146,7 +144,11 @@ impl QRBox {
|
||||
pub fn get_info(&self) -> String {
|
||||
format!(
|
||||
"Type: {}, Size: {}x{}, Error Correction: {}, Data Length: {}",
|
||||
self.qr_type, self.size.0, self.size.1, self.error_correction, self.data.len()
|
||||
self.qr_type,
|
||||
self.size.0,
|
||||
self.size.1,
|
||||
self.error_correction,
|
||||
self.data.len()
|
||||
)
|
||||
}
|
||||
|
||||
@ -170,7 +172,8 @@ impl QRBox {
|
||||
if let Some(canvas_element) = document.get_element_by_id(canvas_id) {
|
||||
if let Ok(canvas) = canvas_element.dyn_into::<HtmlCanvasElement>() {
|
||||
if let Ok(context) = canvas.get_context("2d") {
|
||||
if let Ok(ctx) = context.unwrap().dyn_into::<CanvasRenderingContext2d>() {
|
||||
if let Ok(ctx) = context.unwrap().dyn_into::<CanvasRenderingContext2d>()
|
||||
{
|
||||
return self.draw_simple_qr(&ctx);
|
||||
}
|
||||
}
|
||||
@ -186,28 +189,29 @@ impl QRBox {
|
||||
fn draw_simple_qr(&self, ctx: &CanvasRenderingContext2d) -> bool {
|
||||
let module_size = 8;
|
||||
let modules = 25; // 25x25のQRコード
|
||||
|
||||
|
||||
// 背景を描画
|
||||
ctx.set_fill_style(&wasm_bindgen::JsValue::from_str(&self.background_color));
|
||||
ctx.fill_rect(0.0, 0.0, self.size.0 as f64, self.size.1 as f64);
|
||||
|
||||
|
||||
// QRコードパターンを生成(簡易版)
|
||||
ctx.set_fill_style(&wasm_bindgen::JsValue::from_str(&self.foreground_color));
|
||||
|
||||
|
||||
// データベースの簡単なハッシュを作成
|
||||
let hash = self.simple_hash(&self.data);
|
||||
|
||||
|
||||
for y in 0..modules {
|
||||
for x in 0..modules {
|
||||
// ファインダーパターンの描画
|
||||
if (x < 7 && y < 7) || (x >= modules - 7 && y < 7) || (x < 7 && y >= modules - 7) {
|
||||
if (x == 0 || x == 6 || y == 0 || y == 6) ||
|
||||
(x >= 2 && x <= 4 && y >= 2 && y <= 4) {
|
||||
if (x == 0 || x == 6 || y == 0 || y == 6)
|
||||
|| (x >= 2 && x <= 4 && y >= 2 && y <= 4)
|
||||
{
|
||||
ctx.fill_rect(
|
||||
(x * module_size) as f64,
|
||||
(y * module_size) as f64,
|
||||
module_size as f64,
|
||||
module_size as f64
|
||||
module_size as f64,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@ -218,13 +222,13 @@ impl QRBox {
|
||||
(x * module_size) as f64,
|
||||
(y * module_size) as f64,
|
||||
module_size as f64,
|
||||
module_size as f64
|
||||
module_size as f64,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
@ -241,10 +245,16 @@ impl QRBox {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
/// Non-WASM環境用のダミー実装
|
||||
pub fn draw_to_canvas(&self, canvas_id: &str) -> bool {
|
||||
println!("QRBox: Drawing QR code to canvas '{}' (simulated)", canvas_id);
|
||||
println!(
|
||||
"QRBox: Drawing QR code to canvas '{}' (simulated)",
|
||||
canvas_id
|
||||
);
|
||||
println!(" Data: {}", self.data);
|
||||
println!(" Size: {}x{}", self.size.0, self.size.1);
|
||||
println!(" Colors: {} on {}", self.foreground_color, self.background_color);
|
||||
println!(
|
||||
" Colors: {} on {}",
|
||||
self.foreground_color, self.background_color
|
||||
);
|
||||
true
|
||||
}
|
||||
|
||||
@ -264,7 +274,8 @@ impl QRBox {
|
||||
|
||||
/// バッチ生成機能
|
||||
pub fn generate_batch(&self, data_list: &[String]) -> Vec<String> {
|
||||
data_list.iter()
|
||||
data_list
|
||||
.iter()
|
||||
.map(|data| format!("QR for: {}", data))
|
||||
.collect()
|
||||
}
|
||||
@ -279,7 +290,7 @@ impl QRBox {
|
||||
"H" => 4,
|
||||
_ => 2,
|
||||
};
|
||||
|
||||
|
||||
data_len * base_complexity
|
||||
}
|
||||
}
|
||||
@ -288,19 +299,23 @@ impl BoxCore for QRBox {
|
||||
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, "QRBox(type={}, size={}x{})", self.qr_type, self.size.0, self.size.1)
|
||||
write!(
|
||||
f,
|
||||
"QRBox(type={}, size={}x{})",
|
||||
self.qr_type, self.size.0, self.size.1
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
@ -310,20 +325,23 @@ impl NyashBox for QRBox {
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new(format!("QRBox(type={}, size={}x{})", self.qr_type, self.size.0, self.size.1))
|
||||
StringBox::new(format!(
|
||||
"QRBox(type={}, size={}x{})",
|
||||
self.qr_type, self.size.0, self.size.1
|
||||
))
|
||||
}
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
"QRBox"
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(other_qr) = other.as_any().downcast_ref::<QRBox>() {
|
||||
BoolBox::new(self.base.id == other_qr.base.id)
|
||||
@ -337,4 +355,4 @@ impl std::fmt::Display for QRBox {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.fmt_box(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,46 +1,46 @@
|
||||
/*! 🎲 RandomBox - 乱数生成Box
|
||||
*
|
||||
*
|
||||
* ## 📝 概要
|
||||
* 高品質な乱数生成機能を提供するBox。
|
||||
* ゲーム開発、統計処理、テストデータ生成に最適。
|
||||
*
|
||||
*
|
||||
* ## 🛠️ 利用可能メソッド
|
||||
*
|
||||
*
|
||||
* ### 🔢 基本乱数
|
||||
* - `random()` - 0.0~1.0の浮動小数点乱数
|
||||
* - `randInt(min, max)` - 指定範囲の整数乱数
|
||||
* - `randBool()` - true/falseのランダム選択
|
||||
* - `seed(value)` - 乱数種を設定(再現可能な乱数)
|
||||
*
|
||||
*
|
||||
* ### 🎯 選択・配列操作
|
||||
* - `choice(array)` - 配列からランダム選択
|
||||
* - `shuffle(array)` - 配列をシャッフル
|
||||
*
|
||||
*
|
||||
* ### 🎨 生成
|
||||
* - `randString(length)` - ランダム文字列生成
|
||||
* - `probability(prob)` - 指定確率でtrue
|
||||
*
|
||||
*
|
||||
* ## 💡 使用例
|
||||
* ```nyash
|
||||
* local random, result, dice, array
|
||||
* random = new RandomBox()
|
||||
*
|
||||
*
|
||||
* // 基本的な乱数
|
||||
* result = random.random() // 0.0~1.0
|
||||
* dice = random.randInt(1, 6) // サイコロ(1-6)
|
||||
* result = random.randBool() // true or false
|
||||
*
|
||||
*
|
||||
* // 配列関連
|
||||
* array = ["apple", "banana", "cherry"]
|
||||
* result = random.choice(array) // ランダム選択
|
||||
* array = random.shuffle(array) // シャッフル
|
||||
*
|
||||
*
|
||||
* // ゲーム用途
|
||||
* local password, critical_hit
|
||||
* password = random.randString(8) // 8文字のランダム文字列
|
||||
* critical_hit = random.probability(0.1) // 10%でクリティカル
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* ## 🎮 実用例
|
||||
* ```nyash
|
||||
* // RPGダメージ計算
|
||||
@ -50,7 +50,7 @@
|
||||
* if (is_critical) {
|
||||
* damage = damage * 2
|
||||
* }
|
||||
*
|
||||
*
|
||||
* // テストデータ生成
|
||||
* local users, user_id, user_name
|
||||
* users = []
|
||||
@ -60,17 +60,17 @@
|
||||
* users.push(user_name + ":" + user_id)
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* ## ⚠️ 注意
|
||||
* - 暗号学的に安全な乱数ではない(セキュリティ用途非推奨)
|
||||
* - seed()で同じ値を設定すると同じ乱数列を生成(テスト用)
|
||||
* - 大きな配列のshuffleは処理時間が長い場合あり
|
||||
*/
|
||||
|
||||
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox};
|
||||
use crate::boxes::{ArrayBox, FloatBox};
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::any::Any;
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::sync::RwLock;
|
||||
|
||||
/// 乱数生成を提供するBox
|
||||
@ -84,7 +84,7 @@ pub struct RandomBox {
|
||||
impl Clone for RandomBox {
|
||||
fn clone(&self) -> Self {
|
||||
let seed_val = *self.seed.read().unwrap();
|
||||
|
||||
|
||||
Self {
|
||||
seed: RwLock::new(seed_val),
|
||||
base: BoxBase::new(), // New unique ID for clone
|
||||
@ -99,13 +99,13 @@ impl RandomBox {
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_nanos() as u64;
|
||||
|
||||
|
||||
Self {
|
||||
seed: RwLock::new(seed),
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 種を設定
|
||||
pub fn seed(&self, new_seed: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(int_box) = new_seed.as_any().downcast_ref::<IntegerBox>() {
|
||||
@ -115,7 +115,7 @@ impl RandomBox {
|
||||
Box::new(StringBox::new("Error: seed() requires integer input"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 次の乱数を生成(線形合同法)
|
||||
fn next_random(&self) -> u64 {
|
||||
let mut seed = self.seed.write().unwrap();
|
||||
@ -123,46 +123,53 @@ impl RandomBox {
|
||||
*seed = seed.wrapping_mul(1664525).wrapping_add(1013904223);
|
||||
*seed
|
||||
}
|
||||
|
||||
|
||||
/// 0.0-1.0の浮動小数点乱数
|
||||
pub fn random(&self) -> Box<dyn NyashBox> {
|
||||
let r = self.next_random();
|
||||
let normalized = (r as f64) / (u64::MAX as f64);
|
||||
Box::new(FloatBox::new(normalized))
|
||||
}
|
||||
|
||||
|
||||
/// 指定範囲の整数乱数
|
||||
pub fn randInt(&self, min: Box<dyn NyashBox>, max: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let (Some(min_int), Some(max_int)) = (
|
||||
min.as_any().downcast_ref::<IntegerBox>(),
|
||||
max.as_any().downcast_ref::<IntegerBox>()
|
||||
max.as_any().downcast_ref::<IntegerBox>(),
|
||||
) {
|
||||
if min_int.value > max_int.value {
|
||||
return Box::new(StringBox::new("Error: min must be <= max"));
|
||||
}
|
||||
|
||||
|
||||
let range = (max_int.value - min_int.value + 1) as u64;
|
||||
let r = self.next_random() % range;
|
||||
Box::new(IntegerBox::new(min_int.value + r as i64))
|
||||
} else {
|
||||
Box::new(StringBox::new("Error: randInt() requires two integer inputs"))
|
||||
Box::new(StringBox::new(
|
||||
"Error: randInt() requires two integer inputs",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// true/falseのランダム選択
|
||||
pub fn randBool(&self) -> Box<dyn NyashBox> {
|
||||
let r = self.next_random();
|
||||
Box::new(BoolBox::new(r % 2 == 0))
|
||||
}
|
||||
|
||||
|
||||
/// 配列からランダム選択
|
||||
pub fn choice(&self, array: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(array_box) = array.as_any().downcast_ref::<ArrayBox>() {
|
||||
let length = array_box.length().to_string_box().value.parse::<i64>().unwrap_or(0);
|
||||
let length = array_box
|
||||
.length()
|
||||
.to_string_box()
|
||||
.value
|
||||
.parse::<i64>()
|
||||
.unwrap_or(0);
|
||||
if length == 0 {
|
||||
return Box::new(StringBox::new("Error: cannot choose from empty array"));
|
||||
}
|
||||
|
||||
|
||||
let index = self.next_random() % (length as u64);
|
||||
// 新しいArrayBox.get()は既にBox<dyn NyashBox>を返すので、直接使用
|
||||
array_box.get(Box::new(IntegerBox::new(index as i64)))
|
||||
@ -170,18 +177,23 @@ impl RandomBox {
|
||||
Box::new(StringBox::new("Error: choice() requires array input"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 配列をシャッフル
|
||||
pub fn shuffle(&self, array: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(array_box) = array.as_any().downcast_ref::<ArrayBox>() {
|
||||
let length = array_box.length().to_string_box().value.parse::<i64>().unwrap_or(0);
|
||||
let length = array_box
|
||||
.length()
|
||||
.to_string_box()
|
||||
.value
|
||||
.parse::<i64>()
|
||||
.unwrap_or(0);
|
||||
if length <= 1 {
|
||||
return array;
|
||||
}
|
||||
|
||||
|
||||
// 新しい配列を作成
|
||||
let shuffled = ArrayBox::new();
|
||||
|
||||
|
||||
// 元の配列の要素を全て新しい配列にコピー
|
||||
for i in 0..length {
|
||||
let element = array_box.get(Box::new(IntegerBox::new(i as i64)));
|
||||
@ -190,12 +202,12 @@ impl RandomBox {
|
||||
shuffled.push(element);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 簡易シャッフル実装(完全なFisher-Yatesは複雑なので)
|
||||
// 代わりに、元の配列からランダムに選んで新しい配列を作る
|
||||
let result = ArrayBox::new();
|
||||
let mut remaining_indices: Vec<usize> = (0..length as usize).collect();
|
||||
|
||||
|
||||
while !remaining_indices.is_empty() {
|
||||
let random_idx = (self.next_random() % remaining_indices.len() as u64) as usize;
|
||||
let actual_idx = remaining_indices.remove(random_idx);
|
||||
@ -205,42 +217,44 @@ impl RandomBox {
|
||||
result.push(element);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Box::new(result)
|
||||
} else {
|
||||
Box::new(StringBox::new("Error: shuffle() requires array input"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// ランダムな文字列生成
|
||||
pub fn randString(&self, length: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(len_int) = length.as_any().downcast_ref::<IntegerBox>() {
|
||||
if len_int.value < 0 {
|
||||
return Box::new(StringBox::new("Error: length must be positive"));
|
||||
}
|
||||
|
||||
|
||||
let chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
let char_vec: Vec<char> = chars.chars().collect();
|
||||
let mut result = String::new();
|
||||
|
||||
|
||||
for _ in 0..len_int.value {
|
||||
let index = self.next_random() % (char_vec.len() as u64);
|
||||
result.push(char_vec[index as usize]);
|
||||
}
|
||||
|
||||
|
||||
Box::new(StringBox::new(&result))
|
||||
} else {
|
||||
Box::new(StringBox::new("Error: randString() requires integer length"))
|
||||
Box::new(StringBox::new(
|
||||
"Error: randString() requires integer length",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 指定確率でtrue
|
||||
pub fn probability(&self, prob: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(float_box) = prob.as_any().downcast_ref::<FloatBox>() {
|
||||
if float_box.value < 0.0 || float_box.value > 1.0 {
|
||||
return Box::new(StringBox::new("Error: probability must be 0.0-1.0"));
|
||||
}
|
||||
|
||||
|
||||
let r = self.next_random() as f64 / u64::MAX as f64;
|
||||
Box::new(BoolBox::new(r < float_box.value))
|
||||
} else if let Some(int_box) = prob.as_any().downcast_ref::<IntegerBox>() {
|
||||
@ -248,11 +262,13 @@ impl RandomBox {
|
||||
if prob_val < 0.0 || prob_val > 1.0 {
|
||||
return Box::new(StringBox::new("Error: probability must be 0.0-1.0"));
|
||||
}
|
||||
|
||||
|
||||
let r = self.next_random() as f64 / u64::MAX as f64;
|
||||
Box::new(BoolBox::new(r < prob_val))
|
||||
} else {
|
||||
Box::new(StringBox::new("Error: probability() requires numeric input"))
|
||||
Box::new(StringBox::new(
|
||||
"Error: probability() requires numeric input",
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -261,20 +277,20 @@ impl NyashBox for RandomBox {
|
||||
fn type_name(&self) -> &'static str {
|
||||
"RandomBox"
|
||||
}
|
||||
|
||||
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new("RandomBox()")
|
||||
}
|
||||
|
||||
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(other_random) = other.as_any().downcast_ref::<RandomBox>() {
|
||||
BoolBox::new(self.base.id == other_random.base.id)
|
||||
@ -282,27 +298,25 @@ impl NyashBox for RandomBox {
|
||||
BoolBox::new(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
impl BoxCore for RandomBox {
|
||||
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, "RandomBox()")
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
@ -312,4 +326,4 @@ impl Display for RandomBox {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.fmt_box(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
|
||||
use std::any::Any;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
@ -10,10 +10,16 @@ pub struct RefCellBox {
|
||||
|
||||
impl RefCellBox {
|
||||
pub fn new(initial: Box<dyn NyashBox>) -> Self {
|
||||
Self { inner: Arc::new(Mutex::new(initial)), base: BoxBase::new() }
|
||||
Self {
|
||||
inner: Arc::new(Mutex::new(initial)),
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
pub fn with_inner(inner: Arc<Mutex<Box<dyn NyashBox>>>) -> Self {
|
||||
Self { inner, base: BoxBase::new() }
|
||||
Self {
|
||||
inner,
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
pub fn borrow(&self) -> Box<dyn NyashBox> {
|
||||
self.inner.lock().unwrap().clone_box()
|
||||
@ -22,31 +28,48 @@ impl RefCellBox {
|
||||
let mut guard = self.inner.lock().unwrap();
|
||||
*guard = value;
|
||||
}
|
||||
pub fn inner_arc(&self) -> Arc<Mutex<Box<dyn NyashBox>>> { Arc::clone(&self.inner) }
|
||||
pub fn inner_arc(&self) -> Arc<Mutex<Box<dyn NyashBox>>> {
|
||||
Arc::clone(&self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl BoxCore for RefCellBox {
|
||||
fn box_id(&self) -> u64 { self.base.id }
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id }
|
||||
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, "RefCellBox(..)")
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any { self }
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any { self }
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl NyashBox for RefCellBox {
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self::with_inner(self.inner_arc())) }
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(Self::with_inner(self.inner_arc()))
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
let inner = self.inner.lock().unwrap();
|
||||
StringBox::new(format!("RefCell({})", inner.to_string_box().value))
|
||||
}
|
||||
fn type_name(&self) -> &'static str { "RefCellBox" }
|
||||
fn type_name(&self) -> &'static str {
|
||||
"RefCellBox"
|
||||
}
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(o) = other.as_any().downcast_ref::<RefCellBox>() {
|
||||
BoolBox::new(Arc::ptr_eq(&self.inner, &o.inner))
|
||||
} else { BoolBox::new(false) }
|
||||
} else {
|
||||
BoolBox::new(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2,12 +2,12 @@
|
||||
// Nyashの箱システムによる正規表現処理を提供します。
|
||||
// 参考: 既存Boxの設計思想
|
||||
|
||||
use regex::Regex;
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
|
||||
use crate::boxes::array::ArrayBox;
|
||||
use regex::Regex;
|
||||
use std::any::Any;
|
||||
use std::sync::Arc;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RegexBox {
|
||||
@ -31,13 +31,13 @@ impl RegexBox {
|
||||
pub fn pattern(&self) -> &str {
|
||||
&self.pattern
|
||||
}
|
||||
|
||||
|
||||
/// パターンマッチテスト
|
||||
pub fn test(&self, text: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let text_str = text.to_string_box().value;
|
||||
Box::new(BoolBox::new(self.is_match(&text_str)))
|
||||
}
|
||||
|
||||
|
||||
/// マッチ箇所を検索
|
||||
pub fn find(&self, text: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let text_str = text.to_string_box().value;
|
||||
@ -47,36 +47,40 @@ impl RegexBox {
|
||||
Box::new(crate::boxes::null_box::NullBox::new())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// すべてのマッチを検索
|
||||
pub fn find_all(&self, text: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let text_str = text.to_string_box().value;
|
||||
let array = ArrayBox::new();
|
||||
|
||||
|
||||
for mat in self.regex.find_iter(&text_str) {
|
||||
let _ = array.push(Box::new(StringBox::new(mat.as_str())));
|
||||
}
|
||||
|
||||
|
||||
Box::new(array)
|
||||
}
|
||||
|
||||
|
||||
/// 文字列置換
|
||||
pub fn replace(&self, text: Box<dyn NyashBox>, replacement: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
pub fn replace(
|
||||
&self,
|
||||
text: Box<dyn NyashBox>,
|
||||
replacement: Box<dyn NyashBox>,
|
||||
) -> Box<dyn NyashBox> {
|
||||
let text_str = text.to_string_box().value;
|
||||
let replacement_str = replacement.to_string_box().value;
|
||||
let result = self.regex.replace_all(&text_str, replacement_str.as_str());
|
||||
Box::new(StringBox::new(&result.to_string()))
|
||||
}
|
||||
|
||||
|
||||
/// 文字列分割
|
||||
pub fn split(&self, text: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let text_str = text.to_string_box().value;
|
||||
let array = ArrayBox::new();
|
||||
|
||||
|
||||
for part in self.regex.split(&text_str) {
|
||||
let _ = array.push(Box::new(StringBox::new(part)));
|
||||
}
|
||||
|
||||
|
||||
Box::new(array)
|
||||
}
|
||||
}
|
||||
@ -85,7 +89,7 @@ impl NyashBox for RegexBox {
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
@ -95,12 +99,10 @@ impl NyashBox for RegexBox {
|
||||
StringBox::new(format!("RegexBox({})", self.pattern.as_str()))
|
||||
}
|
||||
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
"RegexBox"
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(other_regex) = other.as_any().downcast_ref::<RegexBox>() {
|
||||
BoolBox::new(self.pattern.as_str() == other_regex.pattern.as_str())
|
||||
@ -114,7 +116,7 @@ impl BoxCore for RegexBox {
|
||||
fn box_id(&self) -> u64 {
|
||||
self.base.id
|
||||
}
|
||||
|
||||
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> {
|
||||
self.base.parent_type_id
|
||||
}
|
||||
@ -122,11 +124,11 @@ impl BoxCore for RegexBox {
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "RegexBox({})", self.pattern.as_str())
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
// Nyashの箱システムによるエラー処理を提供します。
|
||||
// 参考: 既存Boxの設計思想
|
||||
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore};
|
||||
use crate::box_trait::{BoolBox, BoxCore, NyashBox, StringBox};
|
||||
use std::any::Any;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -15,19 +15,19 @@ impl NyashResultBox {
|
||||
pub fn new_ok(value: Box<dyn NyashBox>) -> Self {
|
||||
NyashResultBox::Ok(value)
|
||||
}
|
||||
|
||||
|
||||
pub fn new_err(error: Box<dyn NyashBox>) -> Self {
|
||||
NyashResultBox::Err(error)
|
||||
}
|
||||
|
||||
|
||||
pub fn is_ok_bool(&self) -> bool {
|
||||
matches!(self, NyashResultBox::Ok(_))
|
||||
}
|
||||
|
||||
|
||||
pub fn is_err(&self) -> bool {
|
||||
matches!(self, NyashResultBox::Err(_))
|
||||
}
|
||||
|
||||
|
||||
pub fn unwrap(self) -> Box<dyn NyashBox> {
|
||||
match self {
|
||||
NyashResultBox::Ok(val) => val,
|
||||
@ -43,7 +43,7 @@ impl NyashBox for NyashResultBox {
|
||||
NyashResultBox::Err(err) => Box::new(NyashResultBox::Err(err.clone_or_share())),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
match self {
|
||||
@ -55,16 +55,16 @@ impl NyashBox for NyashResultBox {
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
match self {
|
||||
NyashResultBox::Ok(val) => StringBox::new(format!("Ok({})", val.to_string_box().value)),
|
||||
NyashResultBox::Err(err) => StringBox::new(format!("Err({})", err.to_string_box().value)),
|
||||
NyashResultBox::Err(err) => {
|
||||
StringBox::new(format!("Err({})", err.to_string_box().value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
"NyashResultBox"
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(other_result) = other.as_any().downcast_ref::<NyashResultBox>() {
|
||||
match (self, other_result) {
|
||||
@ -86,7 +86,7 @@ impl BoxCore for NyashResultBox {
|
||||
NyashResultBox::Err(err) => err.box_id(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> {
|
||||
// For enum variants, we use the contained value's parent type ID
|
||||
match self {
|
||||
@ -101,11 +101,11 @@ impl BoxCore for NyashResultBox {
|
||||
NyashResultBox::Err(err) => write!(f, "Err({})", err.to_string_box().value),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
@ -125,13 +125,17 @@ impl ResultBox {
|
||||
pub fn is_ok(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(BoolBox::new(matches!(self, NyashResultBox::Ok(_))))
|
||||
}
|
||||
|
||||
|
||||
/// getValue()の実装 - Ok値を取得
|
||||
pub fn get_value(&self) -> Box<dyn NyashBox> {
|
||||
match self {
|
||||
NyashResultBox::Ok(val) => {
|
||||
// Preserve identity for plugin-backed boxes
|
||||
if val.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some() {
|
||||
if val
|
||||
.as_any()
|
||||
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
|
||||
.is_some()
|
||||
{
|
||||
val.share_box()
|
||||
} else {
|
||||
val.clone_box()
|
||||
@ -140,7 +144,7 @@ impl ResultBox {
|
||||
NyashResultBox::Err(_) => Box::new(StringBox::new("Error: Result is Err")),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// getError()の実装 - Err値を取得
|
||||
pub fn get_error(&self) -> Box<dyn NyashBox> {
|
||||
match self {
|
||||
|
||||
@ -1,23 +1,23 @@
|
||||
/*! 🔌 SocketBox - TCP/UDP Socket networking
|
||||
*
|
||||
*
|
||||
* ## 📝 概要
|
||||
* Rustの std::net を基盤とした高性能ネットワーキング Box
|
||||
* TCP サーバー・クライアント両対応、HTTPサーバー基盤として利用
|
||||
*
|
||||
*
|
||||
* ## 🛠️ 利用可能メソッド
|
||||
* ### TCP Server
|
||||
* - `bind(address, port)` - TCP ソケット bind
|
||||
* - `listen(backlog)` - 接続待機開始
|
||||
* - `accept()` - クライアント接続受諾
|
||||
*
|
||||
*
|
||||
* ### TCP Client
|
||||
* - `connect(address, port)` - サーバーへ接続
|
||||
*
|
||||
*
|
||||
* ### IO Operations
|
||||
* - `read()` - データ読み取り
|
||||
* - `write(data)` - データ送信
|
||||
* - `close()` - ソケット閉鎖
|
||||
*
|
||||
*
|
||||
* ## 💡 使用例
|
||||
* ```nyash
|
||||
* // TCP Server
|
||||
@ -25,7 +25,7 @@
|
||||
* server.bind("0.0.0.0", 8080)
|
||||
* server.listen(128)
|
||||
* client = server.accept()
|
||||
*
|
||||
*
|
||||
* // TCP Client
|
||||
* client = new SocketBox()
|
||||
* client.connect("127.0.0.1", 8080)
|
||||
@ -34,11 +34,11 @@
|
||||
* ```
|
||||
*/
|
||||
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
|
||||
use std::any::Any;
|
||||
use std::io::{BufRead, BufReader, Write};
|
||||
use std::net::{TcpListener, TcpStream};
|
||||
use std::io::{Write, BufRead, BufReader};
|
||||
use std::sync::{Arc, RwLock}; // Arc追加
|
||||
use std::sync::{Arc, RwLock}; // Arc追加
|
||||
use std::time::Duration;
|
||||
|
||||
/// TCP/UDP ソケット操作を提供するBox
|
||||
@ -46,25 +46,25 @@ use std::time::Duration;
|
||||
pub struct SocketBox {
|
||||
base: BoxBase,
|
||||
// TCP Server
|
||||
listener: Arc<RwLock<Option<TcpListener>>>, // Arc追加
|
||||
listener: Arc<RwLock<Option<TcpListener>>>, // Arc追加
|
||||
// TCP Client/Connected Socket
|
||||
stream: Arc<RwLock<Option<TcpStream>>>, // Arc追加
|
||||
stream: Arc<RwLock<Option<TcpStream>>>, // Arc追加
|
||||
// Connection state
|
||||
is_server: Arc<RwLock<bool>>, // Arc追加
|
||||
is_connected: Arc<RwLock<bool>>, // Arc追加
|
||||
is_server: Arc<RwLock<bool>>, // Arc追加
|
||||
is_connected: Arc<RwLock<bool>>, // Arc追加
|
||||
}
|
||||
|
||||
impl Clone for SocketBox {
|
||||
fn clone(&self) -> Self {
|
||||
// ディープコピー(独立インスタンス)
|
||||
// ディープコピー(独立インスタンス)
|
||||
let is_server_val = *self.is_server.read().unwrap();
|
||||
let is_connected_val = *self.is_connected.read().unwrap();
|
||||
|
||||
|
||||
Self {
|
||||
base: BoxBase::new(), // New unique ID for clone
|
||||
listener: Arc::new(RwLock::new(None)), // 新しいArc
|
||||
stream: Arc::new(RwLock::new(None)), // 新しいArc
|
||||
is_server: Arc::new(RwLock::new(is_server_val)), // 状態のみコピー
|
||||
base: BoxBase::new(), // New unique ID for clone
|
||||
listener: Arc::new(RwLock::new(None)), // 新しいArc
|
||||
stream: Arc::new(RwLock::new(None)), // 新しいArc
|
||||
is_server: Arc::new(RwLock::new(is_server_val)), // 状態のみコピー
|
||||
is_connected: Arc::new(RwLock::new(is_connected_val)), // 状態のみコピー
|
||||
}
|
||||
}
|
||||
@ -74,41 +74,41 @@ impl SocketBox {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
base: BoxBase::new(),
|
||||
listener: Arc::new(RwLock::new(None)), // Arc::new追加
|
||||
stream: Arc::new(RwLock::new(None)), // Arc::new追加
|
||||
is_server: Arc::new(RwLock::new(false)), // Arc::new追加
|
||||
listener: Arc::new(RwLock::new(None)), // Arc::new追加
|
||||
stream: Arc::new(RwLock::new(None)), // Arc::new追加
|
||||
is_server: Arc::new(RwLock::new(false)), // Arc::new追加
|
||||
is_connected: Arc::new(RwLock::new(false)), // Arc::new追加
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// TCP ソケットをアドレス・ポートにバインド
|
||||
pub fn bind(&self, address: Box<dyn NyashBox>, port: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let addr_str = address.to_string_box().value;
|
||||
let port_str = port.to_string_box().value;
|
||||
|
||||
|
||||
let socket_addr = format!("{}:{}", addr_str, port_str);
|
||||
|
||||
|
||||
eprintln!("🔥 SOCKETBOX DEBUG: bind() called");
|
||||
eprintln!("🔥 Socket ID = {}", self.base.id);
|
||||
eprintln!("🔥 Address = {}", socket_addr);
|
||||
eprintln!("🔥 Arc pointer = {:p}", &self.is_server);
|
||||
|
||||
|
||||
match TcpListener::bind(&socket_addr) {
|
||||
Ok(listener) => {
|
||||
eprintln!("✅ TCP bind successful");
|
||||
|
||||
|
||||
// listener設定
|
||||
match self.listener.write() {
|
||||
Ok(mut listener_guard) => {
|
||||
*listener_guard = Some(listener);
|
||||
eprintln!("✅ Listener stored successfully");
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ Failed to lock listener mutex: {}", e);
|
||||
return Box::new(BoolBox::new(false));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// is_server状態設定 - 徹底デバッグ
|
||||
match self.is_server.write() {
|
||||
Ok(mut is_server_guard) => {
|
||||
@ -116,62 +116,62 @@ impl SocketBox {
|
||||
eprintln!("🔥 is_server value = {}", *is_server_guard);
|
||||
eprintln!("🔥 RwLock pointer = {:p}", &self.is_server);
|
||||
eprintln!("🔥 Guard pointer = {:p}", &*is_server_guard);
|
||||
|
||||
|
||||
// 状態変更
|
||||
*is_server_guard = true;
|
||||
|
||||
|
||||
eprintln!("🔥 AFTER MUTATION:");
|
||||
eprintln!("🔥 is_server value = {}", *is_server_guard);
|
||||
eprintln!("🔥 Value confirmed = {}", *is_server_guard == true);
|
||||
|
||||
|
||||
// 明示的にドロップしてロック解除
|
||||
drop(is_server_guard);
|
||||
eprintln!("✅ is_server guard dropped");
|
||||
|
||||
|
||||
// 再確認テスト
|
||||
match self.is_server.read() {
|
||||
Ok(check_guard) => {
|
||||
eprintln!("🔥 RECHECK AFTER DROP:");
|
||||
eprintln!("🔥 is_server value = {}", *check_guard);
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ Failed to recheck: {}", e);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ SOCKETBOX: Failed to lock is_server mutex: {}", e);
|
||||
return Box::new(BoolBox::new(false));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
eprintln!("✅ bind() completed successfully");
|
||||
Box::new(BoolBox::new(true))
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ TCP bind failed: {}", e);
|
||||
Box::new(BoolBox::new(false))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 指定した backlog で接続待機開始
|
||||
pub fn listen(&self, backlog: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let _backlog_num = backlog.to_string_box().value.parse::<i32>().unwrap_or(128);
|
||||
|
||||
|
||||
// Check if listener exists and is properly bound
|
||||
let listener_guard = match self.listener.read() {
|
||||
Ok(guard) => guard,
|
||||
Err(_) => return Box::new(BoolBox::new(false)),
|
||||
};
|
||||
|
||||
|
||||
if let Some(ref listener) = *listener_guard {
|
||||
// Try to get the local address to confirm the listener is working
|
||||
match listener.local_addr() {
|
||||
Ok(_addr) => {
|
||||
// Listener is properly set up and can accept connections
|
||||
Box::new(BoolBox::new(true))
|
||||
},
|
||||
}
|
||||
Err(_) => {
|
||||
// Listener exists but has issues
|
||||
Box::new(BoolBox::new(false))
|
||||
@ -183,7 +183,7 @@ impl SocketBox {
|
||||
Box::new(BoolBox::new(false))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// クライアント接続を受諾(ブロッキング)
|
||||
pub fn accept(&self) -> Box<dyn NyashBox> {
|
||||
let listener_guard = self.listener.write().unwrap();
|
||||
@ -191,14 +191,14 @@ impl SocketBox {
|
||||
match listener.accept() {
|
||||
Ok((stream, _addr)) => {
|
||||
drop(listener_guard);
|
||||
|
||||
|
||||
// Create new SocketBox for the client connection
|
||||
let client_socket = SocketBox::new();
|
||||
*client_socket.stream.write().unwrap() = Some(stream);
|
||||
*client_socket.is_connected.write().unwrap() = true;
|
||||
|
||||
|
||||
Box::new(client_socket)
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("🚨 SocketBox accept error: {}", e);
|
||||
Box::new(BoolBox::new(false))
|
||||
@ -212,7 +212,9 @@ impl SocketBox {
|
||||
/// クライアント接続を受諾(タイムアウトms、タイムアウト時はvoid)
|
||||
pub fn accept_timeout(&self, timeout_ms: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let ms = timeout_ms.to_string_box().value.parse::<u64>().unwrap_or(0);
|
||||
if ms == 0 { return self.accept(); }
|
||||
if ms == 0 {
|
||||
return self.accept();
|
||||
}
|
||||
|
||||
let start = std::time::Instant::now();
|
||||
if let Ok(guard) = self.listener.write() {
|
||||
@ -227,7 +229,7 @@ impl SocketBox {
|
||||
*client_socket.stream.write().unwrap() = Some(stream);
|
||||
*client_socket.is_connected.write().unwrap() = true;
|
||||
return Box::new(client_socket);
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
if e.kind() == std::io::ErrorKind::WouldBlock {
|
||||
if start.elapsed() >= Duration::from_millis(ms) {
|
||||
@ -248,32 +250,36 @@ impl SocketBox {
|
||||
}
|
||||
Box::new(crate::box_trait::VoidBox::new())
|
||||
}
|
||||
|
||||
|
||||
/// サーバーに接続(クライアントモード)
|
||||
pub fn connect(&self, address: Box<dyn NyashBox>, port: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
pub fn connect(
|
||||
&self,
|
||||
address: Box<dyn NyashBox>,
|
||||
port: Box<dyn NyashBox>,
|
||||
) -> Box<dyn NyashBox> {
|
||||
let addr_str = address.to_string_box().value;
|
||||
let port_str = port.to_string_box().value;
|
||||
|
||||
|
||||
let socket_addr = format!("{}:{}", addr_str, port_str);
|
||||
|
||||
|
||||
match TcpStream::connect(&socket_addr) {
|
||||
Ok(stream) => {
|
||||
// Set timeout for read/write operations
|
||||
let _ = stream.set_read_timeout(Some(Duration::from_secs(30)));
|
||||
let _ = stream.set_write_timeout(Some(Duration::from_secs(30)));
|
||||
|
||||
|
||||
*self.stream.write().unwrap() = Some(stream);
|
||||
*self.is_connected.write().unwrap() = true;
|
||||
*self.is_server.write().unwrap() = false;
|
||||
Box::new(BoolBox::new(true))
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("🚨 SocketBox connect error: {}", e);
|
||||
Box::new(BoolBox::new(false))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// データを読み取り(改行まで or EOF)
|
||||
pub fn read(&self) -> Box<dyn NyashBox> {
|
||||
let stream_guard = self.stream.write().unwrap();
|
||||
@ -282,10 +288,10 @@ impl SocketBox {
|
||||
match stream.try_clone() {
|
||||
Ok(stream_clone) => {
|
||||
drop(stream_guard);
|
||||
|
||||
|
||||
let mut reader = BufReader::new(stream_clone);
|
||||
let mut buffer = String::new();
|
||||
|
||||
|
||||
match reader.read_line(&mut buffer) {
|
||||
Ok(_) => {
|
||||
// Remove trailing newline
|
||||
@ -296,13 +302,13 @@ impl SocketBox {
|
||||
}
|
||||
}
|
||||
Box::new(StringBox::new(buffer))
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("🚨 SocketBox read error: {}", e);
|
||||
Box::new(StringBox::new("".to_string()))
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("🚨 SocketBox stream clone error: {}", e);
|
||||
Box::new(StringBox::new("".to_string()))
|
||||
@ -328,12 +334,16 @@ impl SocketBox {
|
||||
Ok(_) => {
|
||||
if buffer.ends_with('\n') {
|
||||
buffer.pop();
|
||||
if buffer.ends_with('\r') { buffer.pop(); }
|
||||
if buffer.ends_with('\r') {
|
||||
buffer.pop();
|
||||
}
|
||||
}
|
||||
Box::new(StringBox::new(&buffer))
|
||||
}
|
||||
Err(e) => {
|
||||
if e.kind() == std::io::ErrorKind::WouldBlock || e.kind() == std::io::ErrorKind::TimedOut {
|
||||
if e.kind() == std::io::ErrorKind::WouldBlock
|
||||
|| e.kind() == std::io::ErrorKind::TimedOut
|
||||
{
|
||||
return Box::new(StringBox::new(""));
|
||||
}
|
||||
eprintln!("🚨 SocketBox recv_timeout error: {}", e);
|
||||
@ -350,7 +360,7 @@ impl SocketBox {
|
||||
Box::new(StringBox::new(""))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// HTTP request を読み取り(ヘッダーまで含む)
|
||||
pub fn read_http_request(&self) -> Box<dyn NyashBox> {
|
||||
let stream_guard = self.stream.write().unwrap();
|
||||
@ -358,11 +368,11 @@ impl SocketBox {
|
||||
match stream.try_clone() {
|
||||
Ok(stream_clone) => {
|
||||
drop(stream_guard);
|
||||
|
||||
|
||||
let mut reader = BufReader::new(stream_clone);
|
||||
let mut request = String::new();
|
||||
let mut line = String::new();
|
||||
|
||||
|
||||
// Read HTTP request line by line until empty line
|
||||
loop {
|
||||
line.clear();
|
||||
@ -374,16 +384,16 @@ impl SocketBox {
|
||||
if line.trim().is_empty() {
|
||||
break;
|
||||
}
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("🚨 SocketBox HTTP read error: {}", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Box::new(StringBox::new(request))
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("🚨 SocketBox stream clone error: {}", e);
|
||||
Box::new(StringBox::new("".to_string()))
|
||||
@ -393,21 +403,19 @@ impl SocketBox {
|
||||
Box::new(StringBox::new("".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// データを送信
|
||||
pub fn write(&self, data: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let data_str = data.to_string_box().value;
|
||||
|
||||
|
||||
let mut stream_guard = self.stream.write().unwrap();
|
||||
if let Some(ref mut stream) = *stream_guard {
|
||||
match stream.write_all(data_str.as_bytes()) {
|
||||
Ok(_) => {
|
||||
match stream.flush() {
|
||||
Ok(_) => Box::new(BoolBox::new(true)),
|
||||
Err(e) => {
|
||||
eprintln!("🚨 SocketBox flush error: {}", e);
|
||||
Box::new(BoolBox::new(false))
|
||||
}
|
||||
Ok(_) => match stream.flush() {
|
||||
Ok(_) => Box::new(BoolBox::new(true)),
|
||||
Err(e) => {
|
||||
eprintln!("🚨 SocketBox flush error: {}", e);
|
||||
Box::new(BoolBox::new(false))
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
@ -419,7 +427,7 @@ impl SocketBox {
|
||||
Box::new(BoolBox::new(false))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// ソケット閉鎖
|
||||
pub fn close(&self) -> Box<dyn NyashBox> {
|
||||
*self.stream.write().unwrap() = None;
|
||||
@ -428,18 +436,18 @@ impl SocketBox {
|
||||
*self.is_server.write().unwrap() = false;
|
||||
Box::new(BoolBox::new(true))
|
||||
}
|
||||
|
||||
|
||||
/// 接続状態確認
|
||||
pub fn is_connected(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(BoolBox::new(*self.is_connected.write().unwrap()))
|
||||
}
|
||||
|
||||
|
||||
/// サーバーモード確認
|
||||
pub fn is_server(&self) -> Box<dyn NyashBox> {
|
||||
eprintln!("🔥 SOCKETBOX DEBUG: is_server() called");
|
||||
eprintln!("🔥 Socket ID = {}", self.base.id);
|
||||
eprintln!("🔥 RwLock pointer = {:p}", &self.is_server);
|
||||
|
||||
|
||||
match self.is_server.read() {
|
||||
Ok(is_server_guard) => {
|
||||
let is_server_value = *is_server_guard;
|
||||
@ -447,11 +455,14 @@ impl SocketBox {
|
||||
eprintln!("🔥 is_server value = {}", is_server_value);
|
||||
eprintln!("🔥 Guard pointer = {:p}", &*is_server_guard);
|
||||
eprintln!("🔥 Returning BoolBox with value = {}", is_server_value);
|
||||
|
||||
|
||||
Box::new(BoolBox::new(is_server_value))
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ SOCKETBOX: Failed to lock is_server mutex in is_server(): {}", e);
|
||||
eprintln!(
|
||||
"❌ SOCKETBOX: Failed to lock is_server mutex in is_server(): {}",
|
||||
e
|
||||
);
|
||||
Box::new(BoolBox::new(false))
|
||||
}
|
||||
}
|
||||
@ -462,45 +473,48 @@ impl NyashBox for SocketBox {
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 🎯 状態共有の核心実装 - SocketBox状態保持問題の根本解決
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
let new_instance = SocketBox {
|
||||
base: BoxBase::new(), // 新しいID
|
||||
listener: Arc::clone(&self.listener), // 状態共有
|
||||
stream: Arc::clone(&self.stream), // 状態共有
|
||||
is_server: Arc::clone(&self.is_server), // 状態共有
|
||||
is_connected: Arc::clone(&self.is_connected), // 状態共有
|
||||
base: BoxBase::new(), // 新しいID
|
||||
listener: Arc::clone(&self.listener), // 状態共有
|
||||
stream: Arc::clone(&self.stream), // 状態共有
|
||||
is_server: Arc::clone(&self.is_server), // 状態共有
|
||||
is_connected: Arc::clone(&self.is_connected), // 状態共有
|
||||
};
|
||||
Box::new(new_instance)
|
||||
}
|
||||
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
eprintln!("🔥 SOCKETBOX to_string_box() called - Socket ID = {}", self.base.id);
|
||||
eprintln!(
|
||||
"🔥 SOCKETBOX to_string_box() called - Socket ID = {}",
|
||||
self.base.id
|
||||
);
|
||||
eprintln!("🔥 RwLock pointer = {:p}", &self.is_server);
|
||||
|
||||
|
||||
let is_server = match self.is_server.read() {
|
||||
Ok(guard) => {
|
||||
eprintln!("✅ is_server.read() successful");
|
||||
*guard
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ is_server.read() failed: {}", e);
|
||||
false // デフォルト値
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
let is_connected = match self.is_connected.read() {
|
||||
Ok(guard) => {
|
||||
eprintln!("✅ is_connected.read() successful");
|
||||
*guard
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ is_connected.read() failed: {}", e);
|
||||
false // デフォルト値
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
let status = if is_server {
|
||||
"Server"
|
||||
} else if is_connected {
|
||||
@ -508,8 +522,11 @@ impl NyashBox for SocketBox {
|
||||
} else {
|
||||
"Disconnected"
|
||||
};
|
||||
|
||||
StringBox::new(format!("SocketBox(id: {}, status: {})", self.base.id, status))
|
||||
|
||||
StringBox::new(format!(
|
||||
"SocketBox(id: {}, status: {})",
|
||||
self.base.id, status
|
||||
))
|
||||
}
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
@ -529,14 +546,17 @@ impl BoxCore for SocketBox {
|
||||
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 {
|
||||
eprintln!("🔥 SOCKETBOX fmt_box() called - Socket ID = {}", self.base.id);
|
||||
|
||||
eprintln!(
|
||||
"🔥 SOCKETBOX fmt_box() called - Socket ID = {}",
|
||||
self.base.id
|
||||
);
|
||||
|
||||
let is_server = match self.is_server.read() {
|
||||
Ok(guard) => *guard,
|
||||
Err(e) => {
|
||||
@ -544,7 +564,7 @@ impl BoxCore for SocketBox {
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
let is_connected = match self.is_connected.read() {
|
||||
Ok(guard) => *guard,
|
||||
Err(e) => {
|
||||
@ -552,7 +572,7 @@ impl BoxCore for SocketBox {
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
let status = if is_server {
|
||||
"Server"
|
||||
} else if is_connected {
|
||||
@ -560,14 +580,14 @@ impl BoxCore for SocketBox {
|
||||
} else {
|
||||
"Disconnected"
|
||||
};
|
||||
|
||||
|
||||
write!(f, "SocketBox(id: {}, status: {})", self.base.id, status)
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
/*! 🔊 SoundBox - サウンド・音響効果Box
|
||||
*
|
||||
*
|
||||
* ## 📝 概要
|
||||
* システム音・効果音を提供するBox。
|
||||
* ゲーム効果音、通知音、アラート音の生成に使用。
|
||||
* クロスプラットフォーム対応のシンプルなサウンドシステム。
|
||||
*
|
||||
*
|
||||
* ## 🛠️ 利用可能メソッド
|
||||
* - `beep()` - 基本ビープ音
|
||||
* - `beeps(count)` - 指定回数ビープ
|
||||
@ -13,22 +13,22 @@
|
||||
* - `playTone(frequency, duration)` - 指定周波数・時間で音生成
|
||||
* - `playFile(filename)` - 音声ファイル再生
|
||||
* - `setVolume(level)` - 音量設定 (0.0-1.0)
|
||||
*
|
||||
*
|
||||
* ## 💡 使用例
|
||||
* ```nyash
|
||||
* local sound
|
||||
* sound = new SoundBox()
|
||||
*
|
||||
*
|
||||
* // 基本的な音
|
||||
* sound.beep() // シンプルビープ
|
||||
* sound.beeps(3) // 3回ビープ
|
||||
* sound.bell() // ベル音
|
||||
*
|
||||
*
|
||||
* // ゲーム効果音
|
||||
* sound.playTone(440, 500) // ラの音を500ms
|
||||
* sound.playTone(880, 200) // 高いラの音を200ms
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* ## 🎮 実用例 - ゲーム効果音
|
||||
* ```nyash
|
||||
* static box GameSFX {
|
||||
@ -65,7 +65,7 @@
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* ## 🚨 通知・アラート用途
|
||||
* ```nyash
|
||||
* static box NotificationSystem {
|
||||
@ -100,7 +100,7 @@
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* ## 🎵 音楽生成例
|
||||
* ```nyash
|
||||
* static box MusicBox {
|
||||
@ -129,7 +129,7 @@
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* ## ⚠️ 注意
|
||||
* - システムによってはビープ音が無効化されている場合あり
|
||||
* - 音量設定は環境依存
|
||||
@ -138,9 +138,9 @@
|
||||
* - Web環境では制限が多い(ユーザー操作後のみ音声再生可能)
|
||||
*/
|
||||
|
||||
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, BoxCore, BoxBase};
|
||||
use std::fmt::{Debug, Display};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox};
|
||||
use std::any::Any;
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::process::Command;
|
||||
use std::time::Duration;
|
||||
|
||||
@ -152,69 +152,81 @@ pub struct SoundBox {
|
||||
|
||||
impl SoundBox {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
base: BoxBase::new()
|
||||
Self {
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// ビープ音を鳴らす(基本)
|
||||
pub fn beep(&self) -> Box<dyn NyashBox> {
|
||||
// 端末ベル文字を出力
|
||||
print!("\x07");
|
||||
Box::new(StringBox::new("Beep!"))
|
||||
}
|
||||
|
||||
|
||||
/// 指定回数ビープ
|
||||
pub fn beeps(&self, count: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(count_int) = count.as_any().downcast_ref::<IntegerBox>() {
|
||||
if count_int.value <= 0 {
|
||||
return Box::new(StringBox::new("Beep count must be positive"));
|
||||
}
|
||||
|
||||
|
||||
for i in 0..count_int.value {
|
||||
print!("\x07");
|
||||
if i < count_int.value - 1 {
|
||||
std::thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Box::new(StringBox::new(&format!("Beeped {} times", count_int.value)))
|
||||
} else {
|
||||
Box::new(StringBox::new("Error: beeps() requires integer input"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 指定周波数のビープ(Linuxのみ)
|
||||
pub fn tone(&self, frequency: Box<dyn NyashBox>, duration: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
pub fn tone(
|
||||
&self,
|
||||
frequency: Box<dyn NyashBox>,
|
||||
duration: Box<dyn NyashBox>,
|
||||
) -> Box<dyn NyashBox> {
|
||||
if let (Some(freq_int), Some(dur_int)) = (
|
||||
frequency.as_any().downcast_ref::<IntegerBox>(),
|
||||
duration.as_any().downcast_ref::<IntegerBox>()
|
||||
duration.as_any().downcast_ref::<IntegerBox>(),
|
||||
) {
|
||||
if freq_int.value <= 0 || dur_int.value <= 0 {
|
||||
return Box::new(StringBox::new("Frequency and duration must be positive"));
|
||||
}
|
||||
|
||||
|
||||
// Linuxのbeepコマンドを試行
|
||||
match Command::new("beep")
|
||||
.arg("-f")
|
||||
.arg(&freq_int.value.to_string())
|
||||
.arg("-l")
|
||||
.arg(&dur_int.value.to_string())
|
||||
.output()
|
||||
.output()
|
||||
{
|
||||
Ok(_) => Box::new(StringBox::new(&format!("Played {}Hz for {}ms", freq_int.value, dur_int.value))),
|
||||
Ok(_) => Box::new(StringBox::new(&format!(
|
||||
"Played {}Hz for {}ms",
|
||||
freq_int.value, dur_int.value
|
||||
))),
|
||||
Err(_) => {
|
||||
// beepコマンドが無い場合は端末ベルを使用
|
||||
print!("\x07");
|
||||
std::thread::sleep(Duration::from_millis(dur_int.value as u64));
|
||||
Box::new(StringBox::new(&format!("Fallback beep ({}Hz, {}ms)", freq_int.value, dur_int.value)))
|
||||
Box::new(StringBox::new(&format!(
|
||||
"Fallback beep ({}Hz, {}ms)",
|
||||
freq_int.value, dur_int.value
|
||||
)))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Box::new(StringBox::new("Error: tone() requires two integer inputs (frequency, duration)"))
|
||||
Box::new(StringBox::new(
|
||||
"Error: tone() requires two integer inputs (frequency, duration)",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 警告音
|
||||
pub fn alert(&self) -> Box<dyn NyashBox> {
|
||||
// 3回短いビープ
|
||||
@ -226,7 +238,7 @@ impl SoundBox {
|
||||
}
|
||||
Box::new(StringBox::new("Alert sound played"))
|
||||
}
|
||||
|
||||
|
||||
/// 成功音
|
||||
pub fn success(&self) -> Box<dyn NyashBox> {
|
||||
// 1回長めのビープ
|
||||
@ -235,7 +247,7 @@ impl SoundBox {
|
||||
print!("\x07");
|
||||
Box::new(StringBox::new("Success sound played"))
|
||||
}
|
||||
|
||||
|
||||
/// エラー音
|
||||
pub fn error(&self) -> Box<dyn NyashBox> {
|
||||
// 2回素早いビープ
|
||||
@ -244,12 +256,12 @@ impl SoundBox {
|
||||
print!("\x07");
|
||||
Box::new(StringBox::new("Error sound played"))
|
||||
}
|
||||
|
||||
|
||||
/// カスタムビープパターン
|
||||
pub fn pattern(&self, pattern: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(pattern_str) = pattern.as_any().downcast_ref::<StringBox>() {
|
||||
let mut beep_count = 0;
|
||||
|
||||
|
||||
for ch in pattern_str.value.chars() {
|
||||
match ch {
|
||||
'.' => {
|
||||
@ -272,43 +284,57 @@ impl SoundBox {
|
||||
// その他の文字は無視
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 文字間の短い間隔
|
||||
std::thread::sleep(Duration::from_millis(50));
|
||||
}
|
||||
|
||||
Box::new(StringBox::new(&format!("Played pattern '{}' ({} beeps)", pattern_str.value, beep_count)))
|
||||
|
||||
Box::new(StringBox::new(&format!(
|
||||
"Played pattern '{}' ({} beeps)",
|
||||
pattern_str.value, beep_count
|
||||
)))
|
||||
} else {
|
||||
Box::new(StringBox::new("Error: pattern() requires string input (use '.' for short, '-' for long, ' ' for pause)"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// システム音量チェック(簡易)
|
||||
pub fn volumeTest(&self) -> Box<dyn NyashBox> {
|
||||
print!("\x07");
|
||||
Box::new(StringBox::new("Volume test beep - can you hear it?"))
|
||||
}
|
||||
|
||||
|
||||
/// 指定間隔でビープ
|
||||
pub fn interval(&self, times: Box<dyn NyashBox>, interval_ms: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
pub fn interval(
|
||||
&self,
|
||||
times: Box<dyn NyashBox>,
|
||||
interval_ms: Box<dyn NyashBox>,
|
||||
) -> Box<dyn NyashBox> {
|
||||
if let (Some(times_int), Some(interval_int)) = (
|
||||
times.as_any().downcast_ref::<IntegerBox>(),
|
||||
interval_ms.as_any().downcast_ref::<IntegerBox>()
|
||||
interval_ms.as_any().downcast_ref::<IntegerBox>(),
|
||||
) {
|
||||
if times_int.value <= 0 || interval_int.value < 0 {
|
||||
return Box::new(StringBox::new("Times must be positive, interval must be non-negative"));
|
||||
return Box::new(StringBox::new(
|
||||
"Times must be positive, interval must be non-negative",
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
for i in 0..times_int.value {
|
||||
print!("\x07");
|
||||
if i < times_int.value - 1 {
|
||||
std::thread::sleep(Duration::from_millis(interval_int.value as u64));
|
||||
}
|
||||
}
|
||||
|
||||
Box::new(StringBox::new(&format!("Played {} beeps with {}ms intervals", times_int.value, interval_int.value)))
|
||||
|
||||
Box::new(StringBox::new(&format!(
|
||||
"Played {} beeps with {}ms intervals",
|
||||
times_int.value, interval_int.value
|
||||
)))
|
||||
} else {
|
||||
Box::new(StringBox::new("Error: interval() requires two integer inputs (times, interval_ms)"))
|
||||
Box::new(StringBox::new(
|
||||
"Error: interval() requires two integer inputs (times, interval_ms)",
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -317,20 +343,20 @@ impl NyashBox for SoundBox {
|
||||
fn type_name(&self) -> &'static str {
|
||||
"SoundBox"
|
||||
}
|
||||
|
||||
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new("SoundBox()")
|
||||
}
|
||||
|
||||
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(other_sound) = other.as_any().downcast_ref::<SoundBox>() {
|
||||
BoolBox::new(self.base.id == other_sound.base.id)
|
||||
@ -338,27 +364,25 @@ impl NyashBox for SoundBox {
|
||||
BoolBox::new(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
impl BoxCore for SoundBox {
|
||||
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, "SoundBox()")
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
@ -368,4 +392,4 @@ impl Display for SoundBox {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.fmt_box(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,12 +2,12 @@
|
||||
// Nyashの箱システムによるストリーミング処理を提供します。
|
||||
// 参考: 既存Boxの設計思想
|
||||
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, IntegerBox, BoxCore, BoxBase};
|
||||
use crate::boxes::buffer::BufferBox;
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox};
|
||||
use crate::boxes::array::ArrayBox;
|
||||
use crate::boxes::buffer::BufferBox;
|
||||
use std::any::Any;
|
||||
use std::sync::RwLock;
|
||||
use std::io::Result;
|
||||
use std::sync::RwLock;
|
||||
|
||||
pub struct NyashStreamBox {
|
||||
buffer: RwLock<Vec<u8>>,
|
||||
@ -23,7 +23,7 @@ impl NyashStreamBox {
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn from_data(data: Vec<u8>) -> Self {
|
||||
NyashStreamBox {
|
||||
buffer: RwLock::new(data),
|
||||
@ -31,41 +31,41 @@ impl NyashStreamBox {
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
let buffer = self.buffer.read().unwrap();
|
||||
let mut position = self.position.write().unwrap();
|
||||
|
||||
|
||||
let available = buffer.len().saturating_sub(*position);
|
||||
let to_read = buf.len().min(available);
|
||||
|
||||
|
||||
if to_read == 0 {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
|
||||
buf[..to_read].copy_from_slice(&buffer[*position..*position + to_read]);
|
||||
*position += to_read;
|
||||
Ok(to_read)
|
||||
}
|
||||
|
||||
|
||||
pub fn write(&self, buf: &[u8]) -> Result<()> {
|
||||
let mut buffer = self.buffer.write().unwrap();
|
||||
buffer.extend_from_slice(buf);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.buffer.read().unwrap().len()
|
||||
}
|
||||
|
||||
|
||||
pub fn position(&self) -> usize {
|
||||
*self.position.read().unwrap()
|
||||
}
|
||||
|
||||
|
||||
pub fn reset(&self) {
|
||||
*self.position.write().unwrap() = 0;
|
||||
}
|
||||
|
||||
|
||||
/// ストリームに書き込み
|
||||
pub fn stream_write(&self, data: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
// BufferBoxから変換
|
||||
@ -96,38 +96,40 @@ impl NyashStreamBox {
|
||||
Err(e) => Box::new(StringBox::new(&format!("Error writing to stream: {}", e))),
|
||||
}
|
||||
} else {
|
||||
Box::new(StringBox::new("Error: write() requires BufferBox or StringBox"))
|
||||
Box::new(StringBox::new(
|
||||
"Error: write() requires BufferBox or StringBox",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// ストリームから読み込み
|
||||
pub fn stream_read(&self, count: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(count_int) = count.as_any().downcast_ref::<IntegerBox>() {
|
||||
let count_val = count_int.value as usize;
|
||||
let mut buf = vec![0u8; count_val];
|
||||
|
||||
|
||||
match self.read(&mut buf) {
|
||||
Ok(bytes_read) => {
|
||||
buf.truncate(bytes_read);
|
||||
Box::new(BufferBox::from_vec(buf))
|
||||
},
|
||||
}
|
||||
Err(e) => Box::new(StringBox::new(&format!("Error reading from stream: {}", e))),
|
||||
}
|
||||
} else {
|
||||
Box::new(StringBox::new("Error: read() requires integer count"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 現在位置を取得
|
||||
pub fn get_position(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(IntegerBox::new(self.position() as i64))
|
||||
}
|
||||
|
||||
|
||||
/// バッファサイズを取得
|
||||
pub fn get_length(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(IntegerBox::new(self.len() as i64))
|
||||
}
|
||||
|
||||
|
||||
/// ストリームをリセット
|
||||
pub fn stream_reset(&self) -> Box<dyn NyashBox> {
|
||||
self.reset();
|
||||
@ -139,7 +141,7 @@ impl NyashBox for NyashStreamBox {
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
@ -148,15 +150,17 @@ impl NyashBox for NyashStreamBox {
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
let buffer = self.buffer.read().unwrap();
|
||||
let position = self.position.read().unwrap();
|
||||
StringBox::new(format!("NyashStreamBox({} bytes, pos: {})", buffer.len(), *position))
|
||||
StringBox::new(format!(
|
||||
"NyashStreamBox({} bytes, pos: {})",
|
||||
buffer.len(),
|
||||
*position
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
"NyashStreamBox"
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(other_stream) = other.as_any().downcast_ref::<NyashStreamBox>() {
|
||||
let self_buffer = self.buffer.read().unwrap();
|
||||
@ -174,7 +178,7 @@ impl BoxCore for NyashStreamBox {
|
||||
fn box_id(&self) -> u64 {
|
||||
self.base.id
|
||||
}
|
||||
|
||||
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> {
|
||||
self.base.parent_type_id
|
||||
}
|
||||
@ -182,13 +186,18 @@ impl BoxCore for NyashStreamBox {
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let buffer = self.buffer.read().unwrap();
|
||||
let position = self.position.read().unwrap();
|
||||
write!(f, "NyashStreamBox({} bytes, pos: {})", buffer.len(), *position)
|
||||
write!(
|
||||
f,
|
||||
"NyashStreamBox({} bytes, pos: {})",
|
||||
buffer.len(),
|
||||
*position
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
/*! 🔤 StringBox - 文字列操作Box
|
||||
*
|
||||
*
|
||||
* ## 📝 概要
|
||||
* UTF-8エンコード文字列を扱うためのBox。
|
||||
* JavaScript風のメソッドで直感的な文字列操作が可能。
|
||||
*
|
||||
*
|
||||
* ## 🛠️ 利用可能メソッド
|
||||
* - `length()` - 文字列長を取得
|
||||
* - `concat(other)` - 文字列結合
|
||||
@ -15,19 +15,19 @@
|
||||
* - `indexOf(search)` - 文字列検索
|
||||
* - `replace(from, to)` - 文字列置換
|
||||
* - `charAt(index)` - 指定位置の文字取得
|
||||
*
|
||||
*
|
||||
* ## 💡 使用例
|
||||
* ```nyash
|
||||
* local text, parts, result
|
||||
* text = "Hello, World!"
|
||||
*
|
||||
*
|
||||
* print(text.length()) // 13
|
||||
* print(text.toUpperCase()) // "HELLO, WORLD!"
|
||||
* parts = text.split(",") // ["Hello", " World!"]
|
||||
* result = text.concat(" Nyash") // "Hello, World! Nyash"
|
||||
* ```
|
||||
*/
|
||||
use crate::box_trait::{NyashBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoxBase, BoxCore, NyashBox};
|
||||
use std::any::Any;
|
||||
use std::fmt::Display;
|
||||
|
||||
@ -45,18 +45,19 @@ impl StringBox {
|
||||
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> {
|
||||
use crate::boxes::array::ArrayBox;
|
||||
let parts: Vec<String> = self.value.split(delimiter).map(|s| s.to_string()).collect();
|
||||
let array_elements: Vec<Box<dyn NyashBox>> = parts.into_iter()
|
||||
let array_elements: Vec<Box<dyn NyashBox>> = parts
|
||||
.into_iter()
|
||||
.map(|s| Box::new(StringBox::new(s)) as Box<dyn NyashBox>)
|
||||
.collect();
|
||||
let result = ArrayBox::new();
|
||||
@ -65,7 +66,7 @@ impl StringBox {
|
||||
}
|
||||
Box::new(result)
|
||||
}
|
||||
|
||||
|
||||
/// Find substring and return position (or -1 if not found)
|
||||
pub fn find(&self, search: &str) -> Box<dyn NyashBox> {
|
||||
use crate::boxes::integer_box::IntegerBox;
|
||||
@ -74,50 +75,53 @@ impl StringBox {
|
||||
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> {
|
||||
use crate::boxes::bool_box::BoolBox;
|
||||
Box::new(BoolBox::new(self.value.contains(search)))
|
||||
}
|
||||
|
||||
|
||||
/// Check if string starts with prefix
|
||||
pub fn starts_with(&self, prefix: &str) -> Box<dyn NyashBox> {
|
||||
use crate::boxes::bool_box::BoolBox;
|
||||
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> {
|
||||
use crate::boxes::bool_box::BoolBox;
|
||||
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> {
|
||||
use crate::boxes::array::ArrayBox;
|
||||
if let Some(array) = array_box.as_any().downcast_ref::<ArrayBox>() {
|
||||
let strings: Vec<String> = array.items.read().unwrap()
|
||||
let strings: Vec<String> = array
|
||||
.items
|
||||
.read()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|element| element.to_string_box().value)
|
||||
.collect();
|
||||
@ -127,7 +131,7 @@ impl StringBox {
|
||||
Box::new(StringBox::new(array_box.to_string_box().value))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Convert string to integer (parse as i64)
|
||||
pub fn to_integer(&self) -> Box<dyn NyashBox> {
|
||||
use crate::boxes::integer_box::IntegerBox;
|
||||
@ -145,7 +149,7 @@ impl NyashBox for StringBox {
|
||||
fn to_string_box(&self) -> crate::box_trait::StringBox {
|
||||
crate::box_trait::StringBox::new(self.value.clone())
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> crate::box_trait::BoolBox {
|
||||
use crate::box_trait::BoolBox;
|
||||
if let Some(other_string) = other.as_any().downcast_ref::<StringBox>() {
|
||||
@ -154,16 +158,15 @@ impl NyashBox for StringBox {
|
||||
BoolBox::new(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
"StringBox"
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
@ -174,19 +177,19 @@ 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
|
||||
}
|
||||
@ -196,4 +199,4 @@ impl Display for StringBox {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.fmt_box(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use crate::box_trait::{NyashBox, BoxCore, BoxBase, StringBox, BoolBox, VoidBox};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox, VoidBox};
|
||||
use std::any::Any;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
@ -17,9 +17,17 @@ pub struct TaskGroupBox {
|
||||
|
||||
impl TaskGroupBox {
|
||||
pub fn new() -> Self {
|
||||
Self { base: BoxBase::new(), cancelled: false, inner: Arc::new(TaskGroupInner { strong: Mutex::new(Vec::new()) }) }
|
||||
Self {
|
||||
base: BoxBase::new(),
|
||||
cancelled: false,
|
||||
inner: Arc::new(TaskGroupInner {
|
||||
strong: Mutex::new(Vec::new()),
|
||||
}),
|
||||
}
|
||||
}
|
||||
pub fn cancel_all(&mut self) {
|
||||
self.cancelled = true;
|
||||
}
|
||||
pub fn cancel_all(&mut self) { self.cancelled = true; }
|
||||
/// Cancel all child tasks (scaffold) and return void
|
||||
pub fn cancelAll(&mut self) -> Box<dyn NyashBox> {
|
||||
self.cancel_all();
|
||||
@ -31,7 +39,9 @@ impl TaskGroupBox {
|
||||
self.join_all_inner(ms);
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn is_cancelled(&self) -> bool { self.cancelled }
|
||||
pub fn is_cancelled(&self) -> bool {
|
||||
self.cancelled
|
||||
}
|
||||
|
||||
/// Register a Future into this group's ownership
|
||||
pub fn add_future(&self, fut: &crate::boxes::future::FutureBox) {
|
||||
@ -47,10 +57,16 @@ impl TaskGroupBox {
|
||||
let mut all_ready = true;
|
||||
if let Ok(mut list) = self.inner.strong.lock() {
|
||||
list.retain(|f| !f.ready());
|
||||
if !list.is_empty() { all_ready = false; }
|
||||
if !list.is_empty() {
|
||||
all_ready = false;
|
||||
}
|
||||
}
|
||||
if all_ready {
|
||||
break;
|
||||
}
|
||||
if Instant::now() >= deadline {
|
||||
break;
|
||||
}
|
||||
if all_ready { break; }
|
||||
if Instant::now() >= deadline { break; }
|
||||
crate::runtime::global_hooks::safepoint_and_poll();
|
||||
std::thread::yield_now();
|
||||
}
|
||||
@ -58,20 +74,38 @@ impl TaskGroupBox {
|
||||
}
|
||||
|
||||
impl BoxCore for TaskGroupBox {
|
||||
fn box_id(&self) -> u64 { self.base.id }
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> { None }
|
||||
fn box_id(&self) -> u64 {
|
||||
self.base.id
|
||||
}
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> {
|
||||
None
|
||||
}
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "TaskGroup(cancelled={})", self.cancelled)
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any { self }
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any { self }
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl NyashBox for TaskGroupBox {
|
||||
fn to_string_box(&self) -> StringBox { StringBox::new(format!("TaskGroup(cancelled={})", self.cancelled)) }
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(g) = other.as_any().downcast_ref::<TaskGroupBox>() { BoolBox::new(self.base.id == g.base.id) } else { BoolBox::new(false) }
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new(format!("TaskGroup(cancelled={})", self.cancelled))
|
||||
}
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(g) = other.as_any().downcast_ref::<TaskGroupBox>() {
|
||||
BoolBox::new(self.base.id == g.base.id)
|
||||
} else {
|
||||
BoolBox::new(false)
|
||||
}
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(self.clone()) }
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||
}
|
||||
|
||||
@ -1,18 +1,18 @@
|
||||
/*! ⏰ TimeBox - 時間・日付操作Box
|
||||
*
|
||||
*
|
||||
* ## 📝 概要
|
||||
* 高精度な時間・日付操作を提供するBox。
|
||||
* JavaScript Date、Python datetime、C# DateTimeと同等機能。
|
||||
* タイムスタンプ処理、フォーマット、時差計算をサポート。
|
||||
*
|
||||
*
|
||||
* ## 🛠️ 利用可能メソッド
|
||||
*
|
||||
*
|
||||
* ### 📅 基本操作
|
||||
* - `now()` - 現在日時取得
|
||||
* - `fromTimestamp(timestamp)` - UNIXタイムスタンプから日時作成
|
||||
* - `parse(date_string)` - 文字列から日時パース
|
||||
* - `format(pattern)` - 指定フォーマットで文字列化
|
||||
*
|
||||
*
|
||||
* ### 🔢 値取得
|
||||
* - `year()` - 年取得
|
||||
* - `month()` - 月取得 (1-12)
|
||||
@ -21,34 +21,34 @@
|
||||
* - `minute()` - 分取得 (0-59)
|
||||
* - `second()` - 秒取得 (0-59)
|
||||
* - `weekday()` - 曜日取得 (0=日曜)
|
||||
*
|
||||
*
|
||||
* ### ⏱️ 計算
|
||||
* - `addDays(days)` - 日数加算
|
||||
* - `addHours(hours)` - 時間加算
|
||||
* - `addMinutes(minutes)` - 分加算
|
||||
* - `diffDays(other)` - 日数差計算
|
||||
* - `diffHours(other)` - 時間差計算
|
||||
*
|
||||
*
|
||||
* ## 💡 使用例
|
||||
* ```nyash
|
||||
* local time, now, birthday, age
|
||||
* time = new TimeBox()
|
||||
*
|
||||
*
|
||||
* // 現在日時
|
||||
* now = time.now()
|
||||
* print("現在: " + now.format("yyyy/MM/dd HH:mm:ss"))
|
||||
*
|
||||
*
|
||||
* // 誕生日から年齢計算
|
||||
* birthday = time.parse("1995-03-15")
|
||||
* age = now.diffYears(birthday)
|
||||
* print("年齢: " + age.toString() + "歳")
|
||||
*
|
||||
*
|
||||
* // 1週間後
|
||||
* local next_week
|
||||
* next_week = now.addDays(7)
|
||||
* print("1週間後: " + next_week.format("MM月dd日"))
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* ## 🎮 実用例 - イベントスケジューラー
|
||||
* ```nyash
|
||||
* static box EventScheduler {
|
||||
@ -83,34 +83,34 @@
|
||||
* event = me.events.get(i)
|
||||
* hours_until = event.get("datetime").diffHours(me.current)
|
||||
*
|
||||
* print(event.get("title") + " - " +
|
||||
* print(event.get("title") + " - " +
|
||||
* hours_until.toString() + "時間後")
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* ## 🕐 時間計算例
|
||||
* ```nyash
|
||||
* local time, start, end, duration
|
||||
* time = new TimeBox()
|
||||
*
|
||||
*
|
||||
* // 作業時間計測
|
||||
* start = time.now()
|
||||
* // 何か重い処理...
|
||||
* heavyCalculation()
|
||||
* end = time.now()
|
||||
*
|
||||
*
|
||||
* duration = end.diffSeconds(start)
|
||||
* print("処理時間: " + duration.toString() + "秒")
|
||||
*
|
||||
*
|
||||
* // 締切まで残り時間
|
||||
* local deadline, remaining
|
||||
* deadline = time.parse("2025-12-31 23:59:59")
|
||||
* remaining = deadline.diffDays(time.now())
|
||||
* print("締切まで" + remaining.toString() + "日")
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* ## ⚠️ 注意
|
||||
* - ローカルタイムゾーンに基づく処理
|
||||
* - パース可能な日時フォーマットは限定的
|
||||
@ -118,11 +118,11 @@
|
||||
* - 夏時間切り替え時は計算に注意
|
||||
*/
|
||||
|
||||
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, BoxCore, BoxBase};
|
||||
use std::fmt::{Debug, Display};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox};
|
||||
use chrono::{DateTime, Datelike, Local, TimeZone, Timelike};
|
||||
use std::any::Any;
|
||||
use std::time::{SystemTime, Duration};
|
||||
use chrono::{DateTime, Local, TimeZone, Datelike, Timelike};
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
/// 時間操作を提供するBox
|
||||
#[derive(Debug, Clone)]
|
||||
@ -132,23 +132,27 @@ pub struct TimeBox {
|
||||
|
||||
impl TimeBox {
|
||||
pub fn new() -> Self {
|
||||
Self { base: BoxBase::new() }
|
||||
Self {
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 現在時刻を取得
|
||||
pub fn now(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(DateTimeBox::now())
|
||||
}
|
||||
|
||||
|
||||
/// UNIXタイムスタンプから日時を作成
|
||||
pub fn fromTimestamp(&self, timestamp: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(int_box) = timestamp.as_any().downcast_ref::<IntegerBox>() {
|
||||
Box::new(DateTimeBox::from_timestamp(int_box.value))
|
||||
} else {
|
||||
Box::new(StringBox::new("Error: fromTimestamp() requires integer input"))
|
||||
Box::new(StringBox::new(
|
||||
"Error: fromTimestamp() requires integer input",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 日時文字列をパース
|
||||
pub fn parse(&self, date_str: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(string_box) = date_str.as_any().downcast_ref::<StringBox>() {
|
||||
@ -160,7 +164,7 @@ impl TimeBox {
|
||||
Box::new(StringBox::new("Error: parse() requires string input"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// ミリ秒スリープ
|
||||
pub fn sleep(&self, millis: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(int_box) = millis.as_any().downcast_ref::<IntegerBox>() {
|
||||
@ -168,13 +172,15 @@ impl TimeBox {
|
||||
std::thread::sleep(Duration::from_millis(int_box.value as u64));
|
||||
Box::new(StringBox::new("ok"))
|
||||
} else {
|
||||
Box::new(StringBox::new("Error: sleep() requires positive milliseconds"))
|
||||
Box::new(StringBox::new(
|
||||
"Error: sleep() requires positive milliseconds",
|
||||
))
|
||||
}
|
||||
} else {
|
||||
Box::new(StringBox::new("Error: sleep() requires integer input"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 現在時刻をフォーマット
|
||||
pub fn format(&self, format_str: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(str_box) = format_str.as_any().downcast_ref::<StringBox>() {
|
||||
@ -182,7 +188,9 @@ impl TimeBox {
|
||||
let formatted = now.format(&str_box.value).to_string();
|
||||
Box::new(StringBox::new(formatted))
|
||||
} else {
|
||||
Box::new(StringBox::new("Error: format() requires string format pattern"))
|
||||
Box::new(StringBox::new(
|
||||
"Error: format() requires string format pattern",
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -191,20 +199,20 @@ impl NyashBox for TimeBox {
|
||||
fn type_name(&self) -> &'static str {
|
||||
"TimeBox"
|
||||
}
|
||||
|
||||
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new("TimeBox()")
|
||||
}
|
||||
|
||||
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(other_time) = other.as_any().downcast_ref::<TimeBox>() {
|
||||
BoolBox::new(self.base.id == other_time.base.id)
|
||||
@ -212,26 +220,25 @@ impl NyashBox for TimeBox {
|
||||
BoolBox::new(false)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl BoxCore for TimeBox {
|
||||
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, "TimeBox()")
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
@ -253,23 +260,26 @@ pub struct DateTimeBox {
|
||||
impl DateTimeBox {
|
||||
/// 現在時刻で作成
|
||||
pub fn now() -> Self {
|
||||
Self {
|
||||
Self {
|
||||
datetime: Local::now(),
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// UNIXタイムスタンプから作成
|
||||
pub fn from_timestamp(timestamp: i64) -> Self {
|
||||
let datetime = Local.timestamp_opt(timestamp, 0).unwrap();
|
||||
Self { datetime, base: BoxBase::new() }
|
||||
Self {
|
||||
datetime,
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 文字列からパース
|
||||
pub fn parse(date_str: &str) -> Result<Self, String> {
|
||||
// ISO 8601形式でパース
|
||||
match DateTime::parse_from_rfc3339(date_str) {
|
||||
Ok(dt) => Ok(Self {
|
||||
Ok(dt) => Ok(Self {
|
||||
datetime: dt.with_timezone(&Local),
|
||||
base: BoxBase::new(),
|
||||
}),
|
||||
@ -278,54 +288,57 @@ impl DateTimeBox {
|
||||
match chrono::NaiveDateTime::parse_from_str(date_str, "%Y-%m-%d %H:%M:%S") {
|
||||
Ok(naive_dt) => {
|
||||
let datetime = Local.from_local_datetime(&naive_dt).unwrap();
|
||||
Ok(Self { datetime, base: BoxBase::new() })
|
||||
Ok(Self {
|
||||
datetime,
|
||||
base: BoxBase::new(),
|
||||
})
|
||||
}
|
||||
Err(e) => Err(format!("Failed to parse date: {}", e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 年を取得
|
||||
pub fn year(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(IntegerBox::new(self.datetime.year() as i64))
|
||||
}
|
||||
|
||||
|
||||
/// 月を取得
|
||||
pub fn month(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(IntegerBox::new(self.datetime.month() as i64))
|
||||
}
|
||||
|
||||
|
||||
/// 日を取得
|
||||
pub fn day(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(IntegerBox::new(self.datetime.day() as i64))
|
||||
}
|
||||
|
||||
|
||||
/// 時を取得
|
||||
pub fn hour(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(IntegerBox::new(self.datetime.hour() as i64))
|
||||
}
|
||||
|
||||
|
||||
/// 分を取得
|
||||
pub fn minute(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(IntegerBox::new(self.datetime.minute() as i64))
|
||||
}
|
||||
|
||||
|
||||
/// 秒を取得
|
||||
pub fn second(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(IntegerBox::new(self.datetime.second() as i64))
|
||||
}
|
||||
|
||||
|
||||
/// UNIXタイムスタンプを取得
|
||||
pub fn timestamp(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(IntegerBox::new(self.datetime.timestamp()))
|
||||
}
|
||||
|
||||
|
||||
/// ISO 8601形式でフォーマット
|
||||
pub fn toISOString(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(StringBox::new(&self.datetime.to_rfc3339()))
|
||||
}
|
||||
|
||||
|
||||
/// カスタムフォーマット
|
||||
pub fn format(&self, fmt: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(string_box) = fmt.as_any().downcast_ref::<StringBox>() {
|
||||
@ -335,7 +348,7 @@ impl DateTimeBox {
|
||||
Box::new(StringBox::new("Error: format() requires string input"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 日付を加算
|
||||
pub fn addDays(&self, days: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(int_box) = days.as_any().downcast_ref::<IntegerBox>() {
|
||||
@ -348,7 +361,7 @@ impl DateTimeBox {
|
||||
Box::new(StringBox::new("Error: addDays() requires integer input"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 時間を加算
|
||||
pub fn addHours(&self, hours: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(int_box) = hours.as_any().downcast_ref::<IntegerBox>() {
|
||||
@ -367,20 +380,20 @@ impl NyashBox for DateTimeBox {
|
||||
fn type_name(&self) -> &'static str {
|
||||
"DateTimeBox"
|
||||
}
|
||||
|
||||
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new(&self.datetime.format("%Y-%m-%d %H:%M:%S").to_string())
|
||||
}
|
||||
|
||||
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(other_dt) = other.as_any().downcast_ref::<DateTimeBox>() {
|
||||
BoolBox::new(self.datetime == other_dt.datetime)
|
||||
@ -388,26 +401,25 @@ impl NyashBox for DateTimeBox {
|
||||
BoolBox::new(false)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl BoxCore for DateTimeBox {
|
||||
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.datetime.format("%Y-%m-%d %H:%M:%S"))
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
@ -433,7 +445,7 @@ impl TimerBox {
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 経過時間をミリ秒で取得
|
||||
pub fn elapsed(&self) -> Box<dyn NyashBox> {
|
||||
match self.start_time.elapsed() {
|
||||
@ -444,7 +456,7 @@ impl TimerBox {
|
||||
Err(_) => Box::new(IntegerBox::new(0)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// タイマーをリセット
|
||||
pub fn reset(&mut self) -> Box<dyn NyashBox> {
|
||||
self.start_time = SystemTime::now();
|
||||
@ -456,20 +468,20 @@ impl NyashBox for TimerBox {
|
||||
fn type_name(&self) -> &'static str {
|
||||
"TimerBox"
|
||||
}
|
||||
|
||||
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new("TimerBox()")
|
||||
}
|
||||
|
||||
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(other_timer) = other.as_any().downcast_ref::<TimerBox>() {
|
||||
BoolBox::new(self.base.id == other_timer.base.id)
|
||||
@ -477,26 +489,25 @@ impl NyashBox for TimerBox {
|
||||
BoolBox::new(false)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl BoxCore for TimerBox {
|
||||
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, "TimerBox()")
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
@ -506,4 +517,4 @@ impl Display for TimerBox {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.fmt_box(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,41 +1,41 @@
|
||||
/*!
|
||||
* TimerBox - JavaScript風タイマー機能Box
|
||||
*
|
||||
*
|
||||
* ## 📝 概要
|
||||
* setTimeout/setInterval/requestAnimationFrameをNyashから利用可能にするBox。
|
||||
* アニメーション、遅延実行、定期実行を統一的に管理。
|
||||
*
|
||||
*
|
||||
* ## 🛠️ 利用可能メソッド
|
||||
*
|
||||
*
|
||||
* ### ⏱️ 基本タイマー
|
||||
* - `setTimeout(callback, delay)` - 指定時間後に1回実行
|
||||
* - `setInterval(callback, interval)` - 指定間隔で繰り返し実行
|
||||
* - `clearTimeout(id)` - タイマーをキャンセル
|
||||
* - `clearInterval(id)` - インターバルをキャンセル
|
||||
*
|
||||
*
|
||||
* ### 🎮 アニメーション
|
||||
* - `requestAnimationFrame(callback)` - 次フレームで実行
|
||||
* - `cancelAnimationFrame(id)` - アニメーションをキャンセル
|
||||
*
|
||||
*
|
||||
* ### 📊 時間測定
|
||||
* - `now()` - 現在時刻(ミリ秒)
|
||||
* - `performance()` - 高精度時刻測定
|
||||
*
|
||||
*
|
||||
* ## 💡 使用例
|
||||
* ```nyash
|
||||
* local timer, id
|
||||
* timer = new TimerBox()
|
||||
*
|
||||
*
|
||||
* // 1秒後に実行
|
||||
* id = timer.setTimeout(function() {
|
||||
* print("Hello after 1 second!")
|
||||
* }, 1000)
|
||||
*
|
||||
*
|
||||
* // 500msごとに実行
|
||||
* id = timer.setInterval(function() {
|
||||
* print("Tick every 500ms")
|
||||
* }, 500)
|
||||
*
|
||||
*
|
||||
* // アニメーションループ
|
||||
* timer.requestAnimationFrame(function() {
|
||||
* // 描画処理
|
||||
@ -45,7 +45,7 @@
|
||||
* ```
|
||||
*/
|
||||
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
|
||||
use std::any::Any;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
@ -81,7 +81,7 @@ impl TimerBox {
|
||||
js_sys::Date::now()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
@ -101,7 +101,8 @@ impl TimerBox {
|
||||
/// setTimeout相当の遅延実行
|
||||
pub fn set_timeout(&self, callback: &js_sys::Function, delay: i32) -> i32 {
|
||||
if let Some(window) = window() {
|
||||
window.set_timeout_with_callback_and_timeout_and_arguments_0(callback, delay)
|
||||
window
|
||||
.set_timeout_with_callback_and_timeout_and_arguments_0(callback, delay)
|
||||
.unwrap_or(-1)
|
||||
} else {
|
||||
-1
|
||||
@ -112,7 +113,8 @@ impl TimerBox {
|
||||
/// setInterval相当の定期実行
|
||||
pub fn set_interval(&self, callback: &js_sys::Function, interval: i32) -> i32 {
|
||||
if let Some(window) = window() {
|
||||
window.set_interval_with_callback_and_timeout_and_arguments_0(callback, interval)
|
||||
window
|
||||
.set_interval_with_callback_and_timeout_and_arguments_0(callback, interval)
|
||||
.unwrap_or(-1)
|
||||
} else {
|
||||
-1
|
||||
@ -192,19 +194,19 @@ impl BoxCore for TimerBox {
|
||||
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, "TimerBox(id={})", self.base.id)
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
@ -214,7 +216,7 @@ impl NyashBox for TimerBox {
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
@ -227,7 +229,7 @@ impl NyashBox for TimerBox {
|
||||
fn type_name(&self) -> &'static str {
|
||||
"TimerBox"
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(other_timer) = other.as_any().downcast_ref::<TimerBox>() {
|
||||
BoolBox::new(self.base.id == other_timer.base.id)
|
||||
@ -241,4 +243,4 @@ impl std::fmt::Display for TimerBox {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.fmt_box(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use crate::box_trait::{NyashBox, BoxCore, BoxBase, StringBox, BoolBox};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
|
||||
use std::any::Any;
|
||||
|
||||
/// Cancellation token as a Box for structured concurrency
|
||||
@ -9,30 +9,69 @@ pub struct TokenBox {
|
||||
}
|
||||
|
||||
impl TokenBox {
|
||||
pub fn new() -> Self { Self { base: BoxBase::new(), token: crate::runtime::scheduler::CancellationToken::new() } }
|
||||
pub fn from_token(token: crate::runtime::scheduler::CancellationToken) -> Self { Self { base: BoxBase::new(), token } }
|
||||
pub fn cancel(&self) { self.token.cancel(); }
|
||||
pub fn is_cancelled(&self) -> bool { self.token.is_cancelled() }
|
||||
pub fn inner(&self) -> crate::runtime::scheduler::CancellationToken { self.token.clone() }
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
base: BoxBase::new(),
|
||||
token: crate::runtime::scheduler::CancellationToken::new(),
|
||||
}
|
||||
}
|
||||
pub fn from_token(token: crate::runtime::scheduler::CancellationToken) -> Self {
|
||||
Self {
|
||||
base: BoxBase::new(),
|
||||
token,
|
||||
}
|
||||
}
|
||||
pub fn cancel(&self) {
|
||||
self.token.cancel();
|
||||
}
|
||||
pub fn is_cancelled(&self) -> bool {
|
||||
self.token.is_cancelled()
|
||||
}
|
||||
pub fn inner(&self) -> crate::runtime::scheduler::CancellationToken {
|
||||
self.token.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl BoxCore for TokenBox {
|
||||
fn box_id(&self) -> u64 { self.base.id }
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> { None }
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "CancellationToken(cancelled={})", self.token.is_cancelled())
|
||||
fn box_id(&self) -> u64 {
|
||||
self.base.id
|
||||
}
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> {
|
||||
None
|
||||
}
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"CancellationToken(cancelled={})",
|
||||
self.token.is_cancelled()
|
||||
)
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any { self }
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any { self }
|
||||
}
|
||||
|
||||
impl NyashBox for TokenBox {
|
||||
fn to_string_box(&self) -> StringBox { StringBox::new(format!("CancellationToken(cancelled={})", self.token.is_cancelled())) }
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new(format!(
|
||||
"CancellationToken(cancelled={})",
|
||||
self.token.is_cancelled()
|
||||
))
|
||||
}
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(o) = other.as_any().downcast_ref::<TokenBox>() {
|
||||
BoolBox::new(self.is_cancelled() == o.is_cancelled())
|
||||
} else { BoolBox::new(false) }
|
||||
} else {
|
||||
BoolBox::new(false)
|
||||
}
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(self.clone()) }
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { Box::new(self.clone()) }
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Web Boxes Module - ブラウザ専用Box群
|
||||
*
|
||||
*
|
||||
* WebAssembly環境専用のBox群を管理
|
||||
* HTML5 APIs、DOM操作、Canvas描画等をNyashから利用可能にする
|
||||
*/
|
||||
@ -21,4 +21,4 @@ pub use web_display_box::WebDisplayBox;
|
||||
pub use web_console_box::WebConsoleBox;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub use web_canvas_box::WebCanvasBox;
|
||||
pub use web_canvas_box::WebCanvasBox;
|
||||
|
||||
@ -1,21 +1,18 @@
|
||||
/*!
|
||||
* WebCanvasBox - ブラウザCanvas完全制御Box
|
||||
*
|
||||
*
|
||||
* WebAssembly環境でHTML5 Canvasの完全制御
|
||||
* ピクセルの世界を制圧する革命的Box!
|
||||
*/
|
||||
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
|
||||
use std::any::Any;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use web_sys::{
|
||||
HtmlCanvasElement,
|
||||
CanvasRenderingContext2d,
|
||||
};
|
||||
use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement};
|
||||
|
||||
// 🎨 Browser Canvas complete control Box
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
@ -30,22 +27,22 @@ pub struct WebCanvasBox {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
impl WebCanvasBox {
|
||||
pub fn new(canvas_id: String, width: u32, height: u32) -> Self {
|
||||
let instance = Self {
|
||||
let instance = Self {
|
||||
base: BoxBase::new(),
|
||||
canvas_id: canvas_id.clone(),
|
||||
width,
|
||||
height,
|
||||
};
|
||||
|
||||
|
||||
// キャンバス要素を初期化
|
||||
if let Some(canvas) = instance.get_canvas_element() {
|
||||
canvas.set_width(width);
|
||||
canvas.set_height(height);
|
||||
}
|
||||
|
||||
|
||||
instance
|
||||
}
|
||||
|
||||
|
||||
/// Canvas要素を取得
|
||||
fn get_canvas_element(&self) -> Option<HtmlCanvasElement> {
|
||||
let window = web_sys::window()?;
|
||||
@ -53,7 +50,7 @@ impl WebCanvasBox {
|
||||
let element = document.get_element_by_id(&self.canvas_id)?;
|
||||
element.dyn_into::<HtmlCanvasElement>().ok()
|
||||
}
|
||||
|
||||
|
||||
/// 2Dレンダリングコンテキストを取得
|
||||
fn get_2d_context(&self) -> Option<CanvasRenderingContext2d> {
|
||||
let canvas = self.get_canvas_element()?;
|
||||
@ -62,35 +59,35 @@ impl WebCanvasBox {
|
||||
.ok()?
|
||||
.and_then(|ctx| ctx.dyn_into::<CanvasRenderingContext2d>().ok())
|
||||
}
|
||||
|
||||
|
||||
/// キャンバスをクリア
|
||||
pub fn clear(&self) {
|
||||
if let Some(ctx) = self.get_2d_context() {
|
||||
ctx.clear_rect(0.0, 0.0, self.width as f64, self.height as f64);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 塗りつぶし色を設定
|
||||
pub fn set_fill_style(&self, color: &str) {
|
||||
if let Some(ctx) = self.get_2d_context() {
|
||||
ctx.set_fill_style(&wasm_bindgen::JsValue::from_str(color));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 線の色を設定
|
||||
pub fn set_stroke_style(&self, color: &str) {
|
||||
if let Some(ctx) = self.get_2d_context() {
|
||||
ctx.set_stroke_style(&wasm_bindgen::JsValue::from_str(color));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 線の太さを設定
|
||||
pub fn set_line_width(&self, width: f64) {
|
||||
if let Some(ctx) = self.get_2d_context() {
|
||||
ctx.set_line_width(width);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 塗りつぶし矩形を描画
|
||||
pub fn fill_rect(&self, x: f64, y: f64, width: f64, height: f64, color: &str) {
|
||||
if let Some(ctx) = self.get_2d_context() {
|
||||
@ -98,37 +95,47 @@ impl WebCanvasBox {
|
||||
ctx.fill_rect(x, y, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 枠線矩形を描画
|
||||
pub fn stroke_rect(&self, x: f64, y: f64, width: f64, height: f64, color: &str, line_width: f64) {
|
||||
pub fn stroke_rect(
|
||||
&self,
|
||||
x: f64,
|
||||
y: f64,
|
||||
width: f64,
|
||||
height: f64,
|
||||
color: &str,
|
||||
line_width: f64,
|
||||
) {
|
||||
if let Some(ctx) = self.get_2d_context() {
|
||||
ctx.set_stroke_style(&wasm_bindgen::JsValue::from_str(color));
|
||||
ctx.set_line_width(line_width);
|
||||
ctx.stroke_rect(x, y, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 塗りつぶし円を描画
|
||||
pub fn fill_circle(&self, x: f64, y: f64, radius: f64, color: &str) {
|
||||
if let Some(ctx) = self.get_2d_context() {
|
||||
ctx.set_fill_style(&wasm_bindgen::JsValue::from_str(color));
|
||||
ctx.begin_path();
|
||||
ctx.arc(x, y, radius, 0.0, 2.0 * std::f64::consts::PI).unwrap_or_default();
|
||||
ctx.arc(x, y, radius, 0.0, 2.0 * std::f64::consts::PI)
|
||||
.unwrap_or_default();
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 枠線円を描画
|
||||
pub fn stroke_circle(&self, x: f64, y: f64, radius: f64, color: &str, line_width: f64) {
|
||||
if let Some(ctx) = self.get_2d_context() {
|
||||
ctx.set_stroke_style(&wasm_bindgen::JsValue::from_str(color));
|
||||
ctx.set_line_width(line_width);
|
||||
ctx.begin_path();
|
||||
ctx.arc(x, y, radius, 0.0, 2.0 * std::f64::consts::PI).unwrap_or_default();
|
||||
ctx.arc(x, y, radius, 0.0, 2.0 * std::f64::consts::PI)
|
||||
.unwrap_or_default();
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 直線を描画
|
||||
pub fn draw_line(&self, x1: f64, y1: f64, x2: f64, y2: f64, color: &str, line_width: f64) {
|
||||
if let Some(ctx) = self.get_2d_context() {
|
||||
@ -140,7 +147,7 @@ impl WebCanvasBox {
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// テキストを描画(塗りつぶし)
|
||||
pub fn fill_text(&self, text: &str, x: f64, y: f64, font: &str, color: &str) {
|
||||
if let Some(ctx) = self.get_2d_context() {
|
||||
@ -149,9 +156,17 @@ impl WebCanvasBox {
|
||||
ctx.fill_text(text, x, y).unwrap_or_default();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// テキストを描画(枠線)
|
||||
pub fn stroke_text(&self, text: &str, x: f64, y: f64, font: &str, color: &str, line_width: f64) {
|
||||
pub fn stroke_text(
|
||||
&self,
|
||||
text: &str,
|
||||
x: f64,
|
||||
y: f64,
|
||||
font: &str,
|
||||
color: &str,
|
||||
line_width: f64,
|
||||
) {
|
||||
if let Some(ctx) = self.get_2d_context() {
|
||||
ctx.set_font(font);
|
||||
ctx.set_stroke_style(&wasm_bindgen::JsValue::from_str(color));
|
||||
@ -159,35 +174,35 @@ impl WebCanvasBox {
|
||||
ctx.stroke_text(text, x, y).unwrap_or_default();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// パス描画開始
|
||||
pub fn begin_path(&self) {
|
||||
if let Some(ctx) = self.get_2d_context() {
|
||||
ctx.begin_path();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// パスを指定位置に移動
|
||||
pub fn move_to(&self, x: f64, y: f64) {
|
||||
if let Some(ctx) = self.get_2d_context() {
|
||||
ctx.move_to(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// パスに直線を追加
|
||||
pub fn line_to(&self, x: f64, y: f64) {
|
||||
if let Some(ctx) = self.get_2d_context() {
|
||||
ctx.line_to(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// パスを閉じる
|
||||
pub fn close_path(&self) {
|
||||
if let Some(ctx) = self.get_2d_context() {
|
||||
ctx.close_path();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// パスを塗りつぶし
|
||||
pub fn fill(&self, color: &str) {
|
||||
if let Some(ctx) = self.get_2d_context() {
|
||||
@ -195,7 +210,7 @@ impl WebCanvasBox {
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// パスを枠線描画
|
||||
pub fn stroke(&self, color: &str, line_width: f64) {
|
||||
if let Some(ctx) = self.get_2d_context() {
|
||||
@ -204,56 +219,56 @@ impl WebCanvasBox {
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 現在の描画状態を保存
|
||||
pub fn save(&self) {
|
||||
if let Some(ctx) = self.get_2d_context() {
|
||||
ctx.save();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 描画状態を復元
|
||||
pub fn restore(&self) {
|
||||
if let Some(ctx) = self.get_2d_context() {
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 座標系を回転
|
||||
pub fn rotate(&self, angle: f64) {
|
||||
if let Some(ctx) = self.get_2d_context() {
|
||||
ctx.rotate(angle).unwrap_or_default();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 座標系をスケール
|
||||
pub fn scale(&self, x: f64, y: f64) {
|
||||
if let Some(ctx) = self.get_2d_context() {
|
||||
ctx.scale(x, y).unwrap_or_default();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 座標系を平行移動
|
||||
pub fn translate(&self, x: f64, y: f64) {
|
||||
if let Some(ctx) = self.get_2d_context() {
|
||||
ctx.translate(x, y).unwrap_or_default();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// キャンバスのサイズを取得
|
||||
pub fn get_width(&self) -> u32 {
|
||||
self.width
|
||||
}
|
||||
|
||||
|
||||
pub fn get_height(&self) -> u32 {
|
||||
self.height
|
||||
}
|
||||
|
||||
|
||||
/// キャンバスのサイズを変更
|
||||
pub fn resize(&mut self, width: u32, height: u32) {
|
||||
self.width = width;
|
||||
self.height = height;
|
||||
|
||||
|
||||
if let Some(canvas) = self.get_canvas_element() {
|
||||
canvas.set_width(width);
|
||||
canvas.set_height(height);
|
||||
@ -266,19 +281,23 @@ impl BoxCore for WebCanvasBox {
|
||||
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, "WebCanvasBox({}, {}x{})", self.canvas_id, self.width, self.height)
|
||||
write!(
|
||||
f,
|
||||
"WebCanvasBox({}, {}x{})",
|
||||
self.canvas_id, self.width, self.height
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
@ -289,7 +308,7 @@ impl NyashBox for WebCanvasBox {
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
@ -297,18 +316,14 @@ impl NyashBox for WebCanvasBox {
|
||||
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new(format!(
|
||||
"WebCanvasBox({}, {}x{})",
|
||||
self.canvas_id,
|
||||
self.width,
|
||||
self.height
|
||||
"WebCanvasBox({}, {}x{})",
|
||||
self.canvas_id, self.width, self.height
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
"WebCanvasBox"
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(other_canvas) = other.as_any().downcast_ref::<WebCanvasBox>() {
|
||||
@ -324,4 +339,4 @@ impl std::fmt::Display for WebCanvasBox {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.fmt_box(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
/*!
|
||||
* WebConsoleBox - ブラウザHTML要素コンソール出力Box
|
||||
*
|
||||
*
|
||||
* WebAssembly環境でHTML要素へのコンソール風出力
|
||||
* F12コンソールの代わりに指定要素に出力
|
||||
*/
|
||||
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
|
||||
use std::any::Any;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
@ -25,85 +25,91 @@ pub struct WebConsoleBox {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
impl WebConsoleBox {
|
||||
pub fn new(element_id: String) -> Self {
|
||||
Self {
|
||||
Self {
|
||||
base: BoxBase::new(),
|
||||
target_element_id: element_id,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 指定した要素IDのHTML要素を取得
|
||||
fn get_target_element(&self) -> Option<Element> {
|
||||
let window = web_sys::window()?;
|
||||
let document = window.document()?;
|
||||
document.get_element_by_id(&self.target_element_id)
|
||||
}
|
||||
|
||||
|
||||
/// コンソール出力を追加(改行付き)
|
||||
fn append_console_line(&self, message: &str, level: &str) {
|
||||
if let Some(element) = self.get_target_element() {
|
||||
let timestamp = js_sys::Date::new_0().to_iso_string().as_string().unwrap_or_default();
|
||||
let time_part = timestamp.split('T').nth(1).unwrap_or("00:00:00").split('.').nth(0).unwrap_or("00:00:00");
|
||||
|
||||
let timestamp = js_sys::Date::new_0()
|
||||
.to_iso_string()
|
||||
.as_string()
|
||||
.unwrap_or_default();
|
||||
let time_part = timestamp
|
||||
.split('T')
|
||||
.nth(1)
|
||||
.unwrap_or("00:00:00")
|
||||
.split('.')
|
||||
.nth(0)
|
||||
.unwrap_or("00:00:00");
|
||||
|
||||
let (level_prefix, color) = match level {
|
||||
"log" => ("📝", "white"),
|
||||
"warn" => ("⚠️", "yellow"),
|
||||
"error" => ("❌", "red"),
|
||||
"error" => ("❌", "red"),
|
||||
"info" => ("ℹ️", "cyan"),
|
||||
"debug" => ("🔍", "gray"),
|
||||
_ => ("📝", "white"),
|
||||
};
|
||||
|
||||
|
||||
let formatted_line = format!(
|
||||
"<span style='color: {}'>[{}] {} {}</span><br>",
|
||||
color,
|
||||
time_part,
|
||||
level_prefix,
|
||||
message
|
||||
"<span style='color: {}'>[{}] {} {}</span><br>",
|
||||
color, time_part, level_prefix, message
|
||||
);
|
||||
|
||||
|
||||
let current_content = element.inner_html();
|
||||
let new_content = format!("{}{}", current_content, formatted_line);
|
||||
element.set_inner_html(&new_content);
|
||||
|
||||
|
||||
// 自動スクロール
|
||||
if let Some(html_element) = element.dyn_ref::<HtmlElement>() {
|
||||
html_element.set_scroll_top(html_element.scroll_height());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// ログメッセージを出力
|
||||
pub fn log(&self, message: &str) {
|
||||
self.append_console_line(message, "log");
|
||||
}
|
||||
|
||||
|
||||
/// 警告メッセージを出力
|
||||
pub fn warn(&self, message: &str) {
|
||||
self.append_console_line(message, "warn");
|
||||
}
|
||||
|
||||
|
||||
/// エラーメッセージを出力
|
||||
pub fn error(&self, message: &str) {
|
||||
self.append_console_line(message, "error");
|
||||
}
|
||||
|
||||
|
||||
/// 情報メッセージを出力
|
||||
pub fn info(&self, message: &str) {
|
||||
self.append_console_line(message, "info");
|
||||
}
|
||||
|
||||
|
||||
/// デバッグメッセージを出力
|
||||
pub fn debug(&self, message: &str) {
|
||||
self.append_console_line(message, "debug");
|
||||
}
|
||||
|
||||
|
||||
/// コンソールをクリア
|
||||
pub fn clear(&self) {
|
||||
if let Some(element) = self.get_target_element() {
|
||||
element.set_inner_html("");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 区切り線を追加
|
||||
pub fn separator(&self) {
|
||||
if let Some(element) = self.get_target_element() {
|
||||
@ -113,7 +119,7 @@ impl WebConsoleBox {
|
||||
element.set_inner_html(&new_content);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// グループ開始(見出し付き)
|
||||
pub fn group(&self, title: &str) {
|
||||
if let Some(element) = self.get_target_element() {
|
||||
@ -126,7 +132,7 @@ impl WebConsoleBox {
|
||||
element.set_inner_html(&new_content);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// グループ終了
|
||||
pub fn group_end(&self) {
|
||||
if let Some(element) = self.get_target_element() {
|
||||
@ -143,19 +149,19 @@ impl BoxCore for WebConsoleBox {
|
||||
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, "WebConsoleBox({})", self.target_element_id)
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
@ -166,7 +172,7 @@ impl NyashBox for WebConsoleBox {
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
@ -176,11 +182,9 @@ impl NyashBox for WebConsoleBox {
|
||||
StringBox::new(format!("WebConsoleBox({})", self.target_element_id))
|
||||
}
|
||||
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
"WebConsoleBox"
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(other_console) = other.as_any().downcast_ref::<WebConsoleBox>() {
|
||||
@ -196,4 +200,4 @@ impl std::fmt::Display for WebConsoleBox {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.fmt_box(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
/*!
|
||||
* WebDisplayBox - ブラウザHTML要素表示制御Box
|
||||
*
|
||||
*
|
||||
* WebAssembly環境でHTML要素への直接出力・スタイル制御
|
||||
* プレイグラウンドの出力パネル等を完全制御
|
||||
*/
|
||||
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
|
||||
use std::any::Any;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
@ -25,19 +25,19 @@ pub struct WebDisplayBox {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
impl WebDisplayBox {
|
||||
pub fn new(element_id: String) -> Self {
|
||||
Self {
|
||||
Self {
|
||||
base: BoxBase::new(),
|
||||
target_element_id: element_id,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 指定した要素IDのHTML要素を取得
|
||||
fn get_target_element(&self) -> Option<Element> {
|
||||
let window = web_sys::window()?;
|
||||
let document = window.document()?;
|
||||
document.get_element_by_id(&self.target_element_id)
|
||||
}
|
||||
|
||||
|
||||
/// テキストを追加出力
|
||||
pub fn print(&self, message: &str) {
|
||||
if let Some(element) = self.get_target_element() {
|
||||
@ -50,7 +50,7 @@ impl WebDisplayBox {
|
||||
element.set_inner_html(&new_content);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// テキストを改行付きで追加出力
|
||||
pub fn println(&self, message: &str) {
|
||||
if let Some(element) = self.get_target_element() {
|
||||
@ -63,14 +63,14 @@ impl WebDisplayBox {
|
||||
element.set_inner_html(&new_content);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// HTMLコンテンツを完全置換
|
||||
pub fn set_html(&self, html_content: &str) {
|
||||
if let Some(element) = self.get_target_element() {
|
||||
element.set_inner_html(html_content);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// HTMLコンテンツを追加
|
||||
pub fn append_html(&self, html_content: &str) {
|
||||
if let Some(element) = self.get_target_element() {
|
||||
@ -79,7 +79,7 @@ impl WebDisplayBox {
|
||||
element.set_inner_html(&new_content);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// CSSスタイルを設定
|
||||
pub fn set_css(&self, property: &str, value: &str) {
|
||||
if let Some(element) = self.get_target_element() {
|
||||
@ -89,38 +89,38 @@ impl WebDisplayBox {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// CSSクラスを追加
|
||||
pub fn add_class(&self, class_name: &str) {
|
||||
if let Some(element) = self.get_target_element() {
|
||||
let _ = element.class_list().add_1(class_name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// CSSクラスを削除
|
||||
pub fn remove_class(&self, class_name: &str) {
|
||||
if let Some(element) = self.get_target_element() {
|
||||
let _ = element.class_list().remove_1(class_name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 内容をクリア
|
||||
pub fn clear(&self) {
|
||||
if let Some(element) = self.get_target_element() {
|
||||
element.set_inner_html("");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 要素を表示
|
||||
pub fn show(&self) {
|
||||
self.set_css("display", "block");
|
||||
}
|
||||
|
||||
|
||||
/// 要素を非表示
|
||||
pub fn hide(&self) {
|
||||
self.set_css("display", "none");
|
||||
}
|
||||
|
||||
|
||||
/// スクロールを最下部に移動
|
||||
pub fn scroll_to_bottom(&self) {
|
||||
if let Some(element) = self.get_target_element() {
|
||||
@ -136,19 +136,19 @@ impl BoxCore for WebDisplayBox {
|
||||
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, "WebDisplayBox({})", self.target_element_id)
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
@ -159,7 +159,7 @@ impl NyashBox for WebDisplayBox {
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
@ -169,11 +169,9 @@ impl NyashBox for WebDisplayBox {
|
||||
StringBox::new(format!("WebDisplayBox({})", self.target_element_id))
|
||||
}
|
||||
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
"WebDisplayBox"
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(other_display) = other.as_any().downcast_ref::<WebDisplayBox>() {
|
||||
@ -189,4 +187,4 @@ impl std::fmt::Display for WebDisplayBox {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.fmt_box(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user