Skip to main content

ryo_analysis/cascade/
analyzer.rs

1//! CascadeAnalyzer: Cascade分析エンジン
2//!
3//! # 責務
4//!
5//! 変更の影響を分析し、必要な追加変更(CascadeSpec)を生成する。
6//! 実行はryo-executorに委譲するため、ここでは分析のみ行う。
7//!
8//! # 設計意図
9//!
10//! CascadeAnalyzerはPolicy層に位置し、「何をすべきか」を決定する。
11//! 実際の変更(MutationSpecへの変換と実行)はryo-executorが担当する。
12//!
13//! # 例
14//!
15//! ```rust,ignore
16//! use ryo_analysis::cascade::{CascadeAnalyzer, CascadeStrategy};
17//!
18//! let analyzer = CascadeAnalyzer::new(&graph, &registry, &derive_index);
19//! let specs = analyzer.cascade_add_derive(
20//!     &SymbolPath::parse("crate::Config").unwrap(),
21//!     &["Default".to_string()],
22//!     &CascadeStrategy::eager(),
23//! );
24//!
25//! // specsをryo-executorでMutationSpecに変換して実行
26//! ```
27
28use super::spec::CascadeSpec;
29use super::strategy::CascadeStrategy;
30use crate::query::DeriveIndex;
31use crate::{CodeGraphV2, SymbolPath, SymbolRegistry, TypeFlowGraphV2};
32
33/// Cascade分析エンジン
34///
35/// CodeGraphV2とTypeFlowGraphV2とSymbolRegistryを使用して、変更の影響を分析し、
36/// 必要なCascadeSpecを生成する。
37pub struct CascadeAnalyzer<'a> {
38    graph: &'a CodeGraphV2,
39    typeflow: &'a TypeFlowGraphV2,
40    registry: &'a SymbolRegistry,
41    derive_index: &'a DeriveIndex,
42}
43
44impl<'a> CascadeAnalyzer<'a> {
45    /// 新しいCascadeAnalyzerを作成
46    pub fn new(
47        graph: &'a CodeGraphV2,
48        typeflow: &'a TypeFlowGraphV2,
49        registry: &'a SymbolRegistry,
50        derive_index: &'a DeriveIndex,
51    ) -> Self {
52        Self {
53            graph,
54            typeflow,
55            registry,
56            derive_index,
57        }
58    }
59
60    /// AddDeriveの影響分析 → 必要なCascadeSpecを生成
61    ///
62    /// # 引数
63    ///
64    /// * `target` - derive を追加する対象の型
65    /// * `derives` - 追加する derive macro 名のリスト
66    /// * `strategy` - cascade 戦略(Eager/Lazy)
67    ///
68    /// # 戻り値
69    ///
70    /// 適用すべきCascadeSpecのリスト。ryo-executorで
71    /// MutationSpecに変換後、実行される。
72    pub fn cascade_add_derive(
73        &self,
74        target: &SymbolPath,
75        derives: &[String],
76        strategy: &CascadeStrategy,
77    ) -> Vec<CascadeSpec> {
78        let mut specs = Vec::new();
79
80        // 1. target自身のderive追加 (SymbolIdを解決)
81        if let Some(symbol_id) = self.registry.lookup(target) {
82            specs.push(CascadeSpec::add_derive(symbol_id, derives.to_vec()));
83
84            // 2. フィールド型への伝播(Eagerの場合)
85            if strategy.is_eager() {
86                if let Some(field_types) = self.field_types_of(target) {
87                    for field_type in field_types {
88                        if self.needs_derive(&field_type, derives) {
89                            // 再帰的にcascade
90                            specs.extend(self.cascade_add_derive(&field_type, derives, strategy));
91                        }
92                    }
93                }
94            }
95        }
96
97        specs
98    }
99
100    /// 対象の型が持つフィールドの型を取得
101    fn field_types_of(&self, target: &SymbolPath) -> Option<Vec<SymbolPath>> {
102        let symbol_id = self.registry.lookup(target)?;
103
104        let mut field_types = Vec::new();
105        for child_id in self.graph.children_of(symbol_id) {
106            // フィールドが使用している型を取得 (TypeFlow)
107            for used_id in self.typeflow.types_used_by(child_id) {
108                if let Some(used_path) = self.registry.resolve(used_id) {
109                    field_types.push(used_path.clone());
110                }
111            }
112        }
113
114        if field_types.is_empty() {
115            None
116        } else {
117            Some(field_types)
118        }
119    }
120
121    /// 型が指定されたderiveを必要とするか判定
122    fn needs_derive(&self, target: &SymbolPath, derives: &[String]) -> bool {
123        // 外部crate(std等)の型はスキップ
124        // SymbolPathがcrateで始まる場合はlocal
125        if !target.crate_name().starts_with("crate") {
126            return false;
127        }
128
129        // 既にderiveを持っているかチェック
130        if let Some(symbol_id) = self.registry.lookup(target) {
131            // 全てのderiveを既に持っている場合はスキップ
132            let all_derived = derives
133                .iter()
134                .all(|d| self.derive_index.has_derive(symbol_id, d));
135            if all_derived {
136                return false;
137            }
138        }
139
140        // 少なくとも1つ足りないderiveがある
141        true
142    }
143}