ryo-analysis 0.1.0

Code graph and discovery engine for the RYO project
Documentation
//! CascadeAnalyzer: Cascade分析エンジン
//!
//! # 責務
//!
//! 変更の影響を分析し、必要な追加変更(CascadeSpec)を生成する。
//! 実行はryo-executorに委譲するため、ここでは分析のみ行う。
//!
//! # 設計意図
//!
//! CascadeAnalyzerはPolicy層に位置し、「何をすべきか」を決定する。
//! 実際の変更(MutationSpecへの変換と実行)はryo-executorが担当する。
//!
//! # 例
//!
//! ```rust,ignore
//! use ryo_analysis::cascade::{CascadeAnalyzer, CascadeStrategy};
//!
//! let analyzer = CascadeAnalyzer::new(&graph, &registry, &derive_index);
//! let specs = analyzer.cascade_add_derive(
//!     &SymbolPath::parse("crate::Config").unwrap(),
//!     &["Default".to_string()],
//!     &CascadeStrategy::eager(),
//! );
//!
//! // specsをryo-executorでMutationSpecに変換して実行
//! ```

use super::spec::CascadeSpec;
use super::strategy::CascadeStrategy;
use crate::query::DeriveIndex;
use crate::{CodeGraphV2, SymbolPath, SymbolRegistry, TypeFlowGraphV2};

/// Cascade分析エンジン
///
/// CodeGraphV2とTypeFlowGraphV2とSymbolRegistryを使用して、変更の影響を分析し、
/// 必要なCascadeSpecを生成する。
pub struct CascadeAnalyzer<'a> {
    graph: &'a CodeGraphV2,
    typeflow: &'a TypeFlowGraphV2,
    registry: &'a SymbolRegistry,
    derive_index: &'a DeriveIndex,
}

impl<'a> CascadeAnalyzer<'a> {
    /// 新しいCascadeAnalyzerを作成
    pub fn new(
        graph: &'a CodeGraphV2,
        typeflow: &'a TypeFlowGraphV2,
        registry: &'a SymbolRegistry,
        derive_index: &'a DeriveIndex,
    ) -> Self {
        Self {
            graph,
            typeflow,
            registry,
            derive_index,
        }
    }

    /// AddDeriveの影響分析 → 必要なCascadeSpecを生成
    ///
    /// # 引数
    ///
    /// * `target` - derive を追加する対象の型
    /// * `derives` - 追加する derive macro 名のリスト
    /// * `strategy` - cascade 戦略(Eager/Lazy)
    ///
    /// # 戻り値
    ///
    /// 適用すべきCascadeSpecのリスト。ryo-executorで
    /// MutationSpecに変換後、実行される。
    pub fn cascade_add_derive(
        &self,
        target: &SymbolPath,
        derives: &[String],
        strategy: &CascadeStrategy,
    ) -> Vec<CascadeSpec> {
        let mut specs = Vec::new();

        // 1. target自身のderive追加 (SymbolIdを解決)
        if let Some(symbol_id) = self.registry.lookup(target) {
            specs.push(CascadeSpec::add_derive(symbol_id, derives.to_vec()));

            // 2. フィールド型への伝播(Eagerの場合)
            if strategy.is_eager() {
                if let Some(field_types) = self.field_types_of(target) {
                    for field_type in field_types {
                        if self.needs_derive(&field_type, derives) {
                            // 再帰的にcascade
                            specs.extend(self.cascade_add_derive(&field_type, derives, strategy));
                        }
                    }
                }
            }
        }

        specs
    }

    /// 対象の型が持つフィールドの型を取得
    fn field_types_of(&self, target: &SymbolPath) -> Option<Vec<SymbolPath>> {
        let symbol_id = self.registry.lookup(target)?;

        let mut field_types = Vec::new();
        for child_id in self.graph.children_of(symbol_id) {
            // フィールドが使用している型を取得 (TypeFlow)
            for used_id in self.typeflow.types_used_by(child_id) {
                if let Some(used_path) = self.registry.resolve(used_id) {
                    field_types.push(used_path.clone());
                }
            }
        }

        if field_types.is_empty() {
            None
        } else {
            Some(field_types)
        }
    }

    /// 型が指定されたderiveを必要とするか判定
    fn needs_derive(&self, target: &SymbolPath, derives: &[String]) -> bool {
        // 外部crate(std等)の型はスキップ
        // SymbolPathがcrateで始まる場合はlocal
        if !target.crate_name().starts_with("crate") {
            return false;
        }

        // 既にderiveを持っているかチェック
        if let Some(symbol_id) = self.registry.lookup(target) {
            // 全てのderiveを既に持っている場合はスキップ
            let all_derived = derives
                .iter()
                .all(|d| self.derive_index.has_derive(symbol_id, d));
            if all_derived {
                return false;
            }
        }

        // 少なくとも1つ足りないderiveがある
        true
    }
}