depyler-core 3.23.0

Core transpilation engine for the Depyler Python-to-Rust transpiler
Documentation
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
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
//! TDD Tests for @staticmethod Decorator (DEPYLER-0112 Phase 1)
//!
//! Phase 1: @staticmethod decorator support
//! Python: @staticmethod def method() → Rust: fn method() (no &self)
//!
//! Test Coverage:
//! 1. Simple staticmethod with no parameters
//! 2. Staticmethod with parameters
//! 3. Staticmethod with return type
//! 4. Staticmethod accessing class constants
//! 5. Multiple staticmethods in same class
//! 6. Staticmethod calling another staticmethod
//! 7. Instance method calling staticmethod
//! 8. Staticmethod with type annotations
//! 9. Staticmethod as utility function
//! 10. Mix of static and instance methods

use depyler_core::DepylerPipeline;

#[test]
fn test_simple_staticmethod_no_params() {
    let python = r#"
class Math:
    @staticmethod
    def pi() -> float:
        return 3.14159
"#;

    let pipeline = DepylerPipeline::new();
    let result = pipeline.transpile(python);
    assert!(
        result.is_ok(),
        "Transpilation failed: {:?}",
        result.as_ref().err()
    );

    let rust_code = result.unwrap();

    assert!(
        rust_code.contains("struct Math"),
        "Should have Math struct.\nGot:\n{}",
        rust_code
    );

    assert!(
        rust_code.contains("fn pi"),
        "Should have pi function.\nGot:\n{}",
        rust_code
    );

    // Should NOT have &self or &mut self parameter
    let has_self = rust_code.contains("&self") || rust_code.contains("& self");
    assert!(
        !has_self,
        "Staticmethod should not have &self parameter.\nGot:\n{}",
        rust_code
    );
}

#[test]
fn test_staticmethod_with_parameters() {
    let python = r#"
class Calculator:
    @staticmethod
    def add(a: int, b: int) -> int:
        return a + b
"#;

    let pipeline = DepylerPipeline::new();
    let result = pipeline.transpile(python);
    assert!(
        result.is_ok(),
        "Transpilation failed: {:?}",
        result.as_ref().err()
    );

    let rust_code = result.unwrap();

    assert!(
        rust_code.contains("fn add"),
        "Should have add function.\nGot:\n{}",
        rust_code
    );

    // Should have parameters a and b
    assert!(
        rust_code.contains("a") && rust_code.contains("b"),
        "Should have a and b parameters.\nGot:\n{}",
        rust_code
    );

    // Should NOT have &self
    let has_self = rust_code.contains("&self") || rust_code.contains("& self");
    assert!(
        !has_self,
        "Staticmethod should not have &self.\nGot:\n{}",
        rust_code
    );
}

#[test]
fn test_staticmethod_with_return_type() {
    let python = r#"
class StringUtils:
    @staticmethod
    def uppercase(text: str) -> str:
        return text.upper()
"#;

    let pipeline = DepylerPipeline::new();
    let result = pipeline.transpile(python);
    assert!(
        result.is_ok(),
        "Transpilation failed: {:?}",
        result.as_ref().err()
    );

    let rust_code = result.unwrap();

    assert!(
        rust_code.contains("fn uppercase"),
        "Should have uppercase function.\nGot:\n{}",
        rust_code
    );

    // Should have String return type
    let has_string_return = rust_code.contains("-> String")
        || rust_code.contains("-> str")
        || rust_code.contains("String");
    assert!(
        has_string_return,
        "Should have String return type.\nGot:\n{}",
        rust_code
    );
}

#[test]
fn test_staticmethod_accessing_class_constants() {
    let python = r#"
class Config:
    MAX_SIZE: int = 1000

    @staticmethod
    def get_max_size() -> int:
        return Config.MAX_SIZE
"#;

    let pipeline = DepylerPipeline::new();
    let result = pipeline.transpile(python);
    assert!(
        result.is_ok(),
        "Transpilation failed: {:?}",
        result.as_ref().err()
    );

    let rust_code = result.unwrap();

    assert!(
        rust_code.contains("fn get_max_size"),
        "Should have get_max_size function.\nGot:\n{}",
        rust_code
    );

    // Should have MAX_SIZE constant
    let has_max_size = rust_code.contains("MAX_SIZE") || rust_code.contains("max_size");
    assert!(
        has_max_size,
        "Should reference MAX_SIZE.\nGot:\n{}",
        rust_code
    );
}

#[test]
fn test_multiple_staticmethods() {
    let python = r#"
class Math:
    @staticmethod
    def add(a: int, b: int) -> int:
        return a + b

    @staticmethod
    def multiply(a: int, b: int) -> int:
        return a * b

    @staticmethod
    def subtract(a: int, b: int) -> int:
        return a - b
"#;

    let pipeline = DepylerPipeline::new();
    let result = pipeline.transpile(python);
    assert!(
        result.is_ok(),
        "Transpilation failed: {:?}",
        result.as_ref().err()
    );

    let rust_code = result.unwrap();

    // Should have all three methods
    assert!(
        rust_code.contains("fn add"),
        "Should have add function.\nGot:\n{}",
        rust_code
    );

    assert!(
        rust_code.contains("fn multiply"),
        "Should have multiply function.\nGot:\n{}",
        rust_code
    );

    assert!(
        rust_code.contains("fn subtract"),
        "Should have subtract function.\nGot:\n{}",
        rust_code
    );
}

#[test]
fn test_staticmethod_calling_another_staticmethod() {
    let python = r#"
class Math:
    @staticmethod
    def square(x: int) -> int:
        return x * x

    @staticmethod
    def sum_of_squares(a: int, b: int) -> int:
        return Math.square(a) + Math.square(b)
"#;

    let pipeline = DepylerPipeline::new();
    let result = pipeline.transpile(python);
    assert!(
        result.is_ok(),
        "Transpilation failed: {:?}",
        result.as_ref().err()
    );

    let rust_code = result.unwrap();

    assert!(
        rust_code.contains("fn square"),
        "Should have square function.\nGot:\n{}",
        rust_code
    );

    assert!(
        rust_code.contains("fn sum_of_squares"),
        "Should have sum_of_squares function.\nGot:\n{}",
        rust_code
    );

    // Should call Math::square or similar
    let calls_square = rust_code.contains("square");
    assert!(
        calls_square,
        "Should call square method.\nGot:\n{}",
        rust_code
    );
}

#[test]
fn test_instance_method_calling_staticmethod() {
    let python = r#"
class Calculator:
    def __init__(self, offset: int):
        self.offset = offset

    @staticmethod
    def double(x: int) -> int:
        return x * 2

    def apply_double(self, x: int) -> int:
        return Calculator.double(x) + self.offset
"#;

    let pipeline = DepylerPipeline::new();
    let result = pipeline.transpile(python);
    assert!(
        result.is_ok(),
        "Transpilation failed: {:?}",
        result.as_ref().err()
    );

    let rust_code = result.unwrap();

    assert!(
        rust_code.contains("fn double"),
        "Should have double function.\nGot:\n{}",
        rust_code
    );

    assert!(
        rust_code.contains("fn apply_double"),
        "Should have apply_double method.\nGot:\n{}",
        rust_code
    );

    // apply_double should have &self
    let has_self = rust_code.contains("&self") || rust_code.contains("& self");
    assert!(
        has_self,
        "apply_double should have &self.\nGot:\n{}",
        rust_code
    );
}

#[test]
fn test_staticmethod_with_type_annotations() {
    let python = r#"
class Converter:
    @staticmethod
    def celsius_to_fahrenheit(celsius: float) -> float:
        return celsius * 9.0 / 5.0 + 32.0
"#;

    let pipeline = DepylerPipeline::new();
    let result = pipeline.transpile(python);
    assert!(
        result.is_ok(),
        "Transpilation failed: {:?}",
        result.as_ref().err()
    );

    let rust_code = result.unwrap();

    assert!(
        rust_code.contains("fn celsius_to_fahrenheit"),
        "Should have celsius_to_fahrenheit function.\nGot:\n{}",
        rust_code
    );

    // Should have float parameters and return
    let has_float =
        rust_code.contains("f32") || rust_code.contains("f64") || rust_code.contains("celsius");
    assert!(has_float, "Should have float type.\nGot:\n{}", rust_code);
}

#[test]
fn test_staticmethod_as_utility_function() {
    let python = r#"
class Validator:
    @staticmethod
    def is_valid_email(email: str) -> bool:
        return "@" in email
"#;

    let pipeline = DepylerPipeline::new();
    let result = pipeline.transpile(python);
    assert!(
        result.is_ok(),
        "Transpilation failed: {:?}",
        result.as_ref().err()
    );

    let rust_code = result.unwrap();

    assert!(
        rust_code.contains("fn is_valid_email"),
        "Should have is_valid_email function.\nGot:\n{}",
        rust_code
    );

    // Should have bool return type
    let has_bool_return = rust_code.contains("-> bool") || rust_code.contains("bool");
    assert!(
        has_bool_return,
        "Should have bool return type.\nGot:\n{}",
        rust_code
    );
}

#[test]
fn test_mix_static_and_instance_methods() {
    let python = r#"
class Counter:
    @staticmethod
    def default_start() -> int:
        return 0

    def __init__(self):
        self.count = Counter.default_start()

    def increment(self) -> None:
        self.count = self.count + 1

    def get_count(self) -> int:
        return self.count

    @staticmethod
    def max_count() -> int:
        return 100
"#;

    let pipeline = DepylerPipeline::new();
    let result = pipeline.transpile(python);
    assert!(
        result.is_ok(),
        "Transpilation failed: {:?}",
        result.as_ref().err()
    );

    let rust_code = result.unwrap();

    // Should have static methods
    assert!(
        rust_code.contains("fn default_start"),
        "Should have default_start function.\nGot:\n{}",
        rust_code
    );

    assert!(
        rust_code.contains("fn max_count"),
        "Should have max_count function.\nGot:\n{}",
        rust_code
    );

    // Should have instance methods
    assert!(
        rust_code.contains("fn increment"),
        "Should have increment method.\nGot:\n{}",
        rust_code
    );

    assert!(
        rust_code.contains("fn get_count"),
        "Should have get_count method.\nGot:\n{}",
        rust_code
    );
}