Skip to main content

zsh/ported/
modentry.rs

1//! Module entry point dispatch
2//!
3//! Port from zsh/Src/modentry.c (43 lines).
4//!
5//! In C, this is the dlopen entry point that dispatches setup/boot/cleanup/finish
6//! calls to loaded modules. In Rust, all modules are statically compiled,
7//! so this provides the ModuleLifecycle trait dispatch instead.
8//!
9//! C signature: `int modentry(int boot, Module m, void *ptr)`. The `boot`
10//! int is the op selector — `0=setup_`, `1=boot_`, `2=cleanup_`,
11//! `3=finish_`, `4=features_`, `5=enables_` per Src/modentry.c switch.
12
13use crate::module::ModuleLifecycle;
14
15/// Port of `modentry(int boot, Module m, void *ptr)` from Src/modentry.c:7. Direct port of the
16/// C `int modentry(int boot, Module m, void *ptr)` switch — `boot`
17/// selects which lifecycle function to dispatch. Unknown values
18/// return 1 (matches C's `zerr("bad call to modentry"); return 1`).
19/// WARNING: param names don't match C — Rust=(boot, module) vs C=(boot, m, ptr)
20pub fn modentry(boot: i32, module: &mut dyn ModuleLifecycle) -> i32 {
21    // c:7
22    match boot {
23        0 => module.setup(),   // c:14
24        1 => module.boot(),    // c:18
25        2 => module.cleanup(), // c:22
26        3 => module.finish(),  // c:26
27        4 | 5 => 0,            // c:30,34 features_/enables_
28        _ => 1,                // c:38-40 zerr default
29    }
30}
31
32#[cfg(test)]
33mod tests {
34    use super::*;
35
36    struct TestModule {
37        booted: bool,
38    }
39    impl ModuleLifecycle for TestModule {
40        fn boot(&mut self) -> i32 {
41            self.booted = true;
42            0
43        }
44    }
45
46    #[test]
47    fn modentry_dispatches_setup_and_boot() {
48        let _g = crate::test_util::global_state_lock();
49        let mut m = TestModule { booted: false };
50        assert_eq!(modentry(0, &mut m), 0); // c:14 setup_
51        assert!(!m.booted);
52        assert_eq!(modentry(1, &mut m), 0); // c:18 boot_
53        assert!(m.booted);
54    }
55
56    #[test]
57    fn modentry_unknown_op_returns_one() {
58        let _g = crate::test_util::global_state_lock();
59        let mut m = TestModule { booted: false };
60        assert_eq!(modentry(6, &mut m), 1); // c:38 default
61        assert_eq!(modentry(-1, &mut m), 1);
62    }
63
64    /// c:22 — `cleanup_` op dispatches to module.cleanup(). Default
65    /// ModuleLifecycle::cleanup returns 0 unless overridden.
66    #[test]
67    fn modentry_cleanup_op_returns_zero() {
68        let _g = crate::test_util::global_state_lock();
69        let mut m = TestModule { booted: false };
70        assert_eq!(modentry(2, &mut m), 0);
71    }
72
73    /// c:26 — `finish_` op dispatches to module.finish().
74    #[test]
75    fn modentry_finish_op_returns_zero() {
76        let _g = crate::test_util::global_state_lock();
77        let mut m = TestModule { booted: false };
78        assert_eq!(modentry(3, &mut m), 0);
79    }
80
81    /// c:30/34 — `features_` / `enables_` ops short-circuit to 0
82    /// without invoking the module trait (the module-feature ledger
83    /// is canonicalised in modulestab, not in per-module hooks).
84    #[test]
85    fn modentry_features_enables_short_circuit_zero() {
86        let _g = crate::test_util::global_state_lock();
87        let mut m = TestModule { booted: false };
88        assert_eq!(modentry(4, &mut m), 0); // c:30 features_
89        assert_eq!(modentry(5, &mut m), 0); // c:34 enables_
90                                            // Confirm boot() wasn't invoked as side effect.
91        assert!(!m.booted);
92    }
93
94    // ═══════════════════════════════════════════════════════════════════
95    // Additional C-parity tests for Src/modentry.c.
96    // ═══════════════════════════════════════════════════════════════════
97
98    /// c:38 — `modentry` rejects MANY out-of-range op codes with 1.
99    #[test]
100    fn modentry_far_out_of_range_returns_one() {
101        let _g = crate::test_util::global_state_lock();
102        let mut m = TestModule { booted: false };
103        for op in [-100, -50, 7, 10, 100, i32::MAX, i32::MIN] {
104            assert_eq!(modentry(op, &mut m), 1, "op {} must return 1", op);
105        }
106    }
107
108    /// c:14 — `setup_` op (boot=0) dispatches without invoking boot().
109    /// Pin orthogonality: setup ≠ boot.
110    #[test]
111    fn modentry_setup_does_not_invoke_boot() {
112        let _g = crate::test_util::global_state_lock();
113        let mut m = TestModule { booted: false };
114        assert_eq!(modentry(0, &mut m), 0);
115        assert!(!m.booted, "setup must NOT trigger boot side effect");
116    }
117
118    /// c:18 — `boot_` op (boot=1) sets booted=true on our TestModule.
119    /// Pin: side effect is observable.
120    #[test]
121    fn modentry_boot_triggers_module_boot() {
122        let _g = crate::test_util::global_state_lock();
123        let mut m = TestModule { booted: false };
124        assert_eq!(modentry(1, &mut m), 0);
125        assert!(m.booted, "boot op must invoke module.boot()");
126    }
127
128    /// c:7 — `modentry` is deterministic for known ops on a stateless
129    /// fresh module instance.
130    #[test]
131    fn modentry_is_deterministic_for_known_ops() {
132        let _g = crate::test_util::global_state_lock();
133        for op in [0, 2, 3, 4, 5, 6, -1] {
134            let mut m = TestModule { booted: false };
135            let first = modentry(op, &mut m);
136            // Second call on fresh module should give same return.
137            let mut m2 = TestModule { booted: false };
138            let second = modentry(op, &mut m2);
139            assert_eq!(first, second, "op {} must be pure", op);
140        }
141    }
142
143    /// c:7 — every defined op (0..=5) returns 0 (no error).
144    #[test]
145    fn modentry_known_ops_return_zero() {
146        let _g = crate::test_util::global_state_lock();
147        for op in 0..=5 {
148            let mut m = TestModule { booted: false };
149            assert_eq!(modentry(op, &mut m), 0, "op {} must succeed", op);
150        }
151    }
152
153    /// c:38-40 — unknown op returns 1 for boundary values around 5/6.
154    #[test]
155    fn modentry_boundary_op_6_returns_one() {
156        let _g = crate::test_util::global_state_lock();
157        let mut m = TestModule { booted: false };
158        assert_eq!(modentry(6, &mut m), 1, "op=6 just past valid range → 1");
159    }
160
161    // ═══════════════════════════════════════════════════════════════════
162    // Additional C-parity tests for Src/modentry.c c:7 switch.
163    // ═══════════════════════════════════════════════════════════════════
164
165    /// c:7 — `modentry` repeated calls don't leak state between modules.
166    /// Pin module-isolation: a sequence of modentry calls on module A
167    /// must not poison subsequent modentry calls on module B.
168    #[test]
169    fn modentry_module_isolation() {
170        let _g = crate::test_util::global_state_lock();
171        let mut a = TestModule { booted: false };
172        assert_eq!(modentry(1, &mut a), 0); // boot A
173        assert!(a.booted);
174
175        let mut b = TestModule { booted: false };
176        assert_eq!(modentry(0, &mut b), 0); // setup B
177        assert!(!b.booted, "B must NOT be booted via A's modentry");
178    }
179
180    /// c:14 — calling setup multiple times is safe (no side-effect lock).
181    /// ModuleLifecycle::setup default returns 0.
182    #[test]
183    fn modentry_setup_idempotent() {
184        let _g = crate::test_util::global_state_lock();
185        let mut m = TestModule { booted: false };
186        for _ in 0..10 {
187            assert_eq!(modentry(0, &mut m), 0);
188        }
189        assert!(!m.booted);
190    }
191
192    /// c:22 — calling cleanup multiple times is safe.
193    #[test]
194    fn modentry_cleanup_idempotent() {
195        let _g = crate::test_util::global_state_lock();
196        let mut m = TestModule { booted: false };
197        for _ in 0..10 {
198            assert_eq!(modentry(2, &mut m), 0);
199        }
200    }
201
202    /// c:7 — return value is always either 0 or 1, never anything else.
203    #[test]
204    fn modentry_return_value_in_0_or_1_for_all_inputs() {
205        let _g = crate::test_util::global_state_lock();
206        for op in -5..=15 {
207            let mut m = TestModule { booted: false };
208            let r = modentry(op, &mut m);
209            assert!(r == 0 || r == 1, "modentry({}) = {} not in {{0,1}}", op, r);
210        }
211    }
212
213    /// c:7 — the lifecycle sequence setup→boot→cleanup→finish all succeed
214    /// in order without leaking unknown-op error.
215    #[test]
216    fn modentry_canonical_lifecycle_sequence_all_zero() {
217        let _g = crate::test_util::global_state_lock();
218        let mut m = TestModule { booted: false };
219        assert_eq!(modentry(0, &mut m), 0); // setup
220        assert_eq!(modentry(1, &mut m), 0); // boot
221        assert_eq!(modentry(2, &mut m), 0); // cleanup
222        assert_eq!(modentry(3, &mut m), 0); // finish
223        assert!(m.booted, "boot side effect must survive cleanup/finish");
224    }
225
226    /// c:30/34 — features_/enables_ ops do NOT call any trait method
227    /// (they short-circuit pre-dispatch per the c:30,34 case arms).
228    /// Verified by ensuring boot side effect is NOT triggered.
229    #[test]
230    fn modentry_features_op_does_not_call_module_boot() {
231        let _g = crate::test_util::global_state_lock();
232        let mut m = TestModule { booted: false };
233        assert_eq!(modentry(4, &mut m), 0);
234        assert!(!m.booted, "features_ op MUST NOT side-effect boot");
235        assert_eq!(modentry(5, &mut m), 0);
236        assert!(!m.booted, "enables_ op MUST NOT side-effect boot");
237    }
238
239    /// c:38 — out-of-range op doesn't call any trait method.
240    #[test]
241    fn modentry_unknown_op_does_not_invoke_trait() {
242        let _g = crate::test_util::global_state_lock();
243        let mut m = TestModule { booted: false };
244        for op in [99, 1000, -42] {
245            assert_eq!(modentry(op, &mut m), 1);
246            assert!(!m.booted, "op {} must NOT trigger boot", op);
247        }
248    }
249
250    // ═══════════════════════════════════════════════════════════════════
251    // Additional C-parity tests for Src/modentry.c
252    // c:7 modentry — type/value pins beyond the existing 0-5 sweep
253    // ═══════════════════════════════════════════════════════════════════
254
255    /// c:7 — `modentry` returns i32 (compile-time type pin).
256    #[test]
257    fn modentry_returns_i32_type() {
258        let _g = crate::test_util::global_state_lock();
259        let mut m = TestModule { booted: false };
260        let _: i32 = modentry(0, &mut m);
261    }
262
263    /// c:7 — `modentry` op=0 always returns 0 (success).
264    #[test]
265    fn modentry_setup_op_always_returns_zero() {
266        let _g = crate::test_util::global_state_lock();
267        for _ in 0..10 {
268            let mut m = TestModule { booted: false };
269            assert_eq!(modentry(0, &mut m), 0, "op=0 (setup) → 0");
270        }
271    }
272
273    /// c:7 — `modentry` op=3 (finish) always returns 0.
274    #[test]
275    fn modentry_finish_op_always_returns_zero() {
276        let _g = crate::test_util::global_state_lock();
277        for _ in 0..10 {
278            let mut m = TestModule { booted: false };
279            assert_eq!(modentry(3, &mut m), 0, "op=3 (finish) → 0");
280        }
281    }
282
283    /// c:7 — `modentry` op=4 (features) returns 0 + does NOT touch trait.
284    #[test]
285    fn modentry_features_op_returns_zero_no_side_effect() {
286        let _g = crate::test_util::global_state_lock();
287        for _ in 0..10 {
288            let mut m = TestModule { booted: false };
289            assert_eq!(modentry(4, &mut m), 0);
290            assert!(!m.booted, "features op (4) must NOT trigger boot");
291        }
292    }
293
294    /// c:7 — `modentry` op=5 (enables) returns 0 + does NOT touch trait.
295    #[test]
296    fn modentry_enables_op_returns_zero_no_side_effect() {
297        let _g = crate::test_util::global_state_lock();
298        for _ in 0..10 {
299            let mut m = TestModule { booted: false };
300            assert_eq!(modentry(5, &mut m), 0);
301            assert!(!m.booted, "enables op (5) must NOT trigger boot");
302        }
303    }
304
305    /// c:7 — `modentry` boot side-effect persists across subsequent calls.
306    #[test]
307    fn modentry_boot_side_effect_persists() {
308        let _g = crate::test_util::global_state_lock();
309        let mut m = TestModule { booted: false };
310        modentry(1, &mut m); // boot
311        assert!(m.booted, "boot triggered");
312        modentry(2, &mut m); // cleanup
313        assert!(m.booted, "boot side-effect persists past cleanup");
314        modentry(3, &mut m); // finish
315        assert!(m.booted, "boot side-effect persists past finish");
316    }
317
318    /// c:7 — `modentry` does NOT decrement m.booted on subsequent boot calls.
319    /// (TestModule sets booted=true unconditionally; bool stays true.)
320    #[test]
321    fn modentry_boot_sticky_true_across_calls() {
322        let _g = crate::test_util::global_state_lock();
323        let mut m = TestModule { booted: false };
324        for _ in 0..5 {
325            modentry(1, &mut m);
326        }
327        assert!(m.booted, "booted stays true across repeated boot calls");
328    }
329
330    /// c:38 — `modentry(MAX-1)` is in unknown-op range (returns 1).
331    #[test]
332    fn modentry_max_minus_one_unknown_returns_one() {
333        let _g = crate::test_util::global_state_lock();
334        let mut m = TestModule { booted: false };
335        assert_eq!(modentry(i32::MAX - 1, &mut m), 1, "MAX-1 op → 1 (unknown)");
336    }
337
338    /// c:7 — `modentry` for op=5 returns same result as op=4 (both feature-side).
339    #[test]
340    fn modentry_features_enables_both_zero() {
341        let _g = crate::test_util::global_state_lock();
342        for op in [4, 5] {
343            let mut m = TestModule { booted: false };
344            assert_eq!(modentry(op, &mut m), 0, "feature-side op {} returns 0", op);
345        }
346    }
347
348    /// c:7 — `modentry` for op=2 (cleanup) without prior boot is safe.
349    #[test]
350    fn modentry_cleanup_without_prior_boot_safe() {
351        let _g = crate::test_util::global_state_lock();
352        let mut m = TestModule { booted: false };
353        assert_eq!(modentry(2, &mut m), 0, "cleanup without boot → 0");
354        assert!(!m.booted, "boot side-effect not triggered by cleanup");
355    }
356
357    // ═══════════════════════════════════════════════════════════════════
358    // Additional C-parity tests for Src/modentry.c
359    // c:7 modentry — op dispatch table coverage + invariants
360    // ═══════════════════════════════════════════════════════════════════
361
362    /// c:7 — `modentry` returns i32 (compile-time pin, alt).
363    #[test]
364    fn modentry_returns_i32_pin_alt() {
365        let _g = crate::test_util::global_state_lock();
366        let mut m = TestModule { booted: false };
367        let _: i32 = modentry(0, &mut m);
368    }
369
370    /// c:7 — `modentry` for ops 0..=5 all return 0 (the known-good range).
371    #[test]
372    fn modentry_known_ops_all_return_zero() {
373        let _g = crate::test_util::global_state_lock();
374        for op in 0..=5i32 {
375            let mut m = TestModule { booted: false };
376            assert_eq!(modentry(op, &mut m), 0, "op {} must return 0 (success)", op);
377        }
378    }
379
380    /// c:38 — `modentry` for negative op returns 1 (unknown).
381    #[test]
382    fn modentry_negative_op_returns_one() {
383        let _g = crate::test_util::global_state_lock();
384        for op in [-1, -100, i32::MIN] {
385            let mut m = TestModule { booted: false };
386            assert_eq!(modentry(op, &mut m), 1, "negative op {} → 1", op);
387        }
388    }
389
390    /// c:38 — `modentry` for any op ≥ 6 returns 1.
391    #[test]
392    fn modentry_op_ge_six_returns_one() {
393        let _g = crate::test_util::global_state_lock();
394        for op in [6, 7, 10, 100, 1000, i32::MAX] {
395            let mut m = TestModule { booted: false };
396            assert_eq!(modentry(op, &mut m), 1, "op {} ≥ 6 → 1", op);
397        }
398    }
399
400    /// c:7 — op=0 (setup_) does not trigger boot.
401    #[test]
402    fn modentry_setup_op_does_not_trigger_boot() {
403        let _g = crate::test_util::global_state_lock();
404        let mut m = TestModule { booted: false };
405        modentry(0, &mut m);
406        assert!(!m.booted, "setup must not flip booted=true");
407    }
408
409    /// c:7 — op=3 (finish_) does not trigger boot.
410    #[test]
411    fn modentry_finish_op_does_not_trigger_boot() {
412        let _g = crate::test_util::global_state_lock();
413        let mut m = TestModule { booted: false };
414        modentry(3, &mut m);
415        assert!(!m.booted, "finish must not flip booted=true");
416    }
417
418    /// c:7 — dispatch table is exhaustively deterministic. Two
419    /// invocations with the same op + same module state return the
420    /// same value.
421    #[test]
422    fn modentry_dispatch_is_deterministic_across_ops() {
423        let _g = crate::test_util::global_state_lock();
424        for op in -5..=10i32 {
425            let mut m1 = TestModule { booted: false };
426            let mut m2 = TestModule { booted: false };
427            let a = modentry(op, &mut m1);
428            let b = modentry(op, &mut m2);
429            assert_eq!(a, b, "op {} must produce deterministic return", op);
430        }
431    }
432
433    /// c:7 — return value is always 0 or 1 (boolean i32 sentinel).
434    #[test]
435    fn modentry_return_value_is_boolean() {
436        let _g = crate::test_util::global_state_lock();
437        for op in -5..=10i32 {
438            let mut m = TestModule { booted: false };
439            let r = modentry(op, &mut m);
440            assert!(r == 0 || r == 1, "modentry({}) = {} not in 0/1", op, r);
441        }
442    }
443
444    /// c:7 — boot (op=1) → cleanup (op=2) → finish (op=3) sequence safe.
445    #[test]
446    fn modentry_boot_cleanup_finish_sequence_safe() {
447        let _g = crate::test_util::global_state_lock();
448        let mut m = TestModule { booted: false };
449        assert_eq!(modentry(1, &mut m), 0);
450        assert_eq!(modentry(2, &mut m), 0);
451        assert_eq!(modentry(3, &mut m), 0);
452    }
453
454    /// c:7 — repeated boot+cleanup cycle is safe (no resource leak panics).
455    #[test]
456    fn modentry_repeated_boot_cleanup_cycles_safe() {
457        let _g = crate::test_util::global_state_lock();
458        let mut m = TestModule { booted: false };
459        for _ in 0..10 {
460            assert_eq!(modentry(1, &mut m), 0, "boot");
461            assert_eq!(modentry(2, &mut m), 0, "cleanup");
462        }
463    }
464
465    /// c:30,34 — features (op=4) and enables (op=5) don't perturb
466    /// other ops' return values when interleaved.
467    #[test]
468    fn modentry_features_enables_dont_affect_other_ops() {
469        let _g = crate::test_util::global_state_lock();
470        let mut m = TestModule { booted: false };
471        assert_eq!(modentry(4, &mut m), 0);
472        assert_eq!(modentry(0, &mut m), 0, "setup after features still 0");
473        assert_eq!(modentry(5, &mut m), 0);
474        assert_eq!(modentry(3, &mut m), 0, "finish after enables still 0");
475    }
476
477    // ═══════════════════════════════════════════════════════════════════
478    // Additional C-parity pins for Src/modentry.c
479    // c:7-40 — boot-int selector ∈ {0..=5}; anything else returns 1.
480    // ═══════════════════════════════════════════════════════════════════
481
482    /// c:38 — `modentry(i32::MIN)` (most-negative) returns 1.
483    #[test]
484    fn modentry_i32_min_returns_one() {
485        let _g = crate::test_util::global_state_lock();
486        let mut m = TestModule { booted: false };
487        assert_eq!(modentry(i32::MIN, &mut m), 1);
488    }
489
490    /// c:38 — `modentry(i32::MAX)` (most-positive) returns 1.
491    #[test]
492    fn modentry_i32_max_returns_one() {
493        let _g = crate::test_util::global_state_lock();
494        let mut m = TestModule { booted: false };
495        assert_eq!(modentry(i32::MAX, &mut m), 1);
496    }
497
498    /// c:7-40 — exhaustive sweep of ops 0..=10: ops 0..=5 return 0,
499    /// ops 6..=10 return 1.
500    #[test]
501    fn modentry_exhaustive_sweep_0_to_10() {
502        let _g = crate::test_util::global_state_lock();
503        let mut m = TestModule { booted: false };
504        for op in 0..=5 {
505            assert_eq!(modentry(op, &mut m), 0, "op {} must return 0 (known)", op);
506        }
507        for op in 6..=10 {
508            assert_eq!(modentry(op, &mut m), 1, "op {} must return 1 (unknown)", op);
509        }
510    }
511
512    /// c:7 — `modentry` is deterministic for the same (op, module) state.
513    #[test]
514    fn modentry_deterministic_on_unknown_op() {
515        let _g = crate::test_util::global_state_lock();
516        let mut m = TestModule { booted: false };
517        let first = modentry(99, &mut m);
518        for _ in 0..10 {
519            assert_eq!(
520                modentry(99, &mut m),
521                first,
522                "unknown op must always return same value"
523            );
524        }
525    }
526
527    /// c:7 — `modentry` return type i32 (compile-time pin).
528    #[test]
529    fn modentry_returns_i32_type_compile_pin() {
530        let _g = crate::test_util::global_state_lock();
531        let mut m = TestModule { booted: false };
532        let _: i32 = modentry(0, &mut m);
533    }
534
535    /// c:14 — op=0 (setup) does not boot the module.
536    #[test]
537    fn modentry_op_zero_does_not_set_booted() {
538        let _g = crate::test_util::global_state_lock();
539        let mut m = TestModule { booted: false };
540        assert_eq!(modentry(0, &mut m), 0);
541        assert!(!m.booted, "setup must NOT set booted=true");
542    }
543
544    /// c:22 — op=2 (cleanup) does not boot the module.
545    #[test]
546    fn modentry_op_two_does_not_set_booted() {
547        let _g = crate::test_util::global_state_lock();
548        let mut m = TestModule { booted: false };
549        assert_eq!(modentry(2, &mut m), 0);
550        assert!(!m.booted, "cleanup must NOT set booted=true");
551    }
552
553    /// c:7-40 — all unknown ops in 6..256 return 1 (sweep).
554    #[test]
555    fn modentry_unknown_op_sweep_6_to_255() {
556        let _g = crate::test_util::global_state_lock();
557        let mut m = TestModule { booted: false };
558        for op in 6..256 {
559            assert_eq!(modentry(op, &mut m), 1, "unknown op {} must return 1", op);
560        }
561    }
562
563    /// c:7-40 — all negative ops return 1 (sweep 1..1000).
564    #[test]
565    fn modentry_negative_op_sweep_minus_1_to_minus_1000() {
566        let _g = crate::test_util::global_state_lock();
567        let mut m = TestModule { booted: false };
568        for op in 1..=1000 {
569            assert_eq!(
570                modentry(-op, &mut m),
571                1,
572                "negative op -{} must return 1",
573                op
574            );
575        }
576    }
577
578    /// c:7 — return value is always in {0, 1} (boolean exit).
579    #[test]
580    fn modentry_return_only_0_or_1_sweep() {
581        let _g = crate::test_util::global_state_lock();
582        let mut m = TestModule { booted: false };
583        for op in -50..=50 {
584            let r = modentry(op, &mut m);
585            assert!(r == 0 || r == 1, "modentry({}) = {} not in {{0,1}}", op, r);
586        }
587    }
588
589    /// c:18 — op=1 (boot) sets booted=true; subsequent boots stay sticky.
590    #[test]
591    fn modentry_repeated_boot_is_idempotent_on_booted_flag() {
592        let _g = crate::test_util::global_state_lock();
593        let mut m = TestModule { booted: false };
594        for _ in 0..5 {
595            assert_eq!(modentry(1, &mut m), 0);
596            assert!(m.booted, "booted must stay true");
597        }
598    }
599}