1use std::fmt;
6
7#[derive(Debug, Clone, PartialEq)]
11pub struct ExecutionLimits {
12 pub max_steps: Option<usize>,
15
16 pub max_recursion_depth: Option<usize>,
19
20 pub max_duration_ms: Option<u64>,
23
24 pub max_memory_bytes: Option<usize>,
27}
28
29impl Default for ExecutionLimits {
30 fn default() -> Self {
31 Self {
32 max_steps: Some(1_000_000), max_recursion_depth: Some(1000), max_duration_ms: Some(30_000), max_memory_bytes: None,
36 }
37 }
38}
39
40impl ExecutionLimits {
41 pub fn unrestricted() -> Self {
43 Self {
44 max_steps: None,
45 max_recursion_depth: None,
46 max_duration_ms: None,
47 max_memory_bytes: None,
48 }
49 }
50
51 pub fn strict() -> Self {
53 Self {
54 max_steps: Some(100_000), max_recursion_depth: Some(100), max_duration_ms: Some(5_000), max_memory_bytes: None,
58 }
59 }
60
61 pub fn lenient() -> Self {
63 Self {
64 max_steps: Some(10_000_000), max_recursion_depth: Some(5000), max_duration_ms: Some(300_000), max_memory_bytes: None,
68 }
69 }
70}
71
72#[derive(Debug, Clone, PartialEq)]
76pub enum ExecutionLimitError {
77 StepLimitExceeded { steps: usize, limit: usize },
79
80 RecursionDepthExceeded { depth: usize, limit: usize },
82
83 DurationExceeded { duration_ms: u64, limit: u64 },
85
86 MemoryLimitExceeded { bytes: usize, limit: usize },
88}
89
90impl fmt::Display for ExecutionLimitError {
91 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92 match self {
93 ExecutionLimitError::StepLimitExceeded { steps, limit } => write!(
94 f,
95 "Execution step limit exceeded: {} steps (limit: {})",
96 steps, limit
97 ),
98 ExecutionLimitError::RecursionDepthExceeded { depth, limit } => write!(
99 f,
100 "Recursion depth limit exceeded: {} levels (limit: {})",
101 depth, limit
102 ),
103 ExecutionLimitError::DurationExceeded { duration_ms, limit } => write!(
104 f,
105 "Execution duration limit exceeded: {} ms (limit: {} ms)",
106 duration_ms, limit
107 ),
108 ExecutionLimitError::MemoryLimitExceeded { bytes, limit } => write!(
109 f,
110 "Memory limit exceeded: {} bytes (limit: {} bytes)",
111 bytes, limit
112 ),
113 }
114 }
115}
116
117impl std::error::Error for ExecutionLimitError {}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 #[test]
124 fn test_default_limits() {
125 let limits = ExecutionLimits::default();
126 assert_eq!(limits.max_steps, Some(1_000_000));
127 assert_eq!(limits.max_recursion_depth, Some(1000));
128 assert_eq!(limits.max_duration_ms, Some(30_000));
129 assert_eq!(limits.max_memory_bytes, None);
130 }
131
132 #[test]
133 fn test_unrestricted_limits() {
134 let limits = ExecutionLimits::unrestricted();
135 assert_eq!(limits.max_steps, None);
136 assert_eq!(limits.max_recursion_depth, None);
137 assert_eq!(limits.max_duration_ms, None);
138 assert_eq!(limits.max_memory_bytes, None);
139 }
140
141 #[test]
142 fn test_strict_limits() {
143 let limits = ExecutionLimits::strict();
144 assert_eq!(limits.max_steps, Some(100_000));
145 assert_eq!(limits.max_recursion_depth, Some(100));
146 assert_eq!(limits.max_duration_ms, Some(5_000));
147 assert_eq!(limits.max_memory_bytes, None);
148 }
149
150 #[test]
151 fn test_lenient_limits() {
152 let limits = ExecutionLimits::lenient();
153 assert_eq!(limits.max_steps, Some(10_000_000));
154 assert_eq!(limits.max_recursion_depth, Some(5000));
155 assert_eq!(limits.max_duration_ms, Some(300_000));
156 assert_eq!(limits.max_memory_bytes, None);
157 }
158
159 #[test]
160 fn test_error_display() {
161 let err = ExecutionLimitError::StepLimitExceeded {
162 steps: 1000,
163 limit: 100,
164 };
165 assert!(err.to_string().contains("1000"));
166 assert!(err.to_string().contains("100"));
167
168 let err = ExecutionLimitError::RecursionDepthExceeded {
169 depth: 500,
170 limit: 100,
171 };
172 assert!(err.to_string().contains("500"));
173 assert!(err.to_string().contains("100"));
174
175 let err = ExecutionLimitError::DurationExceeded {
176 duration_ms: 5000,
177 limit: 1000,
178 };
179 assert!(err.to_string().contains("5000"));
180 assert!(err.to_string().contains("1000"));
181 }
182}