Files
hakorune/docs/development/current/main/phase123_consolebox_code_unification.md
nyash-codex e328be0307 Phase 122.5-126完了:ConsoleBox 品質改善・最適化・統合
## 実装成果(Phase 122.5-126)

### Phase 122.5: nyash.toml method_id 修正
- println method_id を 2 → 1 に統一(log と同じ)
- TypeRegistry slot 400 との整合性確保

### Phase 123: ConsoleBox WASM/非WASM コード統一化
- マクロ define_console_impl! による重複排除
- 67行削減(27.3% 削減達成)
- ビルド成功・全テストパス

### Phase 124: VM Method Dispatch 統一化
- TypeRegistry ベースの統一ディスパッチ (dispatch_by_slot)
- String/Array/ConsoleBox を一元化
- 100行削減、メソッド解決の高速化

### Phase 125: 削除:deprecated builtin ConsoleBox
- src/box_factory/builtin_impls/console_box.rs 削除
- Plugin-only 移行で "Everything is Plugin" 実現
- 52行削減

### Phase 126: ドキュメント統合
- consolebox_complete_guide.md (27KB統合マスター)
- core_boxes_design/logging_policy/hako_logging_design 更新
- ~750行の navigation・cross-reference 改善

## 数値成果

- **総コード削減**: 219行
- **新規ドキュメント**: 1ファイル (+27KB)
- **更新ドキュメント**: 6ファイル (+~750行)
- **テスト**: Phase 120 representative tests  PASS
- **ビルド**: Zero errors

## 設計原則の完全実現

 println/log エイリアス統一(Phase 122)
 WASM/非WASM 統一化(Phase 123)
 TypeRegistry 統合(Phase 124)
 Plugin-only 移行(Phase 125)
 ドキュメント統合(Phase 126)

🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 06:02:03 +09:00

13 KiB
Raw Blame History

Phase 123: ConsoleBox WASM/非WASM コード統一化

⚠️ Note: このドキュメントは Phase 123 の実装記録です。 統合的なガイドは ConsoleBox 完全ガイド をご参照ください。

目的

src/boxes/console_box.rs で WASM/非WASM 環境で分離・重複している実装をマクロを使用して統一し、保守性と可読性を向上させる。

現在 ~245行の実装を ~180行程度に削減~25%削減見込み)

現在の問題

コード重複の内容

src/boxes/console_box.rs の現状:

📦 WASM版行 59-144
  ├─ struct ConsoleBox (66-71)
  ├─ impl ConsoleBox (66-96)
  │  ├─ new()
  │  ├─ log()
  │  ├─ println()
  │  ├─ warn()
  │  ├─ error()
  │  └─ clear()
  ├─ impl BoxCore (100-120)
  └─ impl NyashBox (123-144)

📦 非WASM版行 147-229  ← 全く同じ構造!
  ├─ struct ConsoleBox (148-151)
  ├─ impl ConsoleBox (154-182)
  │  ├─ new()
  │  ├─ log()
  │  ├─ println()
  │  ├─ warn()
  │  ├─ error()
  │  └─ clear()
  ├─ impl BoxCore (185-205)
  └─ impl NyashBox (208-229)

📦 Display行 232-244
  ├─ WASM版 Display impl (233-237)
  └─ 非WASM版 Display impl (240-244)

重複の詳細

1. メソッド実装の分岐log, warn, error, clear

// WASM版
pub fn log(&self, message: &str) {
    web_sys::console::log_1(&message.into());
}

// 非WASM版
pub fn log(&self, message: &str) {
    println!("[Console LOG] {}", message);
}

問題: 実装は異なるが、構造は完全に同じ

2. println の重複実装

両方のバージョンで同じ実装:

pub fn println(&self, message: &str) {
    self.log(message);  // 常に log に委譲
}

改善機会: マクロで1度だけ定義

3. BoxCore の完全な重複

// WASM版 impl BoxCore (100-120)
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]")
}
// ... as_any, as_any_mut

// 非WASM版 impl BoxCore (185-205)
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]")  // ← ここだけ異なる!
}
// ... as_any, as_any_mut

改善機会: fmt_box のメッセージのみを条件付きにすれば共通化できる

4. NyashBox の完全な重複

// 両バージョンで完全に同じ
impl NyashBox for ConsoleBox {
    fn to_string_box(&self) -> StringBox { ... }
    fn equals(&self, other: &dyn NyashBox) -> BoolBox { ... }
    fn type_name(&self) -> &'static str { "ConsoleBox" }
    fn clone_box(&self) -> Box<dyn NyashBox> { ... }
    fn share_box(&self) -> Box<dyn NyashBox> { ... }
}

改善機会: 完全に共通化可能

5. Display の重複

// WASM版
impl Display for ConsoleBox {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.fmt_box(f)
    }
}

// 非WASM版 - 完全に同じ
impl Display for ConsoleBox {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.fmt_box(f)
    }
}

改善機会: 両バージョンを削除して1つに統合

実装戦略

戦略 A: マクロベースの統一(推奨)

マクロ macro_rules! define_console_methods! を使用してメソッド実装を統一化

// マクロ定義
macro_rules! define_console_methods {
    (
        log: $log_impl:expr,
        warn: $warn_impl:expr,
        error: $error_impl:expr,
        clear: $clear_impl:expr
    ) => {
        impl ConsoleBox {
            pub fn new() -> Self {
                Self {
                    base: BoxBase::new(),
                }
            }

            pub fn log(&self, message: &str) {
                $log_impl(self, message);
            }

            pub fn println(&self, message: &str) {
                self.log(message);  // 共通実装
            }

            pub fn warn(&self, message: &str) {
                $warn_impl(self, message);
            }

            pub fn error(&self, message: &str) {
                $error_impl(self, message);
            }

            pub fn clear(&self) {
                $clear_impl(self);
            }
        }
    };
}

// WASM環境での使用
#[cfg(target_arch = "wasm32")]
define_console_methods!(
    log: |s: &ConsoleBox, msg: &str| { web_sys::console::log_1(&msg.into()); },
    warn: |s: &ConsoleBox, msg: &str| { web_sys::console::warn_1(&msg.into()); },
    error: |s: &ConsoleBox, msg: &str| { web_sys::console::error_1(&msg.into()); },
    clear: |s: &ConsoleBox| { web_sys::console::clear(); }
);

// 非WASM環境での使用
#[cfg(not(target_arch = "wasm32"))]
define_console_methods!(
    log: |s: &ConsoleBox, msg: &str| { println!("[Console LOG] {}", msg); },
    warn: |s: &ConsoleBox, msg: &str| { println!("[Console WARN] {}", msg); },
    error: |s: &ConsoleBox, msg: &str| { println!("[Console ERROR] {}", msg); },
    clear: |s: &ConsoleBox| { println!("[Console CLEAR]"); }
);

戦略 B: trait オブジェクトによる実装(代替案)

trait ConsolePlatform {
    fn log(&self, message: &str);
    fn warn(&self, message: &str);
    fn error(&self, message: &str);
    fn clear(&self);
}

// WASM版
#[cfg(target_arch = "wasm32")]
struct WasmPlatform;

#[cfg(target_arch = "wasm32")]
impl ConsolePlatform for WasmPlatform {
    // WASM実装
}

// 非WASM版
#[cfg(not(target_arch = "wasm32"))]
struct MockPlatform;

// ConsoleBox はプラットフォームを集約
struct ConsoleBox {
    base: BoxBase,
    platform: Box<dyn ConsolePlatform>,  // ← 委譲
}

注記: 本フェーズではマクロベースを推奨(シンプルで効果的)

実装ステップ

ステップ 1: マクロ定義の作成

新しいマクロを定義:

// 行 54 から 57 の use 宣言直後に挿入

/// ConsoleBox メソッド実装マクロ
/// WASM/非WASM環境で異なるメソッド実装を統一化
macro_rules! define_console_impl {
    (
        log: $log_impl:expr,
        warn: $warn_impl:expr,
        error: $error_impl:expr,
        clear: $clear_impl:expr,
        fmt_desc: $fmt_desc:expr
    ) => {
        impl ConsoleBox {
            pub fn new() -> Self {
                Self {
                    base: BoxBase::new(),
                }
            }

            pub fn log(&self, message: &str) {
                $log_impl(message);
            }

            pub fn println(&self, message: &str) {
                self.log(message);
            }

            pub fn warn(&self, message: &str) {
                $warn_impl(message);
            }

            pub fn error(&self, message: &str) {
                $error_impl(message);
            }

            pub fn clear(&self) {
                $clear_impl();
            }
        }

        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, "{}", $fmt_desc)
            }

            fn as_any(&self) -> &dyn Any {
                self
            }

            fn as_any_mut(&mut self) -> &mut dyn Any {
                self
            }
        }

        impl NyashBox for ConsoleBox {
            fn to_string_box(&self) -> StringBox {
                StringBox::new($fmt_desc)
            }

            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())
            }

            fn share_box(&self) -> Box<dyn NyashBox> {
                self.clone_box()
            }
        }

        impl Display for ConsoleBox {
            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                self.fmt_box(f)
            }
        }
    };
}

ステップ 2: WASM版の実装

#[cfg(target_arch = "wasm32")]
#[derive(Debug, Clone)]
pub struct ConsoleBox {
    base: BoxBase,
}

#[cfg(target_arch = "wasm32")]
define_console_impl!(
    log: |msg: &str| { web_sys::console::log_1(&msg.into()); },
    warn: |msg: &str| { web_sys::console::warn_1(&msg.into()); },
    error: |msg: &str| { web_sys::console::error_1(&msg.into()); },
    clear: || { web_sys::console::clear(); },
    fmt_desc: "[ConsoleBox - Browser Console Interface]"
);

ステップ 3: 非WASM版の実装

#[cfg(not(target_arch = "wasm32"))]
#[derive(Debug, Clone)]
pub struct ConsoleBox {
    base: BoxBase,
}

#[cfg(not(target_arch = "wasm32"))]
define_console_impl!(
    log: |msg: &str| { println!("[Console LOG] {}", msg); },
    warn: |msg: &str| { println!("[Console WARN] {}", msg); },
    error: |msg: &str| { println!("[Console ERROR] {}", msg); },
    clear: || { println!("[Console CLEAR]"); },
    fmt_desc: "[ConsoleBox - Mock Implementation]"
);

ステップ 4: 検証

# コンパイル確認
cargo build --release 2>&1 | grep -i console

# 機能確認
NYASH_JOINIR_STRICT=1 ./target/release/nyash apps/tests/peek_expr_block.hako
NYASH_JOINIR_STRICT=1 ./target/release/nyash apps/tests/loop_min_while.hako
NYASH_JOINIR_STRICT=1 ./target/release/nyash apps/tests/esc_dirname_smoke.hako

# WASM ビルド確認(もし WASM 環境が整っていれば)
cargo build --release --target wasm32-unknown-unknown 2>&1 | grep -i console

期待される効果

コード削減

  • 削減行数: ~65行25%削減)

    • WASM版の重複実装: ~30行削除
    • 非WASM版の重複実装: ~30行削除
    • Display 重複: ~10行削除
    • マクロ定義による新規追加: ~15行
  • 最終サイズ: 245行 → 180行程度

保守性向上

  • メソッド実装を1箇所で管理
  • WASM/非WASM の差分が明確
  • println の一元化実装
  • BoxCore/NyashBox の重複削除

可読性向上

  • マクロの引数で環境別の違いが明示的
  • println が log に委譲される流れが可視化
  • fmt_desc で環境別の説明文が分離

実装上の注意

1. マクロの引数形式

// ✅ 正しい
define_console_impl!(
    log: |msg: &str| { /* 実装 */ },
    // ...
);

// ❌ 避けるべき
define_console_impl!(
    log = |msg: &str| { /* 実装 */ },  // = は使わない
);

2. println のクロージャ

// ❌ self が必要ない場合でも self を渡す
println: |msg: &str| { self.log(msg); }  // self は available ではない

// ✅ 正しいlog に委譲は ConsoleBox メソッド内で実装)
// マクロ内で `pub fn println(&self, message: &str) { self.log(message); }` を生成

3. Display 統合

現在の実装:

#[cfg(target_arch = "wasm32")]
impl Display for ConsoleBox { ... }

#[cfg(not(target_arch = "wasm32"))]
impl Display for ConsoleBox { ... }

マクロ後は両 cfg ブロック内で定義されるため、impl Display は削除可能。

ロールバック計画

修正後に問題が発生した場合:

# 修正前のバージョンに戻す
git checkout HEAD~ -- src/boxes/console_box.rs

# または具体的な commit hash を指定
git show <commit-hash>:src/boxes/console_box.rs > src/boxes/console_box.rs

テスト戦略

ユニットテスト(新規作成不要)

現在、ConsoleBox は直接テストしていない(プラグイン経由)。

統合テスト

既存の Phase 120 representative tests が十分:

# すべてが成功することを確認
NYASH_JOINIR_STRICT=1 ./target/release/nyash apps/tests/esc_dirname_smoke.hako
# 出力: [Console LOG] dir1/dir2

所要時間

2時間程度

  • マクロ設計と定義: 30分
  • WASM/非WASM 実装: 45分
  • 検証とテスト: 30分
  • ドキュメント更新: 15分

完了後の次のステップ

Phase 124: VM Method Dispatch 統一化4時間


記録:

  • Phase 122.5: nyash.toml method_id 修正 完了
  • Phase 123: ConsoleBox WASM/非WASM 統一化 ← 現在のフェーズ
  • Phase 124: VM Method Dispatch 統一化(予定)
  • Phase 125: 削除deprecated builtin ConsoleBox予定
  • Phase 126: ドキュメント統合(予定)