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, ®istry, &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}