1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
//! # Singleton Macro
//!
//! Spring Framework-inspired dependency injection and singleton pattern macros for Rust backend services.
//!
//! Copyright (c) 2025 Janghoon Park <ceo@dataengine.co.kr>
//!
//! Licensed under either of:
//! - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE))
//! - MIT license ([LICENSE-MIT](LICENSE-MIT))
//!
//! at your option.
//!
//! ## 개요
//!
//! 백엔드 서비스를 위한 컴파일 타임 의존성 주입 및 싱글톤 패턴 구현 매크로 크레이트입니다.
//!
//! 이 크레이트는 Spring Framework의 DI 컨테이너와 유사한 방식으로 Rust 서비스의
//! 의존성을 관리하며, 메모리 효율성과 타입 안전성을 보장합니다.
//!
//! ## 주요 기능
//!
//! - **자동 싱글톤 관리**: `OnceCell`과 `Arc`를 사용한 thread-safe 싱글톤 구현
//! - **컴파일 타임 의존성 주입**: `Arc<T>` 타입 필드 자동 감지 및 주입
//! - **전역 레지스트리**: `inventory` 크레이트를 통한 자동 서비스 등록
//! - **Zero-cost abstraction**: 런타임 오버헤드 최소화
//! - **Static context 호환**: 컴파일 타임에 안전한 등록 시스템
//!
//! ## 제공 매크로
//!
//! ### `#[service]`
//!
//! 비즈니스 로직을 담당하는 서비스 컴포넌트를 정의합니다.
//!
//! #### 사용법
//! ```rust,ignore
//! use std::sync::Arc;
//!
//! #[service]
//! struct UserService {
//! user_repo: Arc<UserRepository>, // 자동 주입
//! email_service: Arc<EmailService>, // 자동 주입
//! config: Config, // Default::default() 사용
//! }
//!
//! // 커스텀 이름 지정 (자동으로 _service suffix 추가됨)
//! #[service(name = "auth")] // "auth_service"로 등록됨
//! struct AuthenticationService {
//! // ...
//! }
//! ```
//!
//! #### 생성되는 메서드
//! ```rust,ignore
//! // 싱글톤 인스턴스 가져오기
//! let service = UserService::instance();
//!
//! // Service trait 자동 구현
//! assert_eq!(service.name(), "userservice_service");
//! ```
//!
//! ### `#[repository]`
//!
//! 데이터 액세스 계층을 담당하는 리포지토리 컴포넌트를 정의합니다.
//! MongoDB 컬렉션과의 통합 및 Redis 캐싱을 지원합니다.
//!
//! #### 사용법
//! ```rust,ignore
//! #[repository]
//! struct UserRepository {
//! db: Arc<Database>, // 자동 주입 (필드명이 db/database인 경우)
//! redis: Arc<RedisClient>, // 자동 주입 (필드명이 redis/cache인 경우)
//! }
//!
//! // 커스텀 설정 (자동으로 _repository suffix 추가됨)
//! #[repository(name = "user", collection = "users")] // "user_repository"로 등록됨
//! struct UserRepo {
//! db: Arc<Database>,
//! }
//! ```
//!
//! #### 생성되는 메서드
//! ```rust,ignore
//! let repo = UserRepository::instance();
//!
//! // MongoDB 컬렉션 접근 (db 필드가 있을 때)
//! let collection = repo.collection::<User>();
//!
//! // 컬렉션 이름 가져오기
//! let name = repo.collection_name(); // "users"
//!
//! // Redis 캐싱 메서드 (redis/cache 필드가 있을 때)
//! let key = repo.cache_key("user123"); // "user_repository:user123"
//! repo.invalidate_cache("user123").await?;
//! repo.invalidate_collection_cache(Some("active")).await?;
//! repo.invalidate_pattern_cache("user_repository:*").await?;
//! ```
//!
//! ## 의존성 주입 규칙
//!
//! 필드 타입과 이름에 따라 자동으로 의존성이 주입됩니다:
//!
//! | 필드 타입 | 필드 이름 | 주입 방식 |
//! |-----------|-----------|-----------|
//! | `Arc<T>` | 모든 이름 | `ServiceLocator::get::<T>()` |
//! | 모든 타입 | `db`, `database` | `ServiceLocator::get::<Database>()` |
//! | 모든 타입 | `redis`, `cache` | `ServiceLocator::get::<RedisClient>()` |
//! | 기타 | 기타 | `Default::default()` |
//!
//! ## 레지스트리 시스템
//!
//! ### 자동 등록
//! - **서비스**: `{name}_service` 형태로 자동 등록
//! - **리포지토리**: `{name}_repository` 형태로 자동 등록
//! - **Static 호환**: 컴파일 타임에 안전한 함수 포인터 사용
//!
//! ### 등록 구조체
//! ```rust,ignore
//! pub struct ServiceRegistration {
//! pub name: &'static str,
//! pub constructor: fn() -> Box<dyn Any + Send + Sync>,
//! }
//!
//! pub struct RepositoryRegistration {
//! pub name: &'static str,
//! pub constructor: fn() -> Box<dyn Any + Send + Sync>,
//! }
//! ```
//!
//! ## 동작 원리
//!
//! 1. **컴파일 타임**: 매크로가 구조체를 분석하고 필요한 코드 생성
//! 2. **프로그램 시작**: `inventory`가 모든 서비스/리포지토리 수집
//! 3. **첫 사용**: `instance()` 호출 시 의존성 주입 및 인스턴스 생성
//! 4. **재사용**: 캐시된 싱글톤 인스턴스 반환
//!
//! ## 필수 의존성
//!
//! 이 매크로를 사용하려면 프로젝트에 다음 크레이트가 필요합니다:
//! - `once_cell`: 싱글톤 저장
//! - `inventory`: 전역 레지스트리
//! - `async-trait`: Repository/Service trait 구현
//! - `mongodb` (repository 사용 시)
//! - `redis` (캐싱 사용 시)
//!
//! ## 예제: 완전한 서비스 구성
//!
//! ```rust,ignore
//! // 1. Repository 정의
//! #[repository(collection = "users")]
//! struct UserRepository {
//! db: Arc<Database>,
//! redis: Arc<RedisClient>,
//! }
//!
//! impl UserRepository {
//! async fn find_by_id(&self, id: &str) -> Option<User> {
//! // 캐시 확인
//! let cache_key = self.cache_key(id);
//! if let Ok(Some(cached)) = self.redis.get(&cache_key).await {
//! return Some(cached);
//! }
//!
//! // DB 조회
//! let user = self.collection()
//! .find_one(doc! { "_id": id }, None)
//! .await
//! .ok()
//! .flatten();
//!
//! // 캐시 저장
//! if let Some(ref user) = user {
//! self.redis.set_with_expiry(&cache_key, user, 600).await;
//! }
//!
//! user
//! }
//! }
//!
//! // 2. Service 정의
//! #[service]
//! struct UserService {
//! repo: Arc<UserRepository>,
//! }
//!
//! impl UserService {
//! async fn get_user(&self, id: &str) -> Result<User> {
//! self.repo.find_by_id(id).await
//! .ok_or_else(|| Error::NotFound)
//! }
//! }
//!
//! // 3. 사용
//! #[actix_web::main]
//! async fn main() {
//! // ServiceLocator 초기화 (Database, RedisClient 등록)
//!
//! // 서비스 사용
//! let user_service = UserService::instance();
//! let user = user_service.get_user("123").await?;
//! }
//! ```
//!
//! ## 주의사항
//!
//! - **순환 의존성 방지**: A가 B를 의존하고 B가 A를 의존하면 런타임 패닉 발생
//! - **ServiceLocator 초기화**: Database, RedisClient 등 핵심 서비스는 미리 등록 필요
//! - **Thread-safety**: 모든 의존성은 `Arc`로 감싸져 있어야 함
//! - **Static context**: 모든 등록은 컴파일 타임에 결정됨
//!
//! ## 디버깅
//!
//! 의존성 주입 과정은 콘솔에 로그가 출력됩니다:
//! ```text
//! ServiceLocator::get called for type: UserRepository
//! - Creating new instance for UserRepository
//! - Found matching repository: user_repository
//! ```
use TokenStream;
/// 비즈니스 로직 서비스를 위한 싱글톤 매크로
///
/// 이 매크로는 다음을 자동으로 생성합니다:
/// - 싱글톤 인스턴스 관리 (`instance()` 메서드)
/// - 의존성 자동 주입 (`new()` 메서드)
/// - 전역 레지스트리 등록 (이름에 `_service` suffix 자동 추가)
/// - Service trait 구현
///
/// # 인자
///
/// - `name` (선택): 서비스 이름. 기본값은 구조체 이름의 소문자 + "_service"
///
/// # 의존성 주입 규칙
///
/// - `Arc<T>` 타입: `ServiceLocator::get::<T>()` 자동 주입
/// - 기타 타입: `Default::default()` 사용
///
/// # 예제
///
/// ```rust,ignore
/// #[service]
/// struct PaymentService {
/// payment_repo: Arc<PaymentRepository>, // 자동 주입
/// notification: Arc<NotificationService>, // 자동 주입
/// retry_count: u32, // Default::default()
/// }
///
/// // 커스텀 이름 지정
/// #[service(name = "payment")] // "payment_service"로 등록됨
/// struct PaymentProcessor {
/// // ...
/// }
///
/// // 사용
/// let service = PaymentService::instance();
/// assert_eq!(service.name(), "paymentservice_service");
/// ```
///
/// # 생성되는 코드
///
/// - `instance()`: 싱글톤 인스턴스 접근
/// - `new()`: 의존성 주입을 통한 인스턴스 생성 (private)
/// - Service trait 구현 (`name()`, `init()`)
/// - 전역 레지스트리 자동 등록
/// 데이터 액세스 리포지토리를 위한 싱글톤 매크로
///
/// Service 매크로와 유사하지만 추가로 다음을 제공합니다:
/// - MongoDB 컬렉션 통합 (`collection()` 메서드) - db 필드가 있을 때
/// - 컬렉션 이름 관리 (`collection_name()` 메서드)
/// - Redis 캐싱 헬퍼 메서드들 - redis/cache 필드가 있을 때:
/// - `cache_key()` - 엔티티 캐시 키 생성
/// - `collection_cache_key()` - 컬렉션 캐시 키 생성
/// - `invalidate_cache()` - 캐시 무효화
/// - `invalidate_collection_cache()` - 컬렉션 캐시 무효화
/// - `invalidate_pattern_cache()` - 패턴 기반 캐시 무효화
///
/// # 인자
///
/// - `name` (선택): 리포지토리 이름. 기본값은 구조체 이름의 소문자 + "_repository"
/// - `collection` (선택): MongoDB 컬렉션 이름. 기본값은 구조체 이름에서 "Repository" 제거 후 복수형
///
/// # 의존성 주입 규칙
///
/// - `db`, `database` 필드: `ServiceLocator::get::<Database>()` 자동 주입
/// - `redis`, `cache` 필드: `ServiceLocator::get::<RedisClient>()` 자동 주입
/// - `Arc<T>` 타입: `ServiceLocator::get::<T>()` 자동 주입
/// - 기타 타입: `Default::default()` 사용
///
/// # 예제
///
/// ```rust,ignore
/// #[repository(name = "order", collection = "orders")]
/// struct OrderRepository {
/// db: Arc<Database>, // 자동 주입 (Database)
/// redis: Arc<RedisClient>, // 자동 주입 (RedisClient)
/// user_service: Arc<UserService>, // 자동 주입 (ServiceLocator)
/// }
///
/// // 사용
/// let repo = OrderRepository::instance();
///
/// // MongoDB 작업 (db 필드가 있으므로 자동 생성됨)
/// let orders = repo.collection::<Order>()
/// .find(doc! { "status": "pending" }, None)
/// .await?;
///
/// // Redis 캐싱 (redis 필드가 있으므로 자동 생성됨)
/// let key = repo.cache_key("order123"); // "order_repository:order123"
/// repo.invalidate_cache("order123").await?;
/// repo.invalidate_pattern_cache("order_repository:*").await?;
///
/// // 컬렉션 이름
/// assert_eq!(repo.collection_name(), "orders");
/// assert_eq!(repo.name(), "order_repository");
/// ```
///
/// # 생성되는 코드
///
/// - `instance()`: 싱글톤 인스턴스 접근
/// - `new()`: 의존성 주입을 통한 인스턴스 생성 (private)
/// - `collection_name()`: MongoDB 컬렉션 이름 반환
/// - `collection<T>()`: MongoDB 컬렉션 접근 (db 필드 있을 때만)
/// - Redis 캐싱 메서드들 (redis/cache 필드 있을 때만)
/// - Repository trait 구현 (`name()`, `collection_name()`, `init()`)
/// - 전역 레지스트리 자동 등록
///
/// # 특별한 필드 처리
///
/// | 필드 이름 | 자동 주입 타입 | 추가 메서드 |
/// |-----------|----------------|-------------|
/// | `db`, `database` | `Database` | `collection<T>()` |
/// | `redis`, `cache` | `RedisClient` | 캐싱 메서드들 |