1use serde::{Deserialize, Serialize};
2use thiserror::Error;
3
4#[derive(Debug, Error, Clone, Serialize, Deserialize)]
6pub enum ConfigError {
7 #[error("Missing required configuration key: {key}")]
8 MissingKey { key: String },
9 #[error("Invalid configuration value for {key}: expected {expected}, got {actual}")]
10 InvalidValue {
11 key: String,
12 expected: String,
13 actual: String,
14 },
15 #[error("Configuration file not found: {path}")]
16 FileNotFound { path: String },
17 #[error("Failed to parse configuration: {reason}")]
18 ParseError { reason: String },
19 #[error("Plugin error: {message}")]
20 PluginError { message: String },
21}
22
23#[derive(Debug, Error, Clone, Serialize, Deserialize)]
25pub enum LlmError {
26 #[error("LLM service unavailable: {provider}")]
27 ServiceUnavailable { provider: String },
28 #[error("Authentication failed for {provider}: {reason}")]
29 AuthenticationFailed { provider: String, reason: String },
30 #[error("Request timeout after {timeout_ms}ms")]
31 Timeout { timeout_ms: u64 },
32 #[error("Invalid prompt: {reason}")]
33 InvalidPrompt { reason: String },
34 #[error("Rate limit exceeded for {provider}: retry after {retry_after_seconds}s")]
35 RateLimitExceeded {
36 provider: String,
37 retry_after_seconds: u64,
38 },
39 #[error("LLM responded with error: {message}")]
40 ResponseError { message: String },
41}
42
43#[derive(Debug, Error, Clone, Serialize, Deserialize)]
45pub enum MemoryError {
46 #[error("Memory store not found: {store_id}")]
47 StoreNotFound { store_id: String },
48 #[error("Memory key not found: {key} in store {store_id}")]
49 KeyNotFound { key: String, store_id: String },
50 #[error("Memory store capacity exceeded: {current}/{max}")]
51 CapacityExceeded { current: usize, max: usize },
52 #[error("Invalid memory operation: {operation} on {store_type}")]
53 InvalidOperation {
54 operation: String,
55 store_type: String,
56 },
57 #[error("Memory serialization failed: {reason}")]
58 SerializationFailed { reason: String },
59 #[error("Memory store corrupted: {details}")]
60 Corrupted { details: String },
61}
62
63#[derive(Debug, Error, Clone, Serialize, Deserialize)]
65pub enum ToolError {
66 #[error("Tool not found: {tool_name}")]
67 NotFound { tool_name: String },
68 #[error("Tool execution failed: {tool_name} - {reason}")]
69 ExecutionFailed { tool_name: String, reason: String },
70 #[error("Invalid tool parameters for {tool_name}: {details}")]
71 InvalidParameters { tool_name: String, details: String },
72 #[error("Tool timeout: {tool_name} exceeded {timeout_ms}ms")]
73 Timeout { tool_name: String, timeout_ms: u64 },
74 #[error("Tool permission denied: {tool_name} - {reason}")]
75 PermissionDenied { tool_name: String, reason: String },
76 #[error("Tool dependency missing: {tool_name} requires {dependency}")]
77 DependencyMissing {
78 tool_name: String,
79 dependency: String,
80 },
81}
82
83#[derive(Debug, Error, Clone, Serialize, Deserialize)]
85pub enum ExecutionError {
86 #[error("Mission not found: {mission_id}")]
87 MissionNotFound { mission_id: String },
88 #[error("Step failed: {step_id} in mission {mission_id} - {reason}")]
89 StepFailed {
90 step_id: String,
91 mission_id: String,
92 reason: String,
93 },
94 #[error("Dependency cycle detected in mission {mission_id}: {cycle}")]
95 DependencyCycle { mission_id: String, cycle: String },
96 #[error("Resource exhausted: {resource} - {details}")]
97 ResourceExhausted { resource: String, details: String },
98 #[error("Execution timeout: mission {mission_id} exceeded {timeout_ms}ms")]
99 Timeout { mission_id: String, timeout_ms: u64 },
100 #[error("Invalid mission state: {state} for operation {operation}")]
101 InvalidState { state: String, operation: String },
102}
103
104#[derive(Debug, Error, Clone, Serialize, Deserialize)]
106pub enum SchemaError {
107 #[error("Schema validation failed for {schema_name}: {}", errors.join(", "))]
108 ValidationFailed {
109 schema_name: String,
110 errors: Vec<String>,
111 },
112 #[error("Schema not found: {schema_name}")]
113 SchemaNotFound { schema_name: String },
114 #[error("Invalid schema definition: {schema_name} - {reason}")]
115 InvalidDefinition { schema_name: String, reason: String },
116 #[error("Schema version mismatch: expected {expected}, got {actual}")]
117 VersionMismatch { expected: String, actual: String },
118}
119
120#[derive(Debug, Error)]
122pub enum RustChainError {
123 #[error("IO error: {0}")]
124 Io(#[from] std::io::Error),
125
126 #[error("Configuration error: {0}")]
127 Config(#[from] ConfigError),
128
129 #[error("LLM error: {0}")]
130 Llm(#[from] LlmError),
131
132 #[error("Memory error: {0}")]
133 Memory(#[from] MemoryError),
134
135 #[error("Tool error: {0}")]
136 Tool(#[from] ToolError),
137
138 #[error("Execution error: {0}")]
139 Execution(#[from] ExecutionError),
140
141 #[error("Schema validation error: {0}")]
142 Schema(#[from] SchemaError),
143
144 #[error("Security error: {0}")]
145 Security(String),
146
147 #[error("JSON serialization error: {0}")]
148 Json(#[from] serde_json::Error),
149
150 #[error("YAML serialization error: {0}")]
151 Yaml(#[from] serde_yaml::Error),
152
153 #[error("Unknown error: {message}")]
154 Unknown { message: String },
155
156 #[error("Execution error: {0}")]
157 Exec(String),
158}
159
160pub type Result<T> = std::result::Result<T, RustChainError>;
161
162impl ConfigError {
164 pub fn missing_key(key: impl Into<String>) -> Self {
165 Self::MissingKey { key: key.into() }
166 }
167
168 pub fn invalid_value(
169 key: impl Into<String>,
170 expected: impl Into<String>,
171 actual: impl Into<String>,
172 ) -> Self {
173 Self::InvalidValue {
174 key: key.into(),
175 expected: expected.into(),
176 actual: actual.into(),
177 }
178 }
179
180 pub fn file_not_found(path: impl Into<String>) -> Self {
181 Self::FileNotFound { path: path.into() }
182 }
183
184 pub fn parse_error(reason: impl Into<String>) -> Self {
185 Self::ParseError {
186 reason: reason.into(),
187 }
188 }
189}
190
191impl LlmError {
192 pub fn service_unavailable(provider: impl Into<String>) -> Self {
193 Self::ServiceUnavailable {
194 provider: provider.into(),
195 }
196 }
197
198 pub fn authentication_failed(provider: impl Into<String>, reason: impl Into<String>) -> Self {
199 Self::AuthenticationFailed {
200 provider: provider.into(),
201 reason: reason.into(),
202 }
203 }
204
205 pub fn timeout(timeout_ms: u64) -> Self {
206 Self::Timeout { timeout_ms }
207 }
208
209 pub fn invalid_prompt(reason: impl Into<String>) -> Self {
210 Self::InvalidPrompt {
211 reason: reason.into(),
212 }
213 }
214
215 pub fn rate_limit_exceeded(provider: impl Into<String>, retry_after_seconds: u64) -> Self {
216 Self::RateLimitExceeded {
217 provider: provider.into(),
218 retry_after_seconds,
219 }
220 }
221
222 pub fn response_error(message: impl Into<String>) -> Self {
223 Self::ResponseError {
224 message: message.into(),
225 }
226 }
227}
228
229impl MemoryError {
230 pub fn store_not_found(store_id: impl Into<String>) -> Self {
231 Self::StoreNotFound {
232 store_id: store_id.into(),
233 }
234 }
235
236 pub fn key_not_found(key: impl Into<String>, store_id: impl Into<String>) -> Self {
237 Self::KeyNotFound {
238 key: key.into(),
239 store_id: store_id.into(),
240 }
241 }
242
243 pub fn capacity_exceeded(current: usize, max: usize) -> Self {
244 Self::CapacityExceeded { current, max }
245 }
246
247 pub fn invalid_operation(operation: impl Into<String>, store_type: impl Into<String>) -> Self {
248 Self::InvalidOperation {
249 operation: operation.into(),
250 store_type: store_type.into(),
251 }
252 }
253
254 pub fn serialization_failed(reason: impl Into<String>) -> Self {
255 Self::SerializationFailed {
256 reason: reason.into(),
257 }
258 }
259
260 pub fn corrupted(details: impl Into<String>) -> Self {
261 Self::Corrupted {
262 details: details.into(),
263 }
264 }
265}
266
267impl ToolError {
268 pub fn not_found(tool_name: impl Into<String>) -> Self {
269 Self::NotFound {
270 tool_name: tool_name.into(),
271 }
272 }
273
274 pub fn execution_failed(tool_name: impl Into<String>, reason: impl Into<String>) -> Self {
275 Self::ExecutionFailed {
276 tool_name: tool_name.into(),
277 reason: reason.into(),
278 }
279 }
280
281 pub fn invalid_parameters(tool_name: impl Into<String>, details: impl Into<String>) -> Self {
282 Self::InvalidParameters {
283 tool_name: tool_name.into(),
284 details: details.into(),
285 }
286 }
287
288 pub fn timeout(tool_name: impl Into<String>, timeout_ms: u64) -> Self {
289 Self::Timeout {
290 tool_name: tool_name.into(),
291 timeout_ms,
292 }
293 }
294
295 pub fn permission_denied(tool_name: impl Into<String>, reason: impl Into<String>) -> Self {
296 Self::PermissionDenied {
297 tool_name: tool_name.into(),
298 reason: reason.into(),
299 }
300 }
301
302 pub fn dependency_missing(tool_name: impl Into<String>, dependency: impl Into<String>) -> Self {
303 Self::DependencyMissing {
304 tool_name: tool_name.into(),
305 dependency: dependency.into(),
306 }
307 }
308}
309
310impl ExecutionError {
311 pub fn mission_not_found(mission_id: impl Into<String>) -> Self {
312 Self::MissionNotFound {
313 mission_id: mission_id.into(),
314 }
315 }
316
317 pub fn step_failed(
318 step_id: impl Into<String>,
319 mission_id: impl Into<String>,
320 reason: impl Into<String>,
321 ) -> Self {
322 Self::StepFailed {
323 step_id: step_id.into(),
324 mission_id: mission_id.into(),
325 reason: reason.into(),
326 }
327 }
328
329 pub fn dependency_cycle(mission_id: impl Into<String>, cycle: impl Into<String>) -> Self {
330 Self::DependencyCycle {
331 mission_id: mission_id.into(),
332 cycle: cycle.into(),
333 }
334 }
335
336 pub fn resource_exhausted(resource: impl Into<String>, details: impl Into<String>) -> Self {
337 Self::ResourceExhausted {
338 resource: resource.into(),
339 details: details.into(),
340 }
341 }
342
343 pub fn timeout(mission_id: impl Into<String>, timeout_ms: u64) -> Self {
344 Self::Timeout {
345 mission_id: mission_id.into(),
346 timeout_ms,
347 }
348 }
349
350 pub fn invalid_state(state: impl Into<String>, operation: impl Into<String>) -> Self {
351 Self::InvalidState {
352 state: state.into(),
353 operation: operation.into(),
354 }
355 }
356}
357
358impl SchemaError {
359 pub fn validation_failed(schema_name: impl Into<String>, errors: Vec<String>) -> Self {
360 Self::ValidationFailed {
361 schema_name: schema_name.into(),
362 errors,
363 }
364 }
365
366 pub fn schema_not_found(schema_name: impl Into<String>) -> Self {
367 Self::SchemaNotFound {
368 schema_name: schema_name.into(),
369 }
370 }
371
372 pub fn invalid_definition(schema_name: impl Into<String>, reason: impl Into<String>) -> Self {
373 Self::InvalidDefinition {
374 schema_name: schema_name.into(),
375 reason: reason.into(),
376 }
377 }
378
379 pub fn version_mismatch(expected: impl Into<String>, actual: impl Into<String>) -> Self {
380 Self::VersionMismatch {
381 expected: expected.into(),
382 actual: actual.into(),
383 }
384 }
385}
386
387impl From<anyhow::Error> for RustChainError {
389 fn from(e: anyhow::Error) -> Self {
390 RustChainError::Unknown {
391 message: e.to_string(),
392 }
393 }
394}
395
396impl From<String> for RustChainError {
398 fn from(e: String) -> Self {
399 RustChainError::Unknown { message: e }
400 }
401}
402
403impl From<&str> for RustChainError {
404 fn from(e: &str) -> Self {
405 RustChainError::Unknown {
406 message: e.to_string(),
407 }
408 }
409}
410
411#[cfg(test)]
412mod tests {
413 use super::*;
414 use serde_json;
415
416 #[test]
417 fn test_config_error_variants() {
418 let missing = ConfigError::missing_key("api_key");
419 assert!(missing.to_string().contains("Missing required configuration key: api_key"));
420
421 let invalid = ConfigError::invalid_value("timeout", "number", "string");
422 assert!(invalid.to_string().contains("Invalid configuration value for timeout"));
423 assert!(invalid.to_string().contains("expected number, got string"));
424
425 let not_found = ConfigError::file_not_found("/path/to/config.toml");
426 assert!(not_found.to_string().contains("Configuration file not found: /path/to/config.toml"));
427
428 let parse = ConfigError::parse_error("invalid TOML syntax");
429 assert!(parse.to_string().contains("Failed to parse configuration: invalid TOML syntax"));
430 }
431
432 #[test]
433 fn test_config_error_serialization() {
434 let error = ConfigError::missing_key("test_key");
435
436 let serialized = serde_json::to_string(&error).unwrap();
438 assert!(serialized.contains("MissingKey"));
439 assert!(serialized.contains("test_key"));
440
441 let deserialized: ConfigError = serde_json::from_str(&serialized).unwrap();
443 match deserialized {
444 ConfigError::MissingKey { key } => assert_eq!(key, "test_key"),
445 _ => panic!("Deserialization failed"),
446 }
447 }
448
449 #[test]
450 fn test_llm_error_variants() {
451 let unavailable = LlmError::service_unavailable("openai");
452 assert!(unavailable.to_string().contains("LLM service unavailable: openai"));
453
454 let auth_failed = LlmError::authentication_failed("anthropic", "invalid API key");
455 assert!(auth_failed.to_string().contains("Authentication failed for anthropic"));
456 assert!(auth_failed.to_string().contains("invalid API key"));
457
458 let timeout = LlmError::timeout(30000);
459 assert!(timeout.to_string().contains("Request timeout after 30000ms"));
460
461 let invalid_prompt = LlmError::invalid_prompt("empty prompt");
462 assert!(invalid_prompt.to_string().contains("Invalid prompt: empty prompt"));
463
464 let rate_limit = LlmError::rate_limit_exceeded("openai", 60);
465 assert!(rate_limit.to_string().contains("Rate limit exceeded for openai"));
466 assert!(rate_limit.to_string().contains("retry after 60s"));
467
468 let response_error = LlmError::response_error("model returned error");
469 assert!(response_error.to_string().contains("LLM responded with error: model returned error"));
470 }
471
472 #[test]
473 fn test_llm_error_serialization() {
474 let error = LlmError::timeout(5000);
475
476 let serialized = serde_json::to_string(&error).unwrap();
477 assert!(serialized.contains("Timeout"));
478 assert!(serialized.contains("5000"));
479
480 let deserialized: LlmError = serde_json::from_str(&serialized).unwrap();
481 match deserialized {
482 LlmError::Timeout { timeout_ms } => assert_eq!(timeout_ms, 5000),
483 _ => panic!("Deserialization failed"),
484 }
485 }
486
487 #[test]
488 fn test_memory_error_variants() {
489 let store_not_found = MemoryError::store_not_found("main_store");
490 assert!(store_not_found.to_string().contains("Memory store not found: main_store"));
491
492 let key_not_found = MemoryError::key_not_found("user_data", "session_store");
493 assert!(key_not_found.to_string().contains("Memory key not found: user_data in store session_store"));
494
495 let capacity = MemoryError::capacity_exceeded(150, 100);
496 assert!(capacity.to_string().contains("Memory store capacity exceeded: 150/100"));
497
498 let invalid_op = MemoryError::invalid_operation("delete", "read_only");
499 assert!(invalid_op.to_string().contains("Invalid memory operation: delete on read_only"));
500
501 let serialization = MemoryError::serialization_failed("invalid JSON");
502 assert!(serialization.to_string().contains("Memory serialization failed: invalid JSON"));
503
504 let corrupted = MemoryError::corrupted("checksum mismatch");
505 assert!(corrupted.to_string().contains("Memory store corrupted: checksum mismatch"));
506 }
507
508 #[test]
509 fn test_memory_error_serialization() {
510 let error = MemoryError::capacity_exceeded(200, 100);
511
512 let serialized = serde_json::to_string(&error).unwrap();
513 assert!(serialized.contains("CapacityExceeded"));
514 assert!(serialized.contains("200"));
515 assert!(serialized.contains("100"));
516
517 let deserialized: MemoryError = serde_json::from_str(&serialized).unwrap();
518 match deserialized {
519 MemoryError::CapacityExceeded { current, max } => {
520 assert_eq!(current, 200);
521 assert_eq!(max, 100);
522 },
523 _ => panic!("Deserialization failed"),
524 }
525 }
526
527 #[test]
528 fn test_tool_error_variants() {
529 let not_found = ToolError::not_found("missing_tool");
530 assert!(not_found.to_string().contains("Tool not found: missing_tool"));
531
532 let execution_failed = ToolError::execution_failed("file_reader", "permission denied");
533 assert!(execution_failed.to_string().contains("Tool execution failed: file_reader - permission denied"));
534
535 let invalid_params = ToolError::invalid_parameters("calculator", "missing operands");
536 assert!(invalid_params.to_string().contains("Invalid tool parameters for calculator: missing operands"));
537
538 let timeout = ToolError::timeout("slow_tool", 10000);
539 assert!(timeout.to_string().contains("Tool timeout: slow_tool exceeded 10000ms"));
540
541 let permission = ToolError::permission_denied("system_tool", "insufficient privileges");
542 assert!(permission.to_string().contains("Tool permission denied: system_tool - insufficient privileges"));
543
544 let dependency = ToolError::dependency_missing("advanced_tool", "basic_tool");
545 assert!(dependency.to_string().contains("Tool dependency missing: advanced_tool requires basic_tool"));
546 }
547
548 #[test]
549 fn test_tool_error_serialization() {
550 let error = ToolError::timeout("test_tool", 5000);
551
552 let serialized = serde_json::to_string(&error).unwrap();
553 assert!(serialized.contains("Timeout"));
554 assert!(serialized.contains("test_tool"));
555 assert!(serialized.contains("5000"));
556
557 let deserialized: ToolError = serde_json::from_str(&serialized).unwrap();
558 match deserialized {
559 ToolError::Timeout { tool_name, timeout_ms } => {
560 assert_eq!(tool_name, "test_tool");
561 assert_eq!(timeout_ms, 5000);
562 },
563 _ => panic!("Deserialization failed"),
564 }
565 }
566
567 #[test]
568 fn test_execution_error_variants() {
569 let mission_not_found = ExecutionError::mission_not_found("mission_123");
570 assert!(mission_not_found.to_string().contains("Mission not found: mission_123"));
571
572 let step_failed = ExecutionError::step_failed("step_1", "mission_456", "validation error");
573 assert!(step_failed.to_string().contains("Step failed: step_1 in mission mission_456 - validation error"));
574
575 let cycle = ExecutionError::dependency_cycle("mission_789", "A -> B -> C -> A");
576 assert!(cycle.to_string().contains("Dependency cycle detected in mission mission_789: A -> B -> C -> A"));
577
578 let resource = ExecutionError::resource_exhausted("memory", "8GB limit reached");
579 assert!(resource.to_string().contains("Resource exhausted: memory - 8GB limit reached"));
580
581 let timeout = ExecutionError::timeout("mission_abc", 60000);
582 assert!(timeout.to_string().contains("Execution timeout: mission mission_abc exceeded 60000ms"));
583
584 let invalid_state = ExecutionError::invalid_state("running", "pause");
585 assert!(invalid_state.to_string().contains("Invalid mission state: running for operation pause"));
586 }
587
588 #[test]
589 fn test_execution_error_serialization() {
590 let error = ExecutionError::step_failed("test_step", "test_mission", "test_reason");
591
592 let serialized = serde_json::to_string(&error).unwrap();
593 assert!(serialized.contains("StepFailed"));
594 assert!(serialized.contains("test_step"));
595 assert!(serialized.contains("test_mission"));
596 assert!(serialized.contains("test_reason"));
597
598 let deserialized: ExecutionError = serde_json::from_str(&serialized).unwrap();
599 match deserialized {
600 ExecutionError::StepFailed { step_id, mission_id, reason } => {
601 assert_eq!(step_id, "test_step");
602 assert_eq!(mission_id, "test_mission");
603 assert_eq!(reason, "test_reason");
604 },
605 _ => panic!("Deserialization failed"),
606 }
607 }
608
609 #[test]
610 fn test_schema_error_variants() {
611 let validation_failed = SchemaError::validation_failed("user_schema", vec![
612 "missing field 'name'".to_string(),
613 "invalid email format".to_string(),
614 ]);
615 assert!(validation_failed.to_string().contains("Schema validation failed for user_schema"));
616 assert!(validation_failed.to_string().contains("missing field 'name'"));
617 assert!(validation_failed.to_string().contains("invalid email format"));
618
619 let not_found = SchemaError::schema_not_found("unknown_schema");
620 assert!(not_found.to_string().contains("Schema not found: unknown_schema"));
621
622 let invalid_def = SchemaError::invalid_definition("bad_schema", "circular reference");
623 assert!(invalid_def.to_string().contains("Invalid schema definition: bad_schema - circular reference"));
624
625 let version_mismatch = SchemaError::version_mismatch("2.0", "1.5");
626 assert!(version_mismatch.to_string().contains("Schema version mismatch: expected 2.0, got 1.5"));
627 }
628
629 #[test]
630 fn test_schema_error_serialization() {
631 let error = SchemaError::validation_failed("test_schema", vec!["error1".to_string(), "error2".to_string()]);
632
633 let serialized = serde_json::to_string(&error).unwrap();
634 assert!(serialized.contains("ValidationFailed"));
635 assert!(serialized.contains("test_schema"));
636 assert!(serialized.contains("error1"));
637 assert!(serialized.contains("error2"));
638
639 let deserialized: SchemaError = serde_json::from_str(&serialized).unwrap();
640 match deserialized {
641 SchemaError::ValidationFailed { schema_name, errors } => {
642 assert_eq!(schema_name, "test_schema");
643 assert_eq!(errors, vec!["error1", "error2"]);
644 },
645 _ => panic!("Deserialization failed"),
646 }
647 }
648
649 #[test]
650 fn test_rustchain_error_variants() {
651 let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
653 let rustchain_io: RustChainError = io_error.into();
654 assert!(rustchain_io.to_string().contains("IO error"));
655
656 let config_error = ConfigError::missing_key("test");
658 let rustchain_config: RustChainError = config_error.into();
659 assert!(rustchain_config.to_string().contains("Configuration error"));
660
661 let llm_error = LlmError::timeout(1000);
662 let rustchain_llm: RustChainError = llm_error.into();
663 assert!(rustchain_llm.to_string().contains("LLM error"));
664
665 let memory_error = MemoryError::store_not_found("test");
666 let rustchain_memory: RustChainError = memory_error.into();
667 assert!(rustchain_memory.to_string().contains("Memory error"));
668
669 let tool_error = ToolError::not_found("test");
670 let rustchain_tool: RustChainError = tool_error.into();
671 assert!(rustchain_tool.to_string().contains("Tool error"));
672
673 let execution_error = ExecutionError::mission_not_found("test");
674 let rustchain_execution: RustChainError = execution_error.into();
675 assert!(rustchain_execution.to_string().contains("Execution error"));
676
677 let schema_error = SchemaError::schema_not_found("test");
678 let rustchain_schema: RustChainError = schema_error.into();
679 assert!(rustchain_schema.to_string().contains("Schema validation error"));
680 }
681
682 #[test]
683 fn test_rustchain_error_json_yaml() {
684 let json_error = serde_json::from_str::<serde_json::Value>("invalid json");
686 assert!(json_error.is_err());
687 let rustchain_json: RustChainError = json_error.unwrap_err().into();
688 assert!(rustchain_json.to_string().contains("JSON serialization error"));
689
690 let yaml_error = serde_yaml::from_str::<serde_yaml::Value>("invalid: yaml: structure");
692 assert!(yaml_error.is_err());
693 let rustchain_yaml: RustChainError = yaml_error.unwrap_err().into();
694 assert!(rustchain_yaml.to_string().contains("YAML serialization error"));
695 }
696
697 #[test]
698 fn test_rustchain_error_unknown() {
699 let unknown = RustChainError::Unknown {
700 message: "test unknown error".to_string(),
701 };
702 assert!(unknown.to_string().contains("Unknown error: test unknown error"));
703 }
704
705 #[test]
706 fn test_rustchain_error_exec() {
707 let exec = RustChainError::Exec("execution failed".to_string());
708 assert!(exec.to_string().contains("Execution error: execution failed"));
709 }
710
711 #[test]
712 fn test_anyhow_conversion() {
713 let anyhow_error = anyhow::anyhow!("test anyhow error");
714 let rustchain_error: RustChainError = anyhow_error.into();
715
716 match rustchain_error {
717 RustChainError::Unknown { message } => {
718 assert_eq!(message, "test anyhow error");
719 },
720 _ => panic!("Expected Unknown variant"),
721 }
722 }
723
724 #[test]
725 fn test_string_conversions() {
726 let string_error = "string error".to_string();
728 let rustchain_string: RustChainError = string_error.into();
729
730 match rustchain_string {
731 RustChainError::Unknown { message } => {
732 assert_eq!(message, "string error");
733 },
734 _ => panic!("Expected Unknown variant"),
735 }
736
737 let str_error = "str error";
739 let rustchain_str: RustChainError = str_error.into();
740
741 match rustchain_str {
742 RustChainError::Unknown { message } => {
743 assert_eq!(message, "str error");
744 },
745 _ => panic!("Expected Unknown variant"),
746 }
747 }
748
749 #[test]
750 fn test_error_cloning() {
751 let config = ConfigError::missing_key("test");
753 let _config_clone = config.clone();
754
755 let llm = LlmError::timeout(1000);
756 let _llm_clone = llm.clone();
757
758 let memory = MemoryError::store_not_found("test");
759 let _memory_clone = memory.clone();
760
761 let tool = ToolError::not_found("test");
762 let _tool_clone = tool.clone();
763
764 let execution = ExecutionError::mission_not_found("test");
765 let _execution_clone = execution.clone();
766
767 let schema = SchemaError::schema_not_found("test");
768 let _schema_clone = schema.clone();
769 }
770
771 #[test]
772 fn test_error_debug_formatting() {
773 let config_error = ConfigError::missing_key("api_key");
774 let debug_string = format!("{:?}", config_error);
775 assert!(debug_string.contains("MissingKey"));
776 assert!(debug_string.contains("api_key"));
777
778 let llm_error = LlmError::timeout(5000);
779 let debug_string = format!("{:?}", llm_error);
780 assert!(debug_string.contains("Timeout"));
781 assert!(debug_string.contains("5000"));
782 }
783
784 #[test]
785 fn test_result_type_alias() {
786 fn test_function() -> Result<String> {
788 Ok("success".to_string())
789 }
790
791 fn test_error_function() -> Result<String> {
792 Err(ConfigError::missing_key("test").into())
793 }
794
795 let success = test_function();
796 assert!(success.is_ok());
797 assert_eq!(success.unwrap(), "success");
798
799 let error = test_error_function();
800 assert!(error.is_err());
801
802 match error.unwrap_err() {
803 RustChainError::Config(ConfigError::MissingKey { key }) => {
804 assert_eq!(key, "test");
805 },
806 _ => panic!("Expected Config error"),
807 }
808 }
809
810 #[test]
811 fn test_complex_error_scenarios() {
812 let validation_errors = vec![
816 "field 'name' is required".to_string(),
817 "field 'email' must be valid email address".to_string(),
818 "field 'age' must be between 0 and 150".to_string(),
819 ];
820 let schema_error = SchemaError::validation_failed("user_registration", validation_errors);
821 let rustchain_error: RustChainError = schema_error.into();
822
823 let error_string = rustchain_error.to_string();
824 assert!(error_string.contains("field 'name' is required"));
825 assert!(error_string.contains("field 'email' must be valid"));
826 assert!(error_string.contains("field 'age' must be between"));
827
828 let execution_error = ExecutionError::step_failed(
830 "data_validation_step",
831 "user_onboarding_mission",
832 "Schema validation failed: multiple field errors"
833 );
834
835 let error_string = execution_error.to_string();
836 assert!(error_string.contains("data_validation_step"));
837 assert!(error_string.contains("user_onboarding_mission"));
838 assert!(error_string.contains("Schema validation failed"));
839 }
840
841 #[test]
842 fn test_error_constructor_functions() {
843 let config1 = ConfigError::missing_key("test_key");
847 let config2 = ConfigError::invalid_value("timeout", "u64", "string");
848 assert!(config1.to_string().contains("test_key"));
849 assert!(config2.to_string().contains("timeout"));
850
851 let config3 = ConfigError::missing_key("test_key".to_string());
853 let config4 = ConfigError::file_not_found("/path/to/file".to_string());
854 assert!(config3.to_string().contains("test_key"));
855 assert!(config4.to_string().contains("/path/to/file"));
856
857 let llm1 = LlmError::service_unavailable("openai");
859 let llm2 = LlmError::authentication_failed("anthropic".to_string(), "invalid key".to_string());
860 assert!(llm1.to_string().contains("openai"));
861 assert!(llm2.to_string().contains("anthropic"));
862 assert!(llm2.to_string().contains("invalid key"));
863
864 let mem1 = MemoryError::key_not_found("user_data", "session_store");
866 let mem2 = MemoryError::invalid_operation("delete".to_string(), "readonly".to_string());
867 assert!(mem1.to_string().contains("user_data"));
868 assert!(mem1.to_string().contains("session_store"));
869 assert!(mem2.to_string().contains("delete"));
870 assert!(mem2.to_string().contains("readonly"));
871
872 let tool1 = ToolError::execution_failed("file_tool", "permission denied");
874 let tool2 = ToolError::dependency_missing("advanced_tool".to_string(), "basic_tool".to_string());
875 assert!(tool1.to_string().contains("file_tool"));
876 assert!(tool1.to_string().contains("permission denied"));
877 assert!(tool2.to_string().contains("advanced_tool"));
878 assert!(tool2.to_string().contains("basic_tool"));
879
880 let exec1 = ExecutionError::step_failed("step1", "mission1", "failed");
882 let exec2 = ExecutionError::dependency_cycle("mission2".to_string(), "A->B->A".to_string());
883 assert!(exec1.to_string().contains("step1"));
884 assert!(exec1.to_string().contains("mission1"));
885 assert!(exec1.to_string().contains("failed"));
886 assert!(exec2.to_string().contains("mission2"));
887 assert!(exec2.to_string().contains("A->B->A"));
888
889 let schema1 = SchemaError::schema_not_found("missing_schema");
891 let schema2 = SchemaError::version_mismatch("2.0".to_string(), "1.0".to_string());
892 assert!(schema1.to_string().contains("missing_schema"));
893 assert!(schema2.to_string().contains("expected 2.0, got 1.0"));
894 }
895
896 #[test]
897 fn test_edge_cases() {
898 let config_empty = ConfigError::missing_key("");
900 assert!(config_empty.to_string().contains("Missing required configuration key: "));
901
902 let llm_empty = LlmError::service_unavailable("");
903 assert!(llm_empty.to_string().contains("LLM service unavailable: "));
904
905 let long_string = "a".repeat(1000);
907 let config_long = ConfigError::parse_error(&long_string);
908 assert!(config_long.to_string().contains(&long_string));
909
910 let special_chars = "test@#$%^&*()_+-=[]{}|;':\",./<>?";
912 let memory_special = MemoryError::store_not_found(special_chars);
913 assert!(memory_special.to_string().contains(special_chars));
914
915 let timeout_zero = LlmError::timeout(0);
917 assert!(timeout_zero.to_string().contains("timeout after 0ms"));
918
919 let capacity_zero = MemoryError::capacity_exceeded(0, 0);
920 assert!(capacity_zero.to_string().contains("0/0"));
921
922 let timeout_large = ToolError::timeout("tool", u64::MAX);
924 assert!(timeout_large.to_string().contains(&u64::MAX.to_string()));
925 }
926}