Files
hakorune/src/mir/loop_pattern_detection/binding_map_provider.rs
nyash-codex 48bdf2fb98 refactor(joinir): Phase 79 - Detector/Recorder separation + BindingMapProvider
**Phase 79 Medium-Priority Refactoring Complete**

## Action 1: Detector/Recorder Separation

**New Pure Detection Logic:**
- `digitpos_detector.rs` (~350 lines, 7 tests)
  - Pure detection for A-4 DigitPos pattern
  - No binding_map dependency
  - Independently testable

- `trim_detector.rs` (~410 lines, 9 tests)
  - Pure detection for A-3 Trim pattern
  - No binding_map dependency
  - Comprehensive test coverage

**Simplified Promoters:**
- `DigitPosPromoter`: 200+ → 80 lines (60% reduction)
  - Uses DigitPosDetector for detection
  - Focuses on orchestration + recording
  - Removed 6 helper methods

- `LoopBodyCarrierPromoter`: 160+ → 70 lines (56% reduction)
  - Uses TrimDetector for detection
  - Clean separation of concerns
  - Removed 3 helper methods

## Action 2: BindingMapProvider Trait

**Centralized Feature Gate:**
- `binding_map_provider.rs` (~100 lines, 3 tests)
  - Trait to abstract binding_map access
  - #[cfg] guards: 10+ locations → 2 locations (80% reduction)

- `MirBuilder` implementation
  - Clean feature-gated access
  - Single point of control

## Quality Metrics

**Code Reduction:**
- DigitPosPromoter: 200+ → 80 lines (60%)
- LoopBodyCarrierPromoter: 160+ → 70 lines (56%)
- Feature guards: 10+ → 2 locations (80%)

**Tests:**
- All tests passing: 970/970 (100%)
- New test coverage: 19+ tests for detectors
- No regressions

**Design Improvements:**
-  Single Responsibility Principle
-  Independent unit testing
-  Reusable detection logic
-  Centralized feature gating

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

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2025-12-13 07:05:30 +09:00

108 lines
3.4 KiB
Rust

//! BindingMapProvider - Trait to abstract binding_map access
//!
//! Centralizes feature-gated binding map access, eliminating scattered
//! #[cfg] guards across promoters and patterns.
//!
//! # Design Philosophy
//!
//! This trait follows the **Single Point of Control** principle:
//! - **Before Phase 79**: `#[cfg(feature = "normalized_dev")]` guards scattered across 10+ locations
//! - **After Phase 79**: Feature gate centralized in 2 locations (trait + impl)
//!
//! # Benefits
//!
//! 1. **Maintainability**: Change feature gate logic in one place
//! 2. **Readability**: Request structs no longer need feature-gated fields
//! 3. **Testability**: Mock implementations for testing
//! 4. **Consistency**: Uniform access pattern across all promoters
//!
//! # Usage Example
//!
//! ```rust,ignore
//! // Before Phase 79: Scattered #[cfg] guards
//! #[cfg(feature = "normalized_dev")]
//! let binding_map = Some(&builder.binding_map);
//! #[cfg(not(feature = "normalized_dev"))]
//! let binding_map = None;
//!
//! // After Phase 79: Clean trait call
//! let binding_map = builder.get_binding_map();
//! ```
use crate::mir::binding_id::BindingId;
use std::collections::BTreeMap;
/// Trait to provide optional binding_map access (dev-only).
///
/// This trait abstracts the feature-gated access to binding_map,
/// allowing code to request binding information without knowing
/// whether the feature is enabled.
pub trait BindingMapProvider {
/// Get binding map if available (dev-only).
///
/// Returns Some(&BTreeMap) when `normalized_dev` feature is enabled,
/// None otherwise.
fn get_binding_map(&self) -> Option<&BTreeMap<String, BindingId>>;
}
#[cfg(test)]
mod tests {
use super::*;
// Mock implementation for testing
struct MockBuilder {
#[cfg(feature = "normalized_dev")]
binding_map: BTreeMap<String, BindingId>,
}
impl BindingMapProvider for MockBuilder {
#[cfg(feature = "normalized_dev")]
fn get_binding_map(&self) -> Option<&BTreeMap<String, BindingId>> {
Some(&self.binding_map)
}
#[cfg(not(feature = "normalized_dev"))]
fn get_binding_map(&self) -> Option<&BTreeMap<String, BindingId>> {
None
}
}
#[test]
#[cfg(feature = "normalized_dev")]
fn test_binding_map_provider_dev() {
let mut map = BTreeMap::new();
map.insert("x".to_string(), BindingId(1));
map.insert("y".to_string(), BindingId(2));
let builder = MockBuilder { binding_map: map };
let result = builder.get_binding_map();
assert!(result.is_some());
assert_eq!(result.unwrap().len(), 2);
}
#[test]
#[cfg(not(feature = "normalized_dev"))]
fn test_binding_map_provider_non_dev() {
let builder = MockBuilder {};
let result = builder.get_binding_map();
assert!(result.is_none());
}
#[test]
fn test_trait_object_compatibility() {
// Verify trait can be used as trait object
#[cfg(feature = "normalized_dev")]
let builder: Box<dyn BindingMapProvider> = Box::new(MockBuilder {
binding_map: BTreeMap::new(),
});
#[cfg(not(feature = "normalized_dev"))]
let builder: Box<dyn BindingMapProvider> = Box::new(MockBuilder {});
let _result = builder.get_binding_map();
// Test just verifies compilation and trait object usage
}
}